diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0b8e8a11ad58..4636f08cf765 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -30,24 +30,31 @@ First things first, we want to make it clear how you can contribute (if you've n ## Getting Started -/tg/station doesn't have a list of goals and features to add; we instead allow freedom for contributors to suggest and create their ideas for the game. That doesn't mean we aren't determined to squash bugs, which unfortunately pop up a lot due to the deep complexity of the game. Here are some useful starting guides, if you want to contribute or if you want to know what challenges you can tackle with zero knowledge about the game's code structure. +DarkPack13/SecondCity doesn't have a list of goals and features to add; we instead allow freedom for contributors to suggest and create their ideas for the game. That doesn't mean we aren't determined to squash bugs, which unfortunately pop up a lot due to the deep complexity of the game. Here are some useful starting guides, if you want to contribute or if you want to know what challenges you can tackle with zero knowledge about the game's code structure. If you want to contribute the first thing you'll need to do is [set up Git](https://hackmd.io/@tgstation/HJ8OdjNBc) so you can download the source code. After setting it up, optionally navigate your git commandline to the project folder and run the command: `git config blame.ignoreRevsFile .git-blame-ignore-revs`. -We have a [list of guides on the wiki](http://tgstation13.org/wiki/Guides#Development_and_Contribution_Guides) that will help you get started contributing to /tg/station with Git and Dream Maker. For beginners, it is recommended you work on small projects like bugfixes at first. If you need help learning to program in BYOND, check out this [repository of resources](http://www.byond.com/developer/articles/resources). +We have a [list of guides on the wiki](http://tgstation13.org/wiki/Guides#Development_and_Contribution_Guides) that will help you get started contributing to DarkPack13/SecondCity with Git and Dream Maker. For beginners, it is recommended you work on small projects like bugfixes at first. If you need help learning to program in BYOND, check out this [repository of resources](http://www.byond.com/developer/articles/resources). There is an open list of approachable issues for [your inspiration here](https://github.com/tgstation/tgstation/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22). You can of course, as always, ask for help on the Discord channels or the forums. We're just here to have fun and help out, so please don't expect professional support. +## AI Disclosure +Darkpack does not accept any AI-generated code. This to all aspects like use of any inbuilt Gen-AI features in IDEs. **All use of AI in the process of contributing to the project should be disclosed.** +#### Why? +For communication (pr bodies, reviews, etc.), AI creates a large burden on maintainers. It creates a communication barrier between the contributor and maintainer, as you now have to filter every response. We want YOUR thoughts on why you added something or why it's good for the game. +For code, while applicable to many projects, it's especially true that AI is bad at writing DM or SS13 code. It's a language with a very small set of data that's mostly SS13-derived projects. It makes frequent hallucinations and assumptions from hodgepodging together every SS13 codebase. Code written by AI instantly suffers from a bus factor of 0; if even you don't understand your code, how is anyone meant to maintain it? +In general its a big resource drain for a project of volunteers and I would ask you please respect the time of them. + ## Meet the Team -### Headcoder +### ~~Headcoder~~ -The Headcoder is responsible for controlling, adding, and removing maintainers from the project. In addition to filling the role of a normal maintainer, they have sole authority on who becomes a maintainer, as well as who remains a maintainer and who does not. +~~The Headcoder is responsible for controlling, adding, and removing maintainers from the project. In addition to filling the role of a normal maintainer, they have sole authority on who becomes a maintainer, as well as who remains a maintainer and who does not.~~ -### Maintainers +### Maintainers/Antediluvian Maintainers are quality control. If a proposed pull request doesn't meet the following specifications, they can request you to change it, or simply just close the pull request. Maintainers are required to give a reason for closing the pull request. @@ -70,9 +77,9 @@ These are not steadfast rules as maintainers are expected to use their best judg -### Issue Managers +### ~~Issue Managers~~ -Issue Managers help out the project by labelling bug reports and PRs and closing bug reports which are duplicates or are no longer applicable. +~~Issue Managers help out the project by labelling bug reports and PRs and closing bug reports which are duplicates or are no longer applicable.~~
What You Can and Can't Do as an Issue Manager @@ -208,6 +215,6 @@ Github actions that require additional configuration are disabled on the reposit ## Using the Maintainer Role Ping in Discord -When your PR is labeled as "Stale", you are permitted to ping the `@Maintainer` role on the /tg/station Discord. Using this ping for anything else can be met with escalating timeouts and referral to /tg/station's Discord moderators for further infractions. +When your PR is labeled as "Stale", you are permitted to ping the `@Antediluvian` or `@Review Ping` role on the DarkPack13/SecondCity Discord. -Feel free to engage and obtain general feedback in the Coding General channel without the role ping before your PR goes stale to build interest and get reviews. +Feel free to engage and obtain general feedback in the #development channel without the role ping before your PR goes stale to build interest and get reviews. diff --git a/.github/actions/restore_or_install_byond/action.yml b/.github/actions/restore_or_install_byond/action.yml index 78f189ae94d7..02e264b374d7 100644 --- a/.github/actions/restore_or_install_byond/action.yml +++ b/.github/actions/restore_or_install_byond/action.yml @@ -29,6 +29,13 @@ runs: echo "BYOND_MAJOR=$BYOND_MAJOR" >> $GITHUB_ENV echo "BYOND_MINOR=$BYOND_MINOR" >> $GITHUB_ENV + - name: Install 32-bit libcurl + shell: bash + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends libcurl4t64:i386 + # The use of `actions/cache/restore` and `actions/cache/save` here is deliberate, as we want to # save the BYOND install to a cache as early as possible. If we used just `actions/cache`, it # would only attempt to save the cache at the end of a job. This ensures that if a workflow run diff --git a/.github/alternate_byond_versions.txt b/.github/alternate_byond_versions.txt index 5a6ec39f034e..6170d54c184f 100644 --- a/.github/alternate_byond_versions.txt +++ b/.github/alternate_byond_versions.txt @@ -7,4 +7,4 @@ # 500.1337: runtimestation # 516.1638: runtimestation;516 # Lowest supported version -516.1648: runtimestation +516.1659: runtimestation diff --git a/.github/workflows/bt_diff.yml b/.github/workflows/bt_diff.yml new file mode 100644 index 000000000000..c1f232f82d66 --- /dev/null +++ b/.github/workflows/bt_diff.yml @@ -0,0 +1,30 @@ +# Renders the diffs and uploads them to filehouse +name: Behavior tree diff + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - "**/*.bt.json" + +permissions: + contents: read + pull-requests: read + +jobs: + render: + name: Render behavior tree diffs + runs-on: ubuntu-24.04 + steps: + - name: Render diffs + uses: tgstation/BehaviorTreeG@main + with: + mode: render + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload rendered diffs + uses: actions/upload-artifact@v7 + with: + name: bt-diff + path: bt-diff-out + if-no-files-found: ignore diff --git a/.github/workflows/bt_diff_comment.yml b/.github/workflows/bt_diff_comment.yml new file mode 100644 index 000000000000..9ddca67101e8 --- /dev/null +++ b/.github/workflows/bt_diff_comment.yml @@ -0,0 +1,42 @@ +# Uploads the rendered images and posts the PR comment. Runs on workflow_run. +name: Behavior tree diff comment + +on: + workflow_run: + workflows: ["Behavior tree diff"] + types: [completed] + +permissions: + contents: read + pull-requests: write + actions: read + +jobs: + comment: + name: Post behavior tree diff comment + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-24.04 + steps: + - name: "Check for ARTIFACTS_FILE_HOUSE_KEY" + id: secrets_set + env: + ENABLER_SECRET: ${{ secrets.ARTIFACTS_FILE_HOUSE_KEY }} + run: | + unset SECRET_EXISTS + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT + - name: Download rendered diffs + uses: actions/download-artifact@v8 + with: + name: bt-diff + path: bt-diff-out + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload images and comment + if: steps.secrets_set.outputs.SECRETS_ENABLED + uses: tgstation/BehaviorTreeG@main + with: + mode: publish + token: ${{ secrets.GITHUB_TOKEN }} + file-house-key: ${{ secrets.ARTIFACTS_FILE_HOUSE_KEY }} diff --git a/.github/workflows/check_bt_compiled.yml b/.github/workflows/check_bt_compiled.yml new file mode 100644 index 000000000000..dbe39e5dde7b --- /dev/null +++ b/.github/workflows/check_bt_compiled.yml @@ -0,0 +1,24 @@ +name: Check BT Compiled JSON + +on: + pull_request: + paths: + - "**/*.bt.json" + - "build/behavior_trees/**" + - "code/__DEFINES/**" + - "tools/build_bt.py" + +jobs: + check_bt_compiled: + name: Verify BT compiled JSON is up to date + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v7 + with: + python-version: "3.x" + + - name: Check compiled BT files are up to date + run: python tools/build_bt.py --check diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index 0067c4c1d559..a79f01d317cd 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -23,7 +23,7 @@ jobs: - name: "Setup python" if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: "3.x" diff --git a/.github/workflows/discord_discussions.yml b/.github/workflows/discord_discussions.yml index 6d9938dff58b..8d6d6f20b61c 100644 --- a/.github/workflows/discord_discussions.yml +++ b/.github/workflows/discord_discussions.yml @@ -28,7 +28,7 @@ jobs: exit 1 - name: Setup dotnet - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: 8.0.x dotnet-quality: ga diff --git a/.github/workflows/run_linters.yml b/.github/workflows/run_linters.yml index 751fc8a2e958..31949d7fcede 100644 --- a/.github/workflows/run_linters.yml +++ b/.github/workflows/run_linters.yml @@ -39,7 +39,7 @@ jobs: path: tools/icon_cutter/cache key: ${{ runner.os }}-cutter-${{ hashFiles('dependencies.sh') }} - name: Setup .NET SDK - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: 9.x - name: Install OpenDream diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index eea4a9c91de5..7e637de1577c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Mark stale issues and pull requests - uses: actions/stale@v10 + uses: actions/stale@v11 id: stale with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tgs_test.yml b/.github/workflows/tgs_test.yml index c52b5bd05f62..e6a63d4e3385 100644 --- a/.github/workflows/tgs_test.yml +++ b/.github/workflows/tgs_test.yml @@ -59,7 +59,7 @@ jobs: - 5000:5000 #Can't use env here for some reason steps: - name: Setup dotnet - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: 8.0.x diff --git a/.tgs.yml b/.tgs.yml index a8cd14e45b63..441b1c91c794 100644 --- a/.tgs.yml +++ b/.tgs.yml @@ -3,7 +3,7 @@ version: 1 # The BYOND version to use (kept in sync with dependencies.sh by the "TGS Test Suite" CI job) # Must be interpreted as a string, keep quoted -byond: "516.1659" +byond: "516.1685" # Folders to create in "/Configuration/GameStaticFiles/" static_files: # Config directory should be static diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4f751c350b95..c67415ade6ba 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,6 +7,8 @@ "Donkie.vscode-tgstation-test-adapter", "anturk.dmi-editor", "esbenp.prettier-vscode", - "biomejs.biome" + "biomejs.biome", + "ss13.ss13buildflags", + "BehaviorTreeG.behaviortreeg" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index e137e741ef40..9c9d8b0ff963 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ { "type": "byond", "request": "launch", - "name": "Launch DreamSeeker", + "name": "Launch DreamSeeker (With selected flags)", "preLaunchTask": "Build All", "dmb": "${workspaceFolder}/${command:CurrentDMB}" }, @@ -46,7 +46,7 @@ { "type": "byond", "request": "launch", - "name": "Launch DreamDaemon", + "name": "Launch DreamDaemon (With selected flags)", "preLaunchTask": "Build All", "dmb": "${workspaceFolder}/${command:CurrentDMB}", "dreamDaemon": true diff --git a/.vscode/settings.json b/.vscode/settings.json index baca4eb8452f..af27d2682104 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,5 +25,8 @@ "search.exclude": { "**/node_modules": true }, - "Lua.diagnostics.enable": false + "Lua.diagnostics.enable": false, + "ss13BuildFlags.baseTask": "Build All", + "ss13BuildFlags.configPath": "tools/build/build_flags.json", + "ss13BuildFlags.injectionMode": "cli-args" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aa32b74939a5..e0b41e4482f0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,6 +1,16 @@ { "version": "2.0.0", "tasks": [ + { + "type": "shell", + "command": "tools/bootstrap/python tools/build_bt.py", + "windows": { + "command": ".\\tools\\bootstrap\\python.bat tools/build_bt.py" + }, + "group": "build", + "presentation": { "reveal": "silent", "panel": "shared" }, + "label": "Compile BT Trees" + }, { "type": "process", "command": "tools/build/build.sh", @@ -17,16 +27,24 @@ "kind": "build", "isDefault": true }, - "dependsOn": "dm: reparse", + "dependsOn": ["dm: reparse", "Compile BT Trees"], "label": "Build All" }, { "type": "process", "command": "tools/build/build.sh", - "args": ["-DLOWMEMORYMODE"], + "args": [ + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", + "-DFORCE_MAP=\"runtimetown\"" + ], "windows": { "command": ".\\tools\\build\\build.bat", - "args": ["-DLOWMEMORYMODE"] + "args": [ + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", + "-DFORCE_MAP=\"runtimetown\"" + ] }, "options": { "env": { @@ -43,10 +61,22 @@ { "type": "process", "command": "tools/build/build.sh", - "args": ["-DLOWMEMORYMODE", "-DABSOLUTE_MINIMUM"], + "args": [ + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", + "-DAUTOSTART_GAME", + "-DMINIMAL_CENTCOM", + "-DFORCE_MAP=\"runtimestation_minimal\"" + ], "windows": { "command": ".\\tools\\build\\build.bat", - "args": ["-DLOWMEMORYMODE", "-DABSOLUTE_MINIMUM"] + "args": [ + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", + "-DAUTOSTART_GAME", + "-DMINIMAL_CENTCOM", + "-DFORCE_MAP=\"runtimestation_minimal\"" + ] }, "options": { "env": { @@ -94,20 +124,24 @@ "type": "process", "command": "tools/build/build.sh", "args": [ - "-DLOWMEMORYMODE", + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", "-DTESTING", "-DREAGENTS_TESTING", "-DTIMER_DEBUG", - "-DREFERENCE_DOING_IT_LIVE" + "-DREFERENCE_DOING_IT_LIVE", + "-DFORCE_MAP=\"runtimetown\"" ], "windows": { "command": ".\\tools\\build\\build.bat", "args": [ - "-DLOWMEMORYMODE", + "-DSKIP_LAVALAND", + "-DSKIP_SPACE_LEVELS", "-DTESTING", "-DREAGENTS_TESTING", "-DTIMER_DEBUG", - "-DREFERENCE_DOING_IT_LIVE" + "-DREFERENCE_DOING_IT_LIVE", + "-DFORCE_MAP=\"runtimetown\"" ] }, "options": { diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm index b6f057ca94df..4cc15c273206 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm @@ -321,7 +321,7 @@ }, /area/ruin/powered/shuttle) "Sv" = ( -/turf/open/genturf, +/turf/template_noop, /area/icemoon/underground/unexplored/rivers) "SJ" = ( /obj/structure/railing{ diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm index c11ba6fad9a2..5faa67d14a97 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm @@ -1523,7 +1523,7 @@ /turf/open/floor/iron/edge, /area/ruin/plasma_facility/commons) "AG" = ( -/turf/open/genturf, +/turf/template_noop, /area/icemoon/underground/unexplored/rivers) "AT" = ( /obj/structure/lattice/catwalk, diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_comms_agent.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_comms_agent.dmm index ab0716d4be23..ac6be5fa2096 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_comms_agent.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_comms_agent.dmm @@ -657,9 +657,6 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood/parquet, /area/ruin/comms_agent) -"KQ" = ( -/turf/open/genturf, -/area/template_noop) "KR" = ( /obj/effect/baseturf_helper/asteroid/snow, /turf/closed/wall, @@ -2875,7 +2872,7 @@ MH MH MH MH -KQ +OM tl YX YX @@ -2931,8 +2928,8 @@ MH MH MH MH -KQ -KQ +OM +OM tl tl MH @@ -2988,9 +2985,9 @@ MH MH MH MH -KQ -KQ -KQ +OM +OM +OM tl tl MH @@ -3046,8 +3043,8 @@ PC MH MH MH -KQ -KQ +OM +OM tl MH MH @@ -3104,7 +3101,7 @@ sB MH MH MH -KQ +OM MH MH OM diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_frozen_comms.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_frozen_comms.dmm index 6749491b68b6..8fdf6c493b1a 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_frozen_comms.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_frozen_comms.dmm @@ -4,7 +4,7 @@ /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/underground/explored) "aU" = ( -/turf/open/genturf, +/turf/template_noop, /area/icemoon/underground/unexplored/rivers) "bh" = ( /mob/living/basic/mining/wolf, diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_outpost31.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_outpost31.dmm index b6531a220082..8769d34900c5 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_outpost31.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_outpost31.dmm @@ -442,7 +442,7 @@ /area/ruin/outpost31/lab) "hN" = ( /obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "hT" = ( /obj/machinery/light/warm/directional/east, @@ -512,7 +512,7 @@ dir = 8 }, /obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "jP" = ( /obj/effect/turf_decal/stripes/line{ @@ -557,7 +557,7 @@ "kP" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "kX" = ( /obj/effect/turf_decal/siding/wood{ @@ -600,7 +600,7 @@ /area/ruin/outpost31) "lM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "lO" = ( /obj/effect/decal/cleanable/dirt/dust, @@ -762,7 +762,7 @@ /turf/open/floor/wood, /area/ruin/outpost31/crewquarters) "oF" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "oJ" = ( /obj/structure/chair/stool/directional/east, @@ -849,12 +849,12 @@ }, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/dim/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "qI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /obj/item/flashlight/lantern, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "qK" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer2, @@ -1048,7 +1048,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /obj/structure/cable, /obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "uA" = ( /obj/structure/cable, @@ -1071,7 +1071,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "o31a" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "uR" = ( /obj/effect/turf_decal/siding/white{ @@ -1084,7 +1084,7 @@ name = "winter gear closet" }, /obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "vU" = ( /obj/structure/cable, @@ -1213,7 +1213,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "o31B" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "yo" = ( /turf/closed/indestructible/reinforced, @@ -1254,7 +1254,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /obj/structure/cable, /obj/effect/mapping_helpers/airlock/autoname, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "ze" = ( /obj/structure/table/optable, @@ -1307,7 +1307,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "zF" = ( /obj/machinery/duct, @@ -1338,7 +1338,7 @@ /obj/structure/barricade/wooden/crude/snow{ opacity = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "Ae" = ( /obj/effect/decal/cleanable/dirt/dust, @@ -1425,7 +1425,7 @@ /obj/structure/tank_dispenser/oxygen{ oxygentanks = 5 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "BI" = ( /obj/effect/decal/cleanable/dirt/dust, @@ -1460,7 +1460,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/dim/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "Ca" = ( /obj/structure/closet/emcloset, @@ -1475,7 +1475,7 @@ /obj/structure/cable, /obj/effect/decal/cleanable/dirt/dust, /obj/item/crowbar, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/ruin/outpost31) "Ce" = ( /obj/effect/decal/cleanable/dirt, @@ -1746,7 +1746,7 @@ /obj/machinery/atmospherics/components/unary/airlock_pump{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "HF" = ( /obj/effect/decal/cleanable/dirt, @@ -1838,7 +1838,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "Jq" = ( /obj/effect/spawner/random/entertainment/arcade, @@ -1891,7 +1891,7 @@ /obj/structure/closet/preopen{ name = "winter gear closet" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "Lk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, @@ -1911,7 +1911,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/broken/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "LA" = ( /turf/closed/wall, @@ -1981,7 +1981,7 @@ /obj/structure/tank_dispenser/oxygen{ oxygentanks = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "MO" = ( /obj/effect/turf_decal/stripes/line{ @@ -2004,7 +2004,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/dim/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "MW" = ( /obj/structure/cable, @@ -2032,7 +2032,7 @@ /obj/structure/barricade/wooden/crude/snow{ opacity = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "Nl" = ( /obj/machinery/power/apc/auto_name/directional/south, @@ -2286,7 +2286,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "o31c" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "SN" = ( /obj/machinery/atmospherics/components/unary/passive_vent/layer4{ @@ -2463,7 +2463,7 @@ }, /obj/item/clothing/shoes/winterboots/ice_boots/eva, /obj/item/clothing/suit/hooded/wintercoat/eva, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "VR" = ( /obj/structure/closet/crate/bin, @@ -2498,7 +2498,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "o31B" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "Wu" = ( /obj/effect/decal/cleanable/dirt/dust, @@ -2516,7 +2516,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ cycle_id = "o31c" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/outpost31) "WQ" = ( /turf/template_noop, @@ -2624,7 +2624,7 @@ name = "winter gear closet" }, /obj/machinery/light/small/dim/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/outpost31) "Zq" = ( /obj/structure/window/spawner/directional/east, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index f426b8fe1d93..db975b54ba5f 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -117,7 +117,7 @@ density = 1 }, /obj/effect/decal/fakelattice, -/turf/open/floor/pod/light{ +/turf/open/floor/mineral/plastitanium/pod/light{ density = 1 }, /area/ruin/powered/beach) @@ -195,7 +195,7 @@ "eA" = ( /obj/item/melee/skateboard/hoverboard, /obj/machinery/light/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "eE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -242,7 +242,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "gF" = ( /obj/effect/turf_decal/sand, @@ -332,7 +332,7 @@ /area/ruin/powered/beach) "hX" = ( /obj/structure/punching_bag, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/beach) "iH" = ( /obj/structure/flora/bush/stalky/style_random, @@ -443,12 +443,12 @@ /area/ruin/powered/beach) "og" = ( /obj/structure/weightmachine/weightlifter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/beach) "oF" = ( /obj/structure/table/reinforced, /obj/machinery/reagentgrinder, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "oL" = ( /obj/structure/extinguisher_cabinet/directional/south, @@ -460,7 +460,7 @@ /area/ruin/powered/beach) "oU" = ( /obj/machinery/seed_extractor, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "pz" = ( /turf/open/water/beach, @@ -494,7 +494,7 @@ /area/ruin/powered/beach) "qf" = ( /obj/structure/reagent_dispensers/watertank, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "qg" = ( /obj/structure/flora/bush/large/style_random, @@ -556,7 +556,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "rU" = ( /obj/item/reagent_containers/condiment/enzyme, @@ -633,7 +633,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors/explored) "wY" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "xa" = ( /obj/effect/mapping_helpers/broken_floor, @@ -749,7 +749,7 @@ /area/ruin/powered/beach) "AE" = ( /obj/item/melee/skateboard/hoverboard, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "AG" = ( /obj/structure/flora/rock/pile/jungle/style_random, @@ -858,7 +858,7 @@ /obj/structure/table/reinforced, /obj/item/secateurs, /obj/item/reagent_containers/cup/bottle/nutrient/ez, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "Fa" = ( /obj/structure/chair/stool/bar/directional/south, @@ -952,7 +952,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "Iv" = ( /turf/open/floor/iron/stairs/right, @@ -1010,7 +1010,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "Kn" = ( /obj/structure/flora/bush/large/style_random, @@ -1122,7 +1122,7 @@ desc = "A sink used for washing one's hands and face. It looks rusty and home-made"; name = "old sink" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/beach) "Qf" = ( /obj/effect/turf_decal/sand, @@ -1318,7 +1318,7 @@ /turf/open/floor/plating, /area/ruin/powered/beach) "VO" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/beach) "VY" = ( /obj/structure/flora/coconuts, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm index 3d57df9235ba..de2a10804e8c 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm @@ -2,73 +2,9 @@ "aa" = ( /turf/template_noop, /area/template_noop) -"ab" = ( -/obj/structure/stone_tile/surrounding_tile{ - dir = 4 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ac" = ( -/obj/structure/stone_tile/block{ - dir = 1 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ad" = ( -/obj/structure/stone_tile/block{ - dir = 1 - }, -/obj/structure/stone_tile/cracked{ - dir = 8 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ae" = ( -/obj/structure/stone_tile/block/cracked{ - dir = 1 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"af" = ( -/obj/structure/stone_tile/block{ - dir = 1 - }, -/obj/structure/stone_tile{ - dir = 8 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ag" = ( -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ah" = ( -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ai" = ( -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 4 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"aj" = ( -/obj/structure/stone_tile/slab, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) "ak" = ( /turf/closed/indestructible/riveted/boss, /area/ruin/unpowered/ash_walkers) -"aq" = ( -/obj/structure/stone_tile/block/cracked{ - dir = 8 - }, -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) "as" = ( /turf/closed/wall/mineral/wood, /area/ruin/unpowered/ash_walkers) @@ -180,9 +116,6 @@ /mob/living/basic/mining/gutlunch/milk, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) -"aR" = ( -/turf/closed/indestructible/riveted/boss, -/area/lavaland/surface/outdoors) "aS" = ( /obj/structure/stone_tile/block{ dir = 4 @@ -288,12 +221,6 @@ /obj/structure/stone_tile, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) -"bi" = ( -/obj/structure/stone_tile/block/cracked{ - dir = 8 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) "bj" = ( /obj/structure/stone_tile/block/cracked{ dir = 4 @@ -402,11 +329,6 @@ /obj/item/stack/sheet/mineral/wood, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) -"bx" = ( -/obj/structure/stone_tile/slab/cracked, -/obj/effect/decal/cleanable/blood, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) "by" = ( /obj/structure/closet/crate, /obj/structure/stone_tile/block/cracked{ @@ -443,12 +365,6 @@ }, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) -"bC" = ( -/obj/structure/stone_tile/block/cracked{ - dir = 4 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) "bG" = ( /turf/closed/indestructible/riveted/boss/see_through, /area/ruin/unpowered/ash_walkers) @@ -468,17 +384,37 @@ /obj/structure/stone_tile/slab/cracked, /turf/closed/mineral/volcanic/lava_land_surface, /area/lavaland/surface/outdoors) -"bJ" = ( +"bN" = ( +/obj/structure/stone_tile/block/cracked, /obj/effect/mapping_helpers/no_lava, -/mob/living/basic/mining/gutlunch/milk, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"cp" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/railing/wooden_fence{ +"bQ" = ( +/obj/structure/stone_tile/cracked{ + dir = 4 + }, +/obj/structure/stone_tile{ + dir = 1 + }, +/obj/structure/stone_tile{ dir = 8 }, +/obj/effect/decal/cleanable/blood, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"cy" = ( +/obj/structure/stone_tile, +/obj/structure/stone_tile/cracked{ + dir = 8 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"cz" = ( +/obj/structure/stone_tile, +/obj/structure/stone_tile/block{ + dir = 1 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) "cF" = ( /obj/structure/stone_tile/block{ @@ -537,132 +473,70 @@ /obj/item/clothing/head/helmet/roman/legionnaire, /turf/open/indestructible/boss, /area/ruin/unpowered/ash_walkers) -"cO" = ( -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 8 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"cP" = ( -/obj/structure/stone_tile/block, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"cQ" = ( -/obj/structure/stone_tile/block/cracked, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"cR" = ( -/obj/structure/stone_tile/surrounding_tile/cracked, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +"dj" = ( +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"cT" = ( -/obj/structure/stone_tile, +"dm" = ( /obj/structure/stone_tile/block{ dir = 1 }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"du" = ( -/obj/structure/stone_tile/cracked{ - dir = 1 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"dz" = ( /obj/structure/stone_tile/cracked{ - dir = 1 - }, -/obj/structure/stone_tile/cracked, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"dB" = ( -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 4 - }, -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 - }, -/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 8 }, -/obj/structure/stone_tile/center/cracked, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"dC" = ( -/obj/structure/stone_tile, /turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"dD" = ( +"et" = ( /obj/structure/stone_tile/cracked{ dir = 8 }, /turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"dE" = ( -/obj/structure/stone_tile, +"eT" = ( /obj/structure/stone_tile/cracked{ - dir = 8 - }, -/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"dP" = ( -/obj/structure/stone_tile/block{ dir = 4 }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"eu" = ( -/obj/structure/stone_tile, -/obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"eC" = ( -/obj/structure/stone_tile/block{ +/obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/item/pickaxe, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"eG" = ( -/obj/structure/closet/crate/radiation, -/obj/structure/stone_tile/block/cracked{ +"fg" = ( +/obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/structure/stone_tile/cracked{ - dir = 1 +/obj/structure/stone_tile{ + dir = 4 }, -/obj/item/flashlight/flare/torch, -/obj/item/flashlight/flare/torch, -/obj/item/flashlight/flare/torch, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"fM" = ( +"ge" = ( +/obj/structure/bonfire/dense, +/obj/structure/stone_tile/center, /obj/effect/mapping_helpers/no_lava, -/mob/living/basic/mining/gutlunch/warrior, -/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"gi" = ( -/obj/structure/table/optable, -/obj/structure/stone_tile{ +"gz" = ( +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 4 + }, +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 1 }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"hF" = ( -/obj/structure/stone_tile/block/cracked{ +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 8 }, +/obj/structure/stone_tile/center/cracked, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"gY" = ( +/obj/structure/stone_tile, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"id" = ( -/obj/structure/stone_tile{ - dir = 8 - }, -/obj/structure/stone_tile/cracked, -/obj/effect/decal/cleanable/blood, +"hH" = ( +/obj/structure/stone_tile/block, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) @@ -675,365 +549,445 @@ /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"iy" = ( -/obj/structure/stone_tile/block{ +"iS" = ( +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"jj" = ( +/obj/effect/mapping_helpers/no_lava, +/obj/structure/railing/wooden_fence{ dir = 8 }, -/obj/structure/stone_tile/block/cracked{ +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"jZ" = ( +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 4 }, +/obj/structure/stone_tile/surrounding_tile/cracked, +/obj/structure/stone_tile/surrounding_tile{ + dir = 8 + }, +/obj/structure/stone_tile/center, +/obj/item/hatchet/wooden, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"iW" = ( -/obj/structure/stone_tile/block, -/obj/structure/stone_tile/block{ - dir = 1 - }, +"ky" = ( /obj/effect/mapping_helpers/no_lava, +/obj/structure/ore_container/food_trough/gutlunch_trough, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"kX" = ( -/obj/structure/stone_tile/block/cracked{ +"kW" = ( +/obj/structure/stone_tile/surrounding_tile{ dir = 4 }, -/obj/item/flashlight/flare/torch, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"mm" = ( -/obj/structure/stone_tile, -/obj/structure/stone_tile{ +/obj/structure/stone_tile/surrounding_tile{ dir = 1 }, -/obj/structure/stone_tile/cracked{ - dir = 4 - }, -/obj/structure/stone_tile/cracked{ +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 8 }, +/obj/structure/stone_tile/center, /obj/effect/decal/cleanable/blood, -/obj/item/crusher_trophy/retool_kit/ashenskull, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"mo" = ( -/obj/structure/stone_tile/block, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"mp" = ( -/obj/structure/stone_tile/surrounding/cracked, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"mB" = ( -/obj/structure/stone_tile/surrounding/cracked{ - dir = 1 +"kY" = ( +/obj/structure/stone_tile/block/cracked{ + dir = 4 }, -/obj/item/flashlight/lantern, +/obj/item/flashlight/flare/torch, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"mL" = ( -/obj/effect/mob_spawn/corpse/human/damaged, -/obj/effect/decal/cleanable/blood, -/obj/structure/stone_tile/cracked{ - dir = 1 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"nB" = ( -/obj/effect/decal/cleanable/blood, -/obj/structure/stone_tile/block, -/obj/structure/stone_tile/cracked{ - dir = 4 - }, +"lK" = ( /obj/structure/stone_tile/cracked{ dir = 1 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"oI" = ( +"lU" = ( +/obj/effect/mapping_helpers/no_lava, /obj/structure/stone_tile{ dir = 8 }, -/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"pT" = ( -/obj/structure/stone_tile/surrounding_tile/cracked, -/obj/structure/stone_tile/surrounding_tile/cracked{ +"lX" = ( +/obj/structure/stone_tile/block/cracked{ dir = 1 }, -/obj/structure/stone_tile/surrounding_tile{ - dir = 8 - }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"mM" = ( /obj/structure/stone_tile/center, /obj/effect/mapping_helpers/no_lava, +/obj/item/flashlight/flare/torch, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"qm" = ( -/obj/effect/mapping_helpers/no_lava, +"mS" = ( /obj/structure/stone_tile/block/cracked{ - dir = 8 + dir = 4 }, -/obj/structure/stone_tile/block/cracked{ +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"nb" = ( +/obj/structure/stone_tile/surrounding_tile{ dir = 4 }, +/obj/structure/stone_tile/center/cracked, +/obj/structure/stone_tile/surrounding_tile/cracked, +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 1 + }, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"qn" = ( -/obj/item/storage/box/rxglasses, -/obj/structure/stone_tile{ - dir = 1 +"nd" = ( +/obj/structure/stone_tile/block{ + dir = 8 + }, +/obj/structure/stone_tile/block/cracked{ + dir = 4 }, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"qw" = ( -/obj/structure/stone_tile/slab, +/area/lavaland/surface/outdoors) +"nf" = ( +/obj/structure/stone_tile/slab/cracked, /obj/effect/decal/cleanable/blood, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"nk" = ( /obj/effect/mapping_helpers/no_lava, +/obj/structure/stone_tile/block/cracked{ + dir = 8 + }, +/obj/structure/stone_tile/block/cracked{ + dir = 4 + }, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"qH" = ( +"np" = ( /obj/effect/mapping_helpers/no_lava, -/obj/structure/railing/wooden_fence, +/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"qV" = ( -/obj/structure/stone_tile/surrounding_tile{ - dir = 8 +"nS" = ( +/obj/structure/stone_tile{ + dir = 1 }, -/obj/structure/stone_tile/surrounding_tile, -/obj/structure/stone_tile/center/cracked, -/obj/structure/stone_tile/surrounding_tile/cracked{ +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"of" = ( +/obj/structure/stone_tile/cracked{ dir = 4 }, +/obj/structure/closet/crate/internals, +/obj/item/pickaxe, +/obj/item/pickaxe, +/obj/item/pickaxe, +/obj/item/pickaxe, +/obj/item/pickaxe, +/obj/item/pickaxe, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"qZ" = ( -/obj/structure/stone_tile/block{ +"oy" = ( +/obj/structure/stone_tile/cracked{ dir = 4 }, -/obj/structure/stone_tile/block/cracked{ - dir = 8 - }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"rs" = ( -/obj/structure/stone_tile/surrounding_tile/cracked{ +"oC" = ( +/obj/structure/stone_tile/block/cracked, +/obj/structure/stone_tile{ dir = 4 }, -/obj/structure/stone_tile/surrounding_tile/cracked, -/obj/structure/stone_tile/surrounding_tile{ +/obj/structure/closet/crate/hydroponics, +/obj/item/seeds/glowshroom, +/obj/item/seeds/glowshroom, +/obj/item/seeds/lavaland/porcini, +/obj/item/seeds/lavaland/porcini, +/obj/item/seeds/tower, +/obj/item/seeds/tower, +/obj/item/seeds/bamboo, +/obj/item/seeds/bamboo, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"oH" = ( +/obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/structure/stone_tile/center, -/obj/item/hatchet/wooden, -/obj/effect/mapping_helpers/no_lava, +/obj/item/ai_module/combat, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"ry" = ( -/obj/structure/stone_tile/block, +/area/ruin/unpowered/ash_walkers) +"oT" = ( +/obj/item/shovel, +/obj/structure/stone_tile/cracked{ + dir = 8 + }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"rZ" = ( -/obj/structure/stone_tile/slab/cracked{ +"pu" = ( +/obj/item/stack/sheet/mineral/wood, +/obj/structure/stone_tile{ dir = 4 }, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"sd" = ( -/obj/structure/stone_tile/surrounding_tile{ - dir = 4 +/area/lavaland/surface/outdoors) +"pM" = ( +/obj/structure/stone_tile, +/obj/structure/stone_tile{ + dir = 8 }, -/obj/structure/stone_tile/surrounding_tile{ +/obj/structure/stone_tile/cracked{ dir = 1 }, +/obj/item/spear, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"pQ" = ( +/obj/machinery/hydroponics/soil, /obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 8 + dir = 1 + }, +/obj/structure/stone_tile/surrounding_tile, +/obj/structure/stone_tile/surrounding_tile{ + dir = 4 }, /obj/structure/stone_tile/center, -/obj/effect/decal/cleanable/blood, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"sz" = ( +"qq" = ( +/turf/closed/indestructible/riveted/boss, +/area/lavaland/surface/outdoors) +"qv" = ( +/obj/structure/stone_tile/surrounding_tile/cracked, +/obj/structure/ore_box, /obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"qW" = ( /obj/structure/stone_tile/block/cracked{ dir = 8 }, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"ty" = ( -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"tY" = ( +"rA" = ( /obj/structure/stone_tile/block/cracked{ + dir = 8 + }, +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 1 }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"rD" = ( /obj/effect/mapping_helpers/no_lava, +/obj/structure/table/wood, +/obj/item/reagent_containers/cup/bucket/wooden, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"ua" = ( -/obj/structure/stone_tile{ - dir = 4 +"rS" = ( +/obj/structure/closet/crate/radiation, +/obj/structure/stone_tile/block/cracked{ + dir = 8 }, /obj/structure/stone_tile/cracked{ - dir = 8 + dir = 1 }, -/obj/item/spear, +/obj/item/flashlight/flare/torch, +/obj/item/flashlight/flare/torch, +/obj/item/flashlight/flare/torch, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"sS" = ( /obj/effect/mapping_helpers/no_lava, +/obj/structure/table/wood, +/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"uv" = ( -/obj/structure/stone_tile/surrounding_tile{ +"sX" = ( +/obj/effect/mapping_helpers/no_lava, +/mob/living/basic/mining/gutlunch/warrior, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"sY" = ( +/obj/effect/decal/cleanable/blood, +/obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/structure/stone_tile/block{ +/obj/structure/stone_tile/cracked{ dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"vk" = ( -/obj/structure/stone_tile, +"uz" = ( +/obj/structure/stone_tile/surrounding/cracked{ + dir = 1 + }, +/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"vw" = ( +"uF" = ( +/obj/structure/stone_tile/slab, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"uP" = ( /obj/structure/stone_tile/cracked{ - dir = 8 + dir = 4 }, -/obj/structure/closet/crate, -/obj/item/flashlight/flare/torch, -/obj/item/flashlight/flare/torch, -/obj/item/flashlight/flare/torch, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"wB" = ( -/obj/structure/stone_tile/block, +"vf" = ( +/obj/structure/stone_tile/block/cracked, /obj/structure/stone_tile{ - dir = 4 + dir = 1 }, /obj/structure/stone_tile{ + dir = 4 + }, +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"vm" = ( +/obj/structure/stone_tile/block/cracked{ dir = 1 }, +/obj/structure/stone_tile/block, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"wQ" = ( +"vJ" = ( +/obj/effect/mapping_helpers/no_lava, +/obj/structure/stone_tile, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"wa" = ( /obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/structure/stone_tile{ - dir = 4 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"xd" = ( -/obj/structure/stone_tile/block/cracked, -/obj/item/storage/toolbox/syndicate, +/obj/structure/closet/crate, +/obj/item/flashlight/flare/torch, +/obj/item/flashlight/flare/torch, +/obj/item/flashlight/flare/torch, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"xo" = ( +"wt" = ( +/obj/effect/decal/cleanable/blood, +/obj/structure/stone_tile/block, /obj/structure/stone_tile/cracked{ - dir = 8 + dir = 4 }, -/obj/effect/mob_spawn/corpse/human/damaged, -/obj/effect/decal/cleanable/blood, /obj/structure/stone_tile/cracked{ dir = 1 }, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"xH" = ( -/obj/item/spear, -/obj/structure/stone_tile{ - dir = 4 +"wL" = ( +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 8 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"wM" = ( +/obj/machinery/hydroponics/soil, +/obj/structure/stone_tile/block{ + dir = 8 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"zD" = ( -/obj/item/stack/sheet/mineral/wood, -/obj/structure/stone_tile{ - dir = 4 - }, +"wT" = ( +/obj/structure/stone_tile/slab, +/obj/effect/decal/cleanable/blood, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"zR" = ( -/obj/structure/stone_tile/surrounding_tile, -/obj/structure/stone_tile/surrounding_tile{ - dir = 1 - }, -/obj/structure/stone_tile/surrounding_tile/cracked{ +"wV" = ( +/obj/structure/stone_tile/slab, +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"xM" = ( +/obj/structure/stone_tile/block{ dir = 4 }, -/obj/structure/stone_tile/center, +/obj/structure/stone_tile/block/cracked{ + dir = 8 + }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Aj" = ( -/obj/structure/stone_tile/cracked{ - dir = 1 - }, +"yh" = ( +/obj/structure/stone_tile/cracked, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"AV" = ( -/obj/item/pickaxe, -/obj/structure/stone_tile/cracked{ +"ys" = ( +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"yw" = ( +/obj/structure/stone_tile/block, +/obj/structure/stone_tile/block{ dir = 1 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Bc" = ( -/obj/item/shovel, -/obj/structure/stone_tile/cracked{ +"yA" = ( +/obj/structure/stone_tile/block{ dir = 8 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Bl" = ( -/obj/structure/stone_tile/surrounding_tile, -/obj/structure/stone_tile/center/cracked, +"zw" = ( +/obj/structure/stone_tile, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"zG" = ( +/obj/structure/stone_tile/surrounding_tile/cracked, /obj/structure/stone_tile/surrounding_tile/cracked{ dir = 1 }, -/obj/structure/stone_tile/surrounding_tile/cracked{ +/obj/structure/stone_tile/surrounding_tile{ dir = 8 }, +/obj/structure/stone_tile/center, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Bq" = ( -/obj/structure/stone_tile/cracked{ - dir = 4 - }, -/obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"Cg" = ( -/obj/structure/stone_tile/block/cracked{ +"AI" = ( +/obj/structure/stone_tile/surrounding_tile{ dir = 8 }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"Dx" = ( -/obj/effect/decal/cleanable/blood, -/obj/structure/stone_tile/cracked{ - dir = 8 +/obj/structure/stone_tile/center, +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 1 }, -/obj/structure/stone_tile/cracked{ +/obj/structure/stone_tile/surrounding_tile/cracked{ dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Dz" = ( +"AL" = ( +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 4 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"AU" = ( /obj/effect/mapping_helpers/no_lava, /obj/structure/stone_tile{ dir = 8 @@ -1041,210 +995,212 @@ /obj/structure/stone_tile, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Ee" = ( -/obj/structure/stone_tile/block/cracked, -/obj/structure/stone_tile{ - dir = 1 - }, +"Be" = ( +/obj/item/spear, /obj/structure/stone_tile{ dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Fa" = ( -/obj/structure/stone_tile/block{ - dir = 4 - }, -/obj/effect/mapping_helpers/no_lava, +"Bx" = ( +/obj/structure/stone_tile/block/cracked, +/obj/item/storage/toolbox/syndicate, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"Fg" = ( -/obj/structure/stone_tile, -/obj/structure/stone_tile{ - dir = 8 - }, +/area/ruin/unpowered/ash_walkers) +"Cd" = ( /obj/structure/stone_tile/cracked{ dir = 1 }, -/obj/item/spear, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"Fp" = ( -/obj/structure/stone_tile/slab, +"Dm" = ( +/obj/structure/stone_tile{ + dir = 8 + }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"FJ" = ( -/obj/structure/stone_tile{ - dir = 1 - }, -/obj/item/weldingtool/experimental, +"Dq" = ( +/obj/effect/mapping_helpers/no_lava, +/obj/structure/railing/corner/end/wooden_fence, +/obj/structure/railing/corner/end/flip/wooden_fence, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"Go" = ( +/area/lavaland/surface/outdoors) +"Dr" = ( /obj/structure/stone_tile/cracked{ - dir = 4 + dir = 1 }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"DX" = ( +/obj/item/pickaxe, /obj/structure/stone_tile/cracked{ dir = 1 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Gv" = ( +"EN" = ( /obj/effect/mapping_helpers/no_lava, +/obj/structure/stone_tile/block, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Gx" = ( -/obj/structure/stone_tile/block/cracked, +"FM" = ( +/obj/structure/stone_tile/cracked{ + dir = 8 + }, +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"GY" = ( +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 1 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"Hn" = ( /obj/structure/stone_tile{ dir = 4 }, -/obj/structure/closet/crate/hydroponics, -/obj/item/seeds/glowshroom, -/obj/item/seeds/glowshroom, -/obj/item/seeds/lavaland/porcini, -/obj/item/seeds/lavaland/porcini, -/obj/item/seeds/tower, -/obj/item/seeds/tower, -/obj/item/seeds/bamboo, -/obj/item/seeds/bamboo, +/obj/structure/closet/crate/medical, +/obj/item/storage/medkit/regular, +/obj/item/reagent_containers/blood/random, +/obj/item/reagent_containers/blood/lizard, +/obj/item/reagent_containers/blood/lizard, +/obj/item/stack/sheet/cloth/ten, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"GW" = ( +"HW" = ( +/obj/structure/stone_tile/block/cracked{ + dir = 8 + }, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"In" = ( +/obj/structure/stone_tile/surrounding/cracked, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"IJ" = ( /obj/structure/stone_tile/cracked{ - dir = 4 + dir = 1 + }, +/obj/structure/stone_tile/cracked, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"Jz" = ( +/obj/structure/stone_tile{ + dir = 1 + }, +/obj/item/weldingtool/experimental, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"JF" = ( +/obj/structure/stone_tile/block, +/obj/structure/stone_tile/block/cracked{ + dir = 1 }, -/obj/structure/closet/crate/internals, -/obj/item/pickaxe, -/obj/item/pickaxe, -/obj/item/pickaxe, -/obj/item/pickaxe, -/obj/item/pickaxe, -/obj/item/pickaxe, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"HU" = ( -/obj/structure/stone_tile/block{ +"JQ" = ( +/obj/structure/stone_tile, +/obj/structure/stone_tile{ dir = 8 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Ic" = ( -/obj/machinery/hydroponics/soil, -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 - }, -/obj/structure/stone_tile/surrounding_tile, -/obj/structure/stone_tile/surrounding_tile{ +"Kc" = ( +/obj/structure/stone_tile/block, +/obj/structure/stone_tile{ dir = 4 }, -/obj/structure/stone_tile/center, +/obj/structure/stone_tile{ + dir = 1 + }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Ie" = ( -/obj/structure/stone_tile/cracked{ - dir = 4 +"Kt" = ( +/obj/structure/water_source/puddle{ + pixel_x = -3; + pixel_y = 1 }, +/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) +"KI" = ( +/obj/structure/table/optable, /obj/structure/stone_tile{ dir = 1 }, -/obj/structure/stone_tile{ - dir = 8 - }, -/obj/effect/decal/cleanable/blood, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"If" = ( -/obj/structure/stone_tile/cracked{ +"LR" = ( +/obj/structure/stone_tile/block{ dir = 8 }, -/obj/item/malf_upgrade, +/obj/machinery/iv_drip, +/obj/item/reagent_containers/cup/glass/waterbottle/large, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"Iy" = ( -/obj/structure/stone_tile/center, -/obj/effect/mapping_helpers/no_lava, -/obj/item/flashlight/flare/torch, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"IS" = ( -/obj/structure/stone_tile/block, -/obj/item/spear, -/obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"JD" = ( +"LV" = ( +/obj/structure/stone_tile/block{ + dir = 4 + }, /obj/effect/mapping_helpers/no_lava, -/obj/structure/ore_container/food_trough/gutlunch_trough, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Kt" = ( -/obj/structure/water_source/puddle{ - pixel_x = -3; - pixel_y = 1 +"ME" = ( +/obj/structure/stone_tile/slab/cracked{ + dir = 4 }, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"MQ" = ( /obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) -"KQ" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/table/wood, -/obj/item/reagent_containers/cup/bucket/wooden, +/obj/structure/railing/wooden_fence, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"KR" = ( -/obj/structure/stone_tile/block/cracked, +"MT" = ( /obj/effect/mapping_helpers/no_lava, +/mob/living/basic/mining/gutlunch/warrior, +/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Lb" = ( -/obj/structure/stone_tile/cracked{ +"Nb" = ( +/obj/structure/stone_tile/surrounding_tile, +/obj/structure/stone_tile/surrounding_tile{ dir = 1 }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"LH" = ( -/obj/structure/stone_tile, -/obj/structure/stone_tile{ - dir = 8 +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 4 }, +/obj/structure/stone_tile/center, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"LN" = ( +"Nc" = ( /obj/structure/stone_tile/surrounding_tile{ - dir = 4 + dir = 8 }, +/obj/structure/stone_tile/surrounding_tile, /obj/structure/stone_tile/center/cracked, -/obj/structure/stone_tile/surrounding_tile/cracked, /obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 + dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"MD" = ( +"Nz" = ( +/obj/effect/mapping_helpers/no_lava, /obj/structure/stone_tile/block/cracked{ - dir = 4 - }, -/obj/structure/stone_tile/block{ dir = 8 }, -/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"MY" = ( -/obj/structure/stone_tile/cracked{ - dir = 4 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"Nv" = ( +"Ob" = ( /obj/structure/stone_tile/block/cracked, /obj/structure/stone_tile/block{ dir = 1 @@ -1252,75 +1208,92 @@ /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"NV" = ( -/obj/structure/stone_tile{ +"Om" = ( +/obj/structure/stone_tile/block/cracked, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"OA" = ( +/obj/structure/stone_tile/block{ dir = 1 }, -/obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"Oe" = ( -/obj/effect/mapping_helpers/no_lava, /obj/structure/stone_tile{ dir = 8 }, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"OY" = ( -/obj/structure/stone_tile/surrounding_tile/cracked, -/obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Pk" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/railing/corner/end/wooden_fence, -/obj/structure/railing/corner/end/flip/wooden_fence, +"Pb" = ( +/obj/effect/mob_spawn/corpse/human/damaged, +/obj/effect/decal/cleanable/blood, +/obj/structure/stone_tile/cracked{ + dir = 1 + }, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"PA" = ( +"Qh" = ( +/obj/structure/stone_tile/cracked{ + dir = 4 + }, +/obj/structure/stone_tile/cracked{ + dir = 1 + }, /obj/effect/mapping_helpers/no_lava, -/mob/living/basic/mining/gutlunch/warrior, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"PK" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/stone_tile, +"Qp" = ( +/obj/structure/stone_tile/block{ + dir = 4 + }, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"RE" = ( -/obj/structure/stone_tile/block/cracked{ +/area/ruin/unpowered/ash_walkers) +"Qs" = ( +/obj/structure/stone_tile/cracked{ + dir = 8 + }, +/obj/effect/mob_spawn/corpse/human/damaged, +/obj/effect/decal/cleanable/blood, +/obj/structure/stone_tile/cracked{ dir = 1 }, -/obj/structure/stone_tile/block, -/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"RH" = ( +"Si" = ( /obj/structure/stone_tile{ - dir = 4 + dir = 8 }, +/obj/structure/stone_tile/cracked, +/obj/effect/decal/cleanable/blood, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Sf" = ( -/obj/structure/stone_tile/block/cracked{ - dir = 4 +"Sl" = ( +/obj/item/storage/box/rxglasses, +/obj/structure/stone_tile{ + dir = 1 }, -/obj/item/reagent_containers/cup/bucket/wooden, -/obj/effect/mapping_helpers/no_lava, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"Sx" = ( +/obj/structure/stone_tile/block, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Sq" = ( -/obj/structure/stone_tile/cracked{ - dir = 4 +"SU" = ( +/obj/structure/stone_tile/block, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"UA" = ( +/obj/structure/stone_tile/block{ + dir = 1 }, -/obj/structure/stone_tile/cracked{ - dir = 8 +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"UD" = ( +/obj/structure/stone_tile{ + dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Th" = ( +"UZ" = ( /obj/structure/stone_tile/block/cracked{ dir = 1 }, @@ -1331,93 +1304,120 @@ /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Tl" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/table/wood, -/obj/item/flashlight/lantern, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +"Vd" = ( +/obj/structure/stone_tile/block/cracked{ + dir = 8 + }, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Tr" = ( -/obj/machinery/hydroponics/soil, +"Vy" = ( /obj/structure/stone_tile/block{ dir = 8 }, +/obj/item/pickaxe, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"TC" = ( +"VO" = ( +/obj/structure/stone_tile/block/cracked{ + dir = 4 + }, +/obj/item/reagent_containers/cup/bucket/wooden, /obj/effect/mapping_helpers/no_lava, -/obj/item/flashlight/lantern, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"TO" = ( +"Ws" = ( +/obj/structure/stone_tile/surrounding_tile/cracked, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"WO" = ( /obj/structure/stone_tile{ dir = 4 }, -/obj/structure/closet/crate/medical, -/obj/item/storage/medkit/regular, -/obj/item/reagent_containers/blood/random, -/obj/item/reagent_containers/blood/lizard, -/obj/item/reagent_containers/blood/lizard, -/obj/item/stack/sheet/cloth/ten, +/obj/structure/stone_tile/cracked{ + dir = 8 + }, +/obj/item/spear, +/obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/ruin/unpowered/ash_walkers) -"TY" = ( +/area/lavaland/surface/outdoors) +"Xk" = ( /obj/structure/stone_tile/surrounding_tile{ - dir = 8 + dir = 4 }, -/obj/structure/stone_tile/center, -/obj/structure/stone_tile/surrounding_tile/cracked{ - dir = 1 +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"Xt" = ( +/obj/structure/stone_tile/surrounding_tile{ + dir = 8 }, -/obj/structure/stone_tile/surrounding_tile/cracked{ +/obj/structure/stone_tile/block{ dir = 4 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Uh" = ( -/obj/effect/mapping_helpers/no_lava, -/obj/structure/stone_tile/block, -/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, -/area/lavaland/surface/outdoors) -"UI" = ( -/obj/structure/stone_tile/block{ +"XY" = ( +/obj/structure/stone_tile, +/obj/structure/stone_tile{ + dir = 1 + }, +/obj/structure/stone_tile/cracked{ + dir = 4 + }, +/obj/structure/stone_tile/cracked{ dir = 8 }, -/obj/machinery/iv_drip, -/obj/item/reagent_containers/cup/glass/waterbottle/large, +/obj/effect/decal/cleanable/blood, +/obj/item/crusher_trophy/retool_kit/ashenskull, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/ruin/unpowered/ash_walkers) -"UL" = ( -/obj/structure/stone_tile/cracked, +"Yc" = ( +/obj/structure/stone_tile/surrounding_tile/cracked, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Wh" = ( -/obj/structure/stone_tile/block, +"Yi" = ( +/obj/effect/mapping_helpers/no_lava, +/mob/living/basic/mining/gutlunch/milk, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/lavaland/surface/outdoors) +"YN" = ( /obj/structure/stone_tile/block/cracked{ dir = 1 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Xp" = ( -/obj/structure/stone_tile/cracked{ +"ZD" = ( +/obj/structure/stone_tile/block/cracked{ + dir = 4 + }, +/obj/structure/stone_tile/block{ dir = 8 }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"YO" = ( -/obj/structure/bonfire/dense, -/obj/structure/stone_tile/center, +"ZH" = ( +/obj/structure/stone_tile, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, +/area/ruin/unpowered/ash_walkers) +"ZO" = ( +/obj/structure/stone_tile/block, +/obj/item/spear, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) -"Zr" = ( -/obj/structure/stone_tile/surrounding_tile/cracked, -/obj/structure/ore_box, +"ZW" = ( +/obj/structure/stone_tile/surrounding_tile, +/obj/structure/stone_tile/center/cracked, +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 1 + }, +/obj/structure/stone_tile/surrounding_tile/cracked{ + dir = 8 + }, /obj/effect/mapping_helpers/no_lava, /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) @@ -1427,49 +1427,49 @@ aa aa aa aa -ah -ah -ah -ah -ah -ah -ah +ys +ys +ys +ys +ys +ys +ys aa aa -ah -ah -ah -ah +ys +ys +ys +ys aa aa aa "} (2,1,1) = {" aa -ah -ah -ah -ah -ah -ah -bi -ah -bi -cO -ah -ah -ah -ah -ah -ah -ah +ys +ys +ys +ys +ys +ys +Vd +ys +Vd +wL +ys +ys +ys +ys +ys +ys +ys aa aa "} (3,1,1) = {" aa aa -ah +ys as as as @@ -1477,131 +1477,131 @@ as ak as as -cP -ah -ah -eu -oI -AV -ah -ah -ah +SU +ys +ys +gY +Dm +DX +ys +ys +ys aa "} (4,1,1) = {" aa aa -ah +ys ak -If -MY -dP +oH +uP +Qp bj -vw -ak -cP -eu -Bq -ua -Xp -Xp -xo -ah -ah +wa +ak +SU +gY +oy +WO +FM +FM +Qs +ys +ys aa "} (5,1,1) = {" aa aa -ac +UA as -Gx -FJ +oC +Jz aY bk bw ak -NV -Sq -Bl -Fa -LN -oI -RH -NV -ah -ah +nS +eT +ZW +LV +nb +Dm +UD +nS +ys +ys "} (6,1,1) = {" aa aa -cT -ak -xd -qn -mB -Lb -bx -dP -Fp -MD -KR -YO -tY -id -oI -Xp -ah -ah +cz +ak +Bx +Sl +uz +Cd +nf +Qp +wV +ZD +bN +ge +YN +Si +Dm +FM +ys +ys "} (7,1,1) = {" aa aa -ae +lX as -gi -vk -Ie +KI +ZH +bQ bm by ak -KR -NV -qV -HU -TY -eu -RH -Go -ah -ah +bN +nS +Nc +yA +AI +gY +UD +Qh +ys +ys "} (8,1,1) = {" aa aa -ae +lX as -TO -UI -wQ -eG +Hn +LR +fg +rS bz ak -NV -Dx -Aj -Nv -UL -zD -Sq -Bc -ah -ah +nS +sY +lK +Ob +yh +pu +eT +oT +ys +ys "} (9,1,1) = {" aa -ah -ah +ys +ys as ak as @@ -1609,42 +1609,42 @@ as as ak ak -Bq -NV -Bq -Wh -RH -pT -Sf -Ic -dD -ah +oy +nS +oy +JF +UD +zG +VO +pQ +et +ys "} (10,1,1) = {" aa -ai -aq -aR -aR -aR -aR -eC -HU -uv -OY -Bq -NV -nB -qZ -ry -Iy +AL +rA +qq +qq +qq +qq +Vy +yA +Xt +Yc +oy +nS +wt +xM +hH +mM ie -dE -ah +cy +ys "} (11,1,1) = {" -ab -aj +Xk +uF ak ak ak @@ -1652,20 +1652,20 @@ ak ak ak ak -qw -GW -NV -RH -Nv -NV -rs -Tr -dB -dC +wT +of +nS +UD +Ob +nS +jZ +wM +gz +zw aa "} (12,1,1) = {" -ac +UA ak ak ak @@ -1675,19 +1675,19 @@ ak ak ak ak -IS -Gv -ty -Nv +ZO +dj +iS +Ob Kt -du -dz -dC -ah +Dr +IJ +zw +ys aa "} (13,1,1) = {" -ad +dm ak ak au @@ -1697,19 +1697,19 @@ bc bp ak ak -wB -Gv -mL -iW -ah -ah -ah -ah -ah -ah +Kc +dj +Pb +yw +ys +ys +ys +ys +ys +ys "} (14,1,1) = {" -ae +lX ak ak av @@ -1719,19 +1719,19 @@ bd bq ak bG -zR -LH -Bq -RE -hF -ah -bi -bi -bi -cO +Nb +JQ +oy +vm +qW +ys +Vd +Vd +Vd +wL "} (15,1,1) = {" -ac +UA ak ak aw @@ -1741,19 +1741,19 @@ be br bB bH -Fp -iy -qZ -Th +wV +nd +xM +UZ ak ak as ak ak -ah +ys "} (16,1,1) = {" -ac +UA ak ak ax @@ -1763,19 +1763,19 @@ bf bs ak bG -sd -LH -Aj -iW +kW +JQ +lK +yw as -Fg -kX +pM +kY cL as -ah +ys "} (17,1,1) = {" -af +OA ak ak ay @@ -1785,19 +1785,19 @@ bg bt ak ak -Ee -Bq -xH -Fp -Cg -rZ -mp +vf +oy +Be +wV +HW +ME +In cM as -ah +ys "} (18,1,1) = {" -ac +UA ak ak ak @@ -1807,20 +1807,20 @@ ak ak ak ak -Zr -Gv -qm -Gv +qv +dj +nk +dj as -mm +XY cF cN ak -cP +SU "} (19,1,1) = {" -ag -aj +GY +uF ak ak ak @@ -1829,125 +1829,125 @@ ak ak ak bI -Oe -Gv -qm -Gv +lU +dj +nk +dj as as as ak ak -cQ +Om "} (20,1,1) = {" -ah -aj -ak -ak -fM -Gv -TC -Gv -qH -KQ -Gv -Dz -qm -Gv -ah -ah -bC -ah -ah -cR +ys +uF +ak +ak +MT +dj +np +dj +MQ +rD +dj +AU +nk +dj +ys +ys +mS +ys +ys +Ws "} (21,1,1) = {" aa -aj -ak -ak -Gv -Gv -Gv -PA -qH -KQ -Gv -Gv -qm -Gv -ah -ah -ah -ah -ah -ah +uF +ak +ak +dj +dj +dj +sX +MQ +rD +dj +dj +nk +dj +ys +ys +ys +ys +ys +ys "} (22,1,1) = {" aa -aj -ak -ak -Gv -bJ -bJ -Gv -qH -Tl -Gv -Gv -qm -Gv -Gv -ah -ah -ah -ah -ah +uF +ak +ak +dj +Yi +Yi +dj +MQ +sS +dj +dj +nk +dj +dj +ys +ys +ys +ys +ys "} (23,1,1) = {" aa -aj -ak -ak -bJ -Gv -Gv -Gv -qH -Gv -sz -sz -qm -Gv -PK -ah -ah -ah -ah -ah +uF +ak +ak +Yi +dj +dj +dj +MQ +dj +Nz +Nz +nk +dj +vJ +ys +ys +ys +ys +ys "} (24,1,1) = {" aa aa ak ak -Gv -bJ -bJ -Gv -Pk -sz -Gv -Oe -qm -sz -Gv -ah -ah -ah -ah +dj +Yi +Yi +dj +Dq +Nz +dj +lU +nk +Nz +dj +ys +ys +ys +ys aa "} (25,1,1) = {" @@ -1955,129 +1955,129 @@ aa aa ak ak -Gv -Gv -TC -Gv -qH -Gv -Dz -Oe -qm -Gv -Gv -ah -ah -ah -ah +dj +dj +np +dj +MQ +dj +AU +lU +nk +dj +dj +ys +ys +ys +ys aa "} (26,1,1) = {" aa -ah +ys ak ak -Gv -JD -Gv -Gv -qH -Gv -Gv -Gv -Gv -Gv -Gv -ah -ah -ah +dj +ky +dj +dj +MQ +dj +dj +dj +dj +dj +dj +ys +ys +ys aa aa "} (27,1,1) = {" aa -ah +ys ak ak -Gv -Gv -Gv -PA -qH -Gv -Gv -Gv -Gv -Gv -Gv -ah -ah -ah +dj +dj +dj +sX +MQ +dj +dj +dj +dj +dj +dj +ys +ys +ys aa aa "} (28,1,1) = {" -ah -ah -ak -ak -TC -Gv -PA -TC -qH -Gv -mo -ah -Gv -Dz -Gv -ah -ah -ah +ys +ys +ak +ak +np +dj +sX +np +MQ +dj +Sx +ys +dj +AU +dj +ys +ys +ys aa aa "} (29,1,1) = {" -ah -ah -ak -ak -Gv -Gv -Gv -Gv -qH -Uh -ty -ah -Gv -Gv -Gv -ah -ah -ah +ys +ys +ak +ak +dj +dj +dj +dj +MQ +EN +iS +ys +dj +dj +dj +ys +ys +ys aa aa "} (30,1,1) = {" aa -ah +ys ak ak ak ak ak -cp -cp -Gv -ty -ah -ah -ah -ah -ah -ah +jj +jj +dj +iS +ys +ys +ys +ys +ys +ys aa aa aa @@ -2091,15 +2091,15 @@ ak ak ak ak -ah -ah -ah -ah -ah -ah -ah -ah -ah +ys +ys +ys +ys +ys +ys +ys +ys +ys aa aa aa @@ -2109,15 +2109,15 @@ aa aa aa aa -ah -ah -ah -ah -ah -ah -ah -ah -ah +ys +ys +ys +ys +ys +ys +ys +ys +ys aa aa aa @@ -2134,11 +2134,11 @@ aa aa aa aa -ah -ah -ah -ah -ah +ys +ys +ys +ys +ys aa aa aa diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm index 35c88aa39992..c730e94ad3c7 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm @@ -178,11 +178,11 @@ "aO" = ( /obj/machinery/door/airlock/multi_tile/public/glass, /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "aP" = ( /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "aQ" = ( /obj/structure/flora/grass/both/style_random, @@ -239,24 +239,24 @@ /area/ruin/powered/snow_biodome) "dS" = ( /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "eb" = ( /obj/machinery/light/small/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "eg" = ( /obj/machinery/vending/coffee, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "gh" = ( /obj/machinery/light/small/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "gz" = ( /obj/structure/chair/stool/directional/south, /mob/living/basic/skeleton/settler, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "hr" = ( /mob/living/basic/skeleton/settler, @@ -268,7 +268,7 @@ "hA" = ( /obj/machinery/light/empty/directional/north, /obj/effect/mapping_helpers/no_lava, -/turf/open/floor/pod/dark{ +/turf/open/floor/mineral/plastitanium/pod/dark{ initial_gas_mix = "LAVALAND_ATMOS" }, /area/ruin/powered/snow_biodome) @@ -289,7 +289,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "sn" = ( /obj/structure/bed{ @@ -305,7 +305,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors/explored) "tl" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/powered/snow_biodome) "uO" = ( /obj/item/bedsheet/blue{ @@ -328,7 +328,7 @@ /area/ruin/powered/snow_biodome) "xU" = ( /obj/item/storage/toolbox/mechanical, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "zT" = ( /obj/machinery/door/airlock/silver, @@ -339,7 +339,7 @@ /obj/structure/table, /obj/item/pen, /obj/item/paper, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "BM" = ( /obj/effect/turf_decal/siding/wood/corner{ @@ -352,16 +352,16 @@ /obj/item/clothing/suit/hooded/wintercoat/science, /obj/item/clothing/shoes/winterboots, /obj/item/clothing/gloves/fingerless, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "Ef" = ( /obj/structure/reagent_dispensers/fueltank, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "Ez" = ( /obj/structure/fans/tiny, /obj/machinery/door/airlock/multi_tile/public/glass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "EA" = ( /obj/effect/turf_decal/siding/wood/corner, @@ -373,30 +373,30 @@ /turf/open/misc/asteroid/snow, /area/ruin/powered/snow_biodome) "HP" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "HR" = ( /obj/structure/closet/secure_closet/freezer/fridge/open, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "JZ" = ( /obj/structure/table, /obj/machinery/microwave, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "KS" = ( /obj/item/chainsaw, /obj/structure/closet, /obj/machinery/light/small/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "Mp" = ( /obj/item/clothing/mask/balaclava, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "Oj" = ( /obj/structure/table, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "PD" = ( /obj/machinery/door/airlock/hatch, @@ -404,13 +404,13 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "PK" = ( /obj/structure/table, /obj/item/pen, /obj/item/paper_bin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "PM" = ( /obj/effect/turf_decal/siding/wood, @@ -430,7 +430,7 @@ "QK" = ( /obj/structure/table, /obj/item/storage/fancy/cigarettes/cigpack_carp, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "QN" = ( /obj/effect/spawner/structure/window, @@ -438,17 +438,17 @@ /area/ruin/powered/snow_biodome) "Sj" = ( /obj/effect/decal/cleanable/blood/oil, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "Ub" = ( /obj/structure/filingcabinet, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "UM" = ( /obj/machinery/computer/monitor{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered/snow_biodome) "VE" = ( /obj/effect/turf_decal/siding/wood/corner{ diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm index 660d01dedff3..ac5eeb236235 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_crashsite.dmm @@ -11,13 +11,13 @@ "c" = ( /obj/item/gps/computer, /obj/structure/tubes, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "d" = ( /obj/structure/tubes, /obj/structure/rack, /obj/item/survivalcapsule, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "e" = ( /obj/structure/chair/comfy/shuttle{ @@ -32,7 +32,7 @@ /obj/item/bedsheet/black{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "g" = ( /obj/structure/rack, @@ -40,7 +40,7 @@ /obj/item/clothing/suit/space/orange, /obj/item/clothing/head/helmet/space/fragile, /obj/item/clothing/head/helmet/space/fragile, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "h" = ( /obj/effect/decal/cleanable/rubble, @@ -66,7 +66,7 @@ /area/lavaland/surface/outdoors) "m" = ( /obj/machinery/door/airlock/survival_pod/glass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "n" = ( /turf/closed/wall/mineral/titanium{ @@ -80,7 +80,7 @@ /obj/structure/rack, /obj/item/storage/toolbox/emergency, /obj/item/storage/toolbox/emergency, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "q" = ( /turf/closed/wall/mineral/titanium/interior, @@ -92,7 +92,7 @@ /turf/open/floor/mineral/titanium/blue/lavaland_atmos, /area/ruin/unpowered) "s" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "t" = ( /obj/structure/flora/tree/stump, @@ -167,11 +167,11 @@ desc = "A heated storage unit. This one's seen better days."; name = "dusty survival pod storage" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "K" = ( /obj/structure/fans, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "L" = ( /obj/item/storage/toolbox/fishing, @@ -182,10 +182,10 @@ /obj/item/pickaxe/emergency, /obj/item/pickaxe/emergency, /obj/item/crowbar/large/emergency, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "N" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "O" = ( /obj/effect/spawner/structure/window/reinforced/shuttle, @@ -205,11 +205,11 @@ "S" = ( /obj/structure/bed/pod, /obj/item/bedsheet/black, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "T" = ( /obj/structure/tubes, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "U" = ( /obj/item/bait_can/worm/premium, @@ -218,7 +218,7 @@ "V" = ( /obj/structure/table/survival_pod, /obj/item/food/meat/steak/goliath, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "W" = ( /turf/closed/mineral/volcanic/lava_land_surface/biome_replace, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm index c7c0e7e32836..38d285aa34df 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm @@ -87,7 +87,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) "t" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "u" = ( /obj/structure/bed/pod, @@ -96,18 +96,18 @@ /area/ruin/powered) "v" = ( /obj/structure/fans, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "w" = ( /obj/machinery/smartfridge/survival_pod/preloaded, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "x" = ( /obj/effect/mob_spawn/ghost_role/human/hermit, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "y" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "z" = ( /obj/machinery/light/small/directional/east, @@ -121,13 +121,13 @@ /area/ruin/powered) "B" = ( /obj/structure/table/survival_pod, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "C" = ( /obj/structure/tubes, /obj/item/crowbar, /obj/effect/decal/cleanable/blood/drip, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "D" = ( /obj/effect/decal/cleanable/blood/footprints{ @@ -135,7 +135,7 @@ }, /obj/machinery/door/airlock/survival_pod/glass, /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "E" = ( /obj/machinery/power/shuttle_engine/propulsion/burst{ diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm index 9e01f5f0063c..56d2f0dd53be 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm @@ -7,36 +7,36 @@ /area/lavaland/surface/outdoors) "c" = ( /obj/effect/turf_decal/mining, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "d" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "e" = ( /obj/effect/turf_decal/mining/survival, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "f" = ( /obj/structure/fans, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "g" = ( /obj/machinery/smartfridge/survival_pod{ desc = "A heated storage unit. This one's seen better days."; name = "dusty survival pod storage" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "h" = ( /obj/item/gps/computer, /obj/structure/tubes, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "i" = ( /obj/effect/turf_decal/mining/survival{ dir = 8 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "j" = ( /obj/effect/decal/cleanable/blood/drip, @@ -44,19 +44,19 @@ /area/lavaland/surface/outdoors) "k" = ( /obj/machinery/sleeper/survival_pod, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "l" = ( /obj/item/pickaxe, /obj/effect/decal/cleanable/blood, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "m" = ( /obj/structure/bed/pod, /obj/item/bedsheet/black, /obj/structure/tubes, /obj/machinery/light/small/broken/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "n" = ( /obj/effect/decal/cleanable/blood, @@ -70,7 +70,7 @@ "p" = ( /obj/structure/table/survival_pod, /obj/item/knife/combat/survival, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "q" = ( /obj/effect/mob_spawn/corpse/human/miner/explorer{ @@ -82,13 +82,13 @@ /obj/effect/decal/cleanable/blood/footprints{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "r" = ( /obj/structure/tubes, /obj/item/crowbar, /obj/effect/decal/cleanable/blood/drip, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "s" = ( /obj/effect/decal/cleanable/blood/footprints, @@ -99,7 +99,7 @@ dir = 1 }, /obj/machinery/door/airlock/survival_pod/glass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/powered) "u" = ( /obj/effect/decal/cleanable/blood/footprints{ @@ -119,7 +119,7 @@ /obj/effect/turf_decal/mining/survival{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "x" = ( /obj/effect/decal/cleanable/blood/drip, @@ -132,7 +132,7 @@ /obj/effect/turf_decal/mining/survival{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/ruin/powered) "z" = ( /obj/effect/decal/cleanable/blood/footprints{ diff --git a/_maps/RandomRuins/SpaceRuins/crashedship.dmm b/_maps/RandomRuins/SpaceRuins/crashedship.dmm index 13428e3432f3..cebb697e2177 100644 --- a/_maps/RandomRuins/SpaceRuins/crashedship.dmm +++ b/_maps/RandomRuins/SpaceRuins/crashedship.dmm @@ -94,7 +94,7 @@ /obj/structure/chair/comfy/shuttle{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "eC" = ( /obj/effect/turf_decal/stripes/line{ @@ -141,7 +141,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/crashedship/aft) "gC" = ( /obj/item/reagent_containers/cup/glass/bottle/beer/almost_empty{ @@ -174,7 +174,7 @@ }, /obj/effect/spawner/random/maintenance/two, /obj/machinery/light/small/broken/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "ip" = ( /obj/machinery/light/broken/directional/north, @@ -423,7 +423,7 @@ }, /obj/effect/turf_decal/trimline/blue/line, /obj/structure/closet/firecloset, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "qI" = ( /obj/item/stack/ore/silver, @@ -843,7 +843,7 @@ /obj/machinery/door/airlock/grunge{ name = "Emergency Supplies" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/crashedship/aft) "Ff" = ( /obj/effect/turf_decal/siding/white{ @@ -873,7 +873,7 @@ /obj/effect/turf_decal/trimline/blue/line, /obj/machinery/atmospherics/components/unary/portables_connector/layer4, /obj/machinery/portable_atmospherics/canister/air, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "FP" = ( /obj/effect/spawner/random/maintenance, @@ -1163,7 +1163,7 @@ }, /obj/structure/table, /obj/item/door_seal, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "Ph" = ( /obj/effect/turf_decal/delivery/white{ @@ -1228,7 +1228,7 @@ }, /obj/effect/turf_decal/trimline/blue/line, /obj/effect/spawner/random/structure/tank_holder, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "Uc" = ( /obj/effect/turf_decal/trimline/yellow/filled/end{ @@ -1243,7 +1243,7 @@ }, /obj/effect/turf_decal/trimline/blue/line, /obj/structure/chair/comfy/shuttle, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/crashedship/aft) "Uz" = ( /turf/closed/mineral/gibtonite, @@ -1351,7 +1351,7 @@ "Yw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/crashedship/aft) "YU" = ( /obj/item/stack/rods, diff --git a/_maps/RandomRuins/SpaceRuins/derelict9.dmm b/_maps/RandomRuins/SpaceRuins/derelict9.dmm index 2fa948a60fae..f2b174e00347 100644 --- a/_maps/RandomRuins/SpaceRuins/derelict9.dmm +++ b/_maps/RandomRuins/SpaceRuins/derelict9.dmm @@ -20,7 +20,7 @@ /area/ruin/space) "j" = ( /obj/structure/closet/crate/secure/loot, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "l" = ( /turf/template_noop, @@ -50,22 +50,22 @@ /obj/structure/rack, /obj/effect/spawner/random/clothing/beret_or_rabbitears, /obj/effect/spawner/random/clothing/mafia_outfit, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "x" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/ruin/space/has_grav) "B" = ( /obj/structure/rack, /obj/effect/spawner/random/clothing/funny_hats, /obj/effect/spawner/random/clothing/gloves, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "C" = ( /obj/structure/rack, /obj/effect/spawner/random/clothing/mafia_outfit, /obj/effect/spawner/random/clothing/pirate_or_bandana, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "E" = ( /obj/structure/mineral_door/wood, @@ -74,7 +74,7 @@ "G" = ( /obj/structure/safe, /obj/item/clothing/head/collectable/petehat, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav) "I" = ( /obj/machinery/porta_turret/syndicate/pod, @@ -99,7 +99,7 @@ /turf/open/floor/plating/airless, /area/ruin/space) "Q" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav) "S" = ( /obj/item/flashlight/lantern, @@ -112,17 +112,17 @@ /obj/structure/rack, /obj/effect/spawner/random/clothing/kittyears_or_rabbitears, /obj/effect/spawner/random/clothing/lizardboots, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "X" = ( /obj/structure/rack, /obj/effect/spawner/random/clothing/twentyfive_percent_cyborg_mask, /obj/effect/spawner/random/clothing/costume, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav) "Y" = ( /obj/machinery/light/dim/directional/north, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/ruin/space/has_grav) "Z" = ( /turf/closed/indestructible/fakedoor, diff --git a/_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm b/_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm index b3cd97c716b9..664719d67761 100644 --- a/_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm +++ b/_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm @@ -12,7 +12,7 @@ /obj/effect/decal/cleanable/blood/gibs, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "bO" = ( /obj/structure/window/reinforced/spawner/directional/south, @@ -34,7 +34,7 @@ pixel_x = 5; pixel_y = 9 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "bR" = ( /obj/effect/decal/cleanable/blood/gibs/robot_debris/up, @@ -42,7 +42,7 @@ /area/ruin/space/has_grav/derelictsulaco) "bV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "cu" = ( /obj/effect/decal/cleanable/dirt, @@ -98,7 +98,7 @@ dir = 1 }, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "dM" = ( /obj/structure/alien/weeds, @@ -122,7 +122,7 @@ dir = 1; anchored = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "eZ" = ( /obj/effect/mapping_helpers/burnt_floor, @@ -156,7 +156,7 @@ /obj/structure/table/reinforced, /obj/item/paper_bin, /obj/item/pen/fountain, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "gx" = ( /obj/structure/alien/weeds, @@ -227,14 +227,14 @@ /obj/structure/window/reinforced/spawner/directional/west, /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/table/reinforced, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "ib" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/frame/computer{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "iu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -251,7 +251,7 @@ /turf/open/floor/circuit, /area/ruin/space/has_grav/derelictsulaco) "jn" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "jo" = ( /obj/machinery/door/airlock/command/glass{ @@ -277,7 +277,7 @@ "jI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "kb" = ( /obj/effect/decal/cleanable/blood/footprints{ @@ -291,7 +291,7 @@ /area/ruin/space/has_grav/derelictsulaco) "kd" = ( /obj/item/trash/flare, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "kB" = ( /obj/machinery/door/window/brigdoor/left/directional/east, @@ -329,7 +329,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "lf" = ( /obj/effect/turf_decal/siding/wideplating/dark, @@ -415,7 +415,7 @@ /obj/machinery/computer/terminal/sulaco/map, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "nn" = ( /obj/structure/alien/weeds, @@ -441,7 +441,7 @@ /obj/structure/window/reinforced/spawner/directional/north, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "ol" = ( /obj/structure/window/reinforced/spawner/directional/north, @@ -463,11 +463,11 @@ /obj/structure/barricade/sandbags, /obj/structure/cable, /obj/item/light/tube/broken, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "pv" = ( /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "pF" = ( /obj/effect/spawner/structure/window/reinforced, @@ -475,7 +475,7 @@ /area/ruin/space/has_grav/derelictsulaco) "qS" = ( /obj/item/shard, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "qY" = ( /obj/structure/window/reinforced/spawner/directional/west, @@ -491,7 +491,7 @@ "rb" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "ri" = ( /obj/item/circuitboard/machine/telecomms/processor, @@ -499,7 +499,7 @@ /area/ruin/space/has_grav/derelictsulaco) "rA" = ( /obj/machinery/fax, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "rJ" = ( /obj/structure/alien/weeds, @@ -535,7 +535,7 @@ /obj/structure/frame/computer{ anchored = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "so" = ( /obj/structure/alien/weeds, @@ -576,7 +576,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "tm" = ( /obj/effect/decal/cleanable/dirt, @@ -630,7 +630,7 @@ "uP" = ( /obj/effect/decal/cleanable/blood/footprints, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "uS" = ( /obj/structure/window/reinforced/spawner/directional/north, @@ -663,7 +663,7 @@ /area/ruin/space/has_grav/derelictsulaco) "vi" = ( /obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "vj" = ( /obj/effect/spawner/structure/window/reinforced, @@ -686,7 +686,7 @@ /obj/structure/frame/machine{ anchored = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "wd" = ( /obj/structure/alien/weeds, @@ -727,7 +727,7 @@ /obj/structure/table/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "yy" = ( /obj/structure/frame/machine{ @@ -750,7 +750,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "zf" = ( /obj/structure/alien/weeds, @@ -802,7 +802,7 @@ /obj/machinery/computer/terminal/sulaco/overwatch/main{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "Aa" = ( /obj/structure/alien/weeds, @@ -840,7 +840,7 @@ "CC" = ( /obj/effect/decal/cleanable/blood/gibs/robot_debris, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "CF" = ( /obj/effect/decal/cleanable/dirt, @@ -896,12 +896,12 @@ /obj/effect/decal/cleanable/glass, /obj/item/shard, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "EK" = ( /obj/effect/mapping_helpers/broken_floor, /obj/structure/barricade/sandbags, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "EQ" = ( /obj/structure/alien/weeds, @@ -949,7 +949,7 @@ "FP" = ( /obj/effect/decal/cleanable/blood, /obj/item/clothing/suit/armor/vest/marine/sulaco, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "FQ" = ( /obj/effect/turf_decal/tile/red{ @@ -972,7 +972,7 @@ "GL" = ( /obj/effect/mapping_helpers/burnt_floor, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "Ha" = ( /obj/structure/sign/poster/official/terragov/directional/east, @@ -1166,7 +1166,7 @@ /obj/effect/decal/cleanable/glass, /obj/structure/cable, /obj/machinery/light/floor/broken, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "LW" = ( /obj/structure/cable, @@ -1228,7 +1228,7 @@ /obj/machinery/photocopier/prebuilt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "Ne" = ( /obj/item/stack/tile/iron/smooth_half, @@ -1351,7 +1351,7 @@ /area/ruin/space/has_grav/derelictsulaco) "QP" = ( /obj/structure/barricade/sandbags, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "RA" = ( /obj/machinery/door/airlock/command/glass{ @@ -1391,7 +1391,7 @@ /obj/machinery/door/window/brigdoor/left/directional/south, /obj/effect/decal/cleanable/dirt, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "SD" = ( /obj/machinery/camera/directional/west{ @@ -1547,13 +1547,13 @@ dir = 8 }, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "WM" = ( /obj/structure/chair/office{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "WN" = ( /turf/closed/wall/r_wall, @@ -1576,7 +1576,7 @@ /obj/machinery/power/floodlight{ anchored = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "Xu" = ( /obj/structure/lattice, @@ -1614,7 +1614,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/layer2{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "Yn" = ( /obj/structure/alien/resin/wall, @@ -1665,7 +1665,7 @@ dir = 1 }, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/derelictsulaco) "ZZ" = ( /obj/structure/lattice, diff --git a/_maps/RandomRuins/SpaceRuins/film_studio.dmm b/_maps/RandomRuins/SpaceRuins/film_studio.dmm index c426b7d2bb02..815804958398 100644 --- a/_maps/RandomRuins/SpaceRuins/film_studio.dmm +++ b/_maps/RandomRuins/SpaceRuins/film_studio.dmm @@ -129,7 +129,7 @@ /area/ruin/space/has_grav/film_studio/stage) "cC" = ( /obj/machinery/door/window/right/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "cG" = ( /obj/effect/spawner/random/trash, @@ -860,7 +860,7 @@ /obj/structure/window/reinforced/spawner/directional/south, /obj/item/storage/box/papersack, /obj/structure/window/reinforced/spawner/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "qE" = ( /obj/item/storage/medkit/ancient, @@ -877,7 +877,7 @@ /obj/structure/filingcabinet/white, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/window/reinforced/spawner/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "qM" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -950,7 +950,7 @@ "sj" = ( /obj/machinery/computer/old, /obj/structure/window/reinforced/spawner/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "sm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -969,7 +969,7 @@ /obj/structure/chair/office{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "sK" = ( /obj/structure/table, @@ -1276,7 +1276,7 @@ pixel_y = 6 }, /obj/structure/window/reinforced/spawner/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "xz" = ( /obj/machinery/fax{ @@ -1317,7 +1317,7 @@ dir = 8 }, /obj/structure/window/reinforced/spawner/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "yr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -1373,7 +1373,7 @@ /obj/structure/table/reinforced, /obj/structure/window/reinforced/spawner/directional/north, /obj/structure/window/reinforced/spawner/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "zL" = ( /turf/open/floor/iron/dark, @@ -1401,7 +1401,7 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "Af" = ( /obj/item/kirbyplants/random, @@ -1422,7 +1422,7 @@ /area/ruin/space/has_grav/film_studio/dorms) "Av" = ( /obj/machinery/door/window/left/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "AB" = ( /obj/effect/turf_decal/siding/wood{ @@ -1774,7 +1774,7 @@ /obj/structure/table/reinforced, /obj/structure/window/reinforced/spawner/directional/south, /obj/item/modular_computer/laptop/preset/civilian, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "Fo" = ( /obj/structure/closet/crate{ @@ -2150,7 +2150,7 @@ /obj/item/storage/box/papersack, /obj/structure/window/reinforced/spawner/directional/west, /obj/structure/window/reinforced/spawner/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "LD" = ( /turf/open/floor/iron/white/smooth_half{ @@ -2444,7 +2444,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/film_studio/solars) "PS" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "Qe" = ( /obj/structure/table, @@ -2499,7 +2499,7 @@ /obj/machinery/light/floor, /obj/item/paper_bin, /obj/structure/window/reinforced/spawner/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "QW" = ( /obj/machinery/door/airlock/maintenance{ @@ -2600,7 +2600,7 @@ }, /obj/machinery/door/window/elevator/right/directional/east, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "SE" = ( /obj/structure/table, @@ -2625,7 +2625,7 @@ "Te" = ( /obj/structure/filingcabinet/white, /obj/structure/window/reinforced/spawner/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "Ti" = ( /obj/structure/table/reinforced, @@ -2791,7 +2791,7 @@ "VU" = ( /obj/structure/table/reinforced, /obj/machinery/door/window/left/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/ruin/space/has_grav/film_studio/starboard) "We" = ( /obj/effect/turf_decal/siding/thinplating_new/terracotta{ diff --git a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm index 1544232c33d2..cd41c968a727 100644 --- a/_maps/RandomRuins/SpaceRuins/forgottenship.dmm +++ b/_maps/RandomRuins/SpaceRuins/forgottenship.dmm @@ -205,21 +205,6 @@ "bb" = ( /turf/closed/wall/r_wall/plastitanium/syndicate, /area/ruin/space/has_grav/syndicate_forgotten_cargopod) -"bd" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/plasteel/twenty, -/obj/item/stack/sheet/mineral/plastitanium{ - amount = 50 - }, -/obj/item/stack/sheet/glass/fifty, -/obj/item/stack/rods/fifty, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "be" = ( /obj/structure/cable, /obj/machinery/door/airlock/external/ruin{ @@ -368,14 +353,6 @@ /obj/item/assembly/prox_sensor, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"bA" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/clothing/head/helmet/space/syndicate/black/engie, -/obj/item/clothing/suit/space/syndicate/black/engie, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "bC" = ( /obj/structure/closet/crate/medical, /obj/item/stack/medical/bruise_pack{ @@ -640,18 +617,6 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"cz" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/mineral/titanium{ - amount = 40 - }, -/obj/item/stack/sheet/mineral/uranium{ - amount = 15 - }, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "cA" = ( /obj/structure/table/reinforced, /obj/machinery/atmospherics/components/unary/vent_pump, @@ -850,16 +815,6 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"dd" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/language_manual/codespeak_manual/unlimited, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) -"de" = ( -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "df" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -903,28 +858,9 @@ }, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_cargopod) -"di" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/disk/surgery/forgottenship, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "dj" = ( /turf/closed/mineral/random/high_chance, /area/space/nearstation) -"dl" = ( -/obj/machinery/door/password/voice/sfc{ - password = null - }, -/obj/structure/fans/tiny, -/obj/machinery/door/airlock/grunge{ - desc = "Vault airlock preventing air from going out."; - name = "Syndicate Vault Airlock" - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "dm" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -941,19 +877,6 @@ /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, /turf/open/floor/plating, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"do" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/mineral/gold{ - amount = 30 - }, -/obj/item/stack/sheet/mineral/silver{ - amount = 30 - }, -/obj/machinery/light/directional/south, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "dp" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -981,6 +904,9 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"io" = ( +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "jN" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -992,6 +918,26 @@ /obj/structure/closet/crate/solarpanel_small, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_cargopod) +"ke" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/clothing/head/helmet/space/syndicate/black/engie, +/obj/item/clothing/suit/space/syndicate/black/engie, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) +"kB" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/mineral/titanium{ + amount = 40 + }, +/obj/item/stack/sheet/mineral/uranium{ + amount = 15 + }, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "kY" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -1004,6 +950,18 @@ /obj/effect/spawner/random/contraband/armory, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"la" = ( +/obj/machinery/door/password/voice/sfc{ + password = null + }, +/obj/structure/fans/tiny, +/obj/machinery/door/airlock/grunge{ + desc = "Vault airlock preventing air from going out."; + name = "Syndicate Vault Airlock" + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "lQ" = ( /obj/machinery/ore_silo, /obj/machinery/light/directional/west, @@ -1036,6 +994,28 @@ /obj/machinery/light/small/directional/west, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"pG" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/disk/surgery/forgottenship, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) +"ql" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/plasteel/twenty, +/obj/item/stack/sheet/mineral/plastitanium{ + amount = 50 + }, +/obj/item/stack/sheet/glass/fifty, +/obj/item/stack/rods/fifty, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "qK" = ( /obj/machinery/turretid{ control_area = "/area/ruin/space/has_grav/syndicate_forgotten_ship"; @@ -1053,6 +1033,19 @@ /obj/effect/mapping_helpers/airalarm/syndicate_access, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"sz" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/mineral/gold{ + amount = 30 + }, +/obj/item/stack/sheet/mineral/silver{ + amount = 30 + }, +/obj/machinery/light/directional/south, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "sL" = ( /obj/effect/mob_spawn/ghost_role/human/syndicatespace/captain, /obj/machinery/light/directional/west, @@ -1078,11 +1071,14 @@ /obj/structure/cable, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"vR" = ( -/obj/item/ai_module/core/full/cybersun, -/obj/structure/table/reinforced, -/turf/open/floor/mineral/plastitanium/red, -/area/ruin/space/has_grav/syndicate_forgotten_ship) +"xV" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/melee/energy/sword/saber/red, +/obj/machinery/light/directional/north, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "yf" = ( /mob/living/basic/trooper/nanotrasen/ranged/assault, /turf/open/floor/mineral/plastitanium/red, @@ -1134,14 +1130,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/syndicate_forgotten_ship) -"Il" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/melee/energy/sword/saber/red, -/obj/machinery/light/directional/north, -/turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "IP" = ( /obj/structure/cable, /obj/machinery/light/directional/west, @@ -1154,6 +1142,13 @@ /obj/machinery/light/directional/east, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"Jl" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/language_manual/codespeak_manual/unlimited, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/has_grav/powered/syndicate_forgotten_vault) "Lj" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -1173,6 +1168,11 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/syndicate_forgotten_ship) +"OS" = ( +/obj/item/ai_module/law/core/full/cybersun, +/obj/structure/table/reinforced, +/turf/open/floor/mineral/plastitanium/red, +/area/ruin/space/has_grav/syndicate_forgotten_ship) "OW" = ( /obj/structure/sign/warning/vacuum/external/directional/north, /turf/open/floor/mineral/plastitanium, @@ -1980,8 +1980,8 @@ ba ba aZ aZ -bA -bA +ke +ke aZ aZ ba @@ -2002,7 +2002,7 @@ aa aa aa bZ -vR +OS aw bZ aB @@ -2026,10 +2026,10 @@ ba ba ba aZ -bd -de -de -cz +ql +io +io +kB aZ ba aY @@ -2073,10 +2073,10 @@ ba bq ba aZ -Il -de -de -do +xV +io +io +sz aZ ba bq @@ -2120,10 +2120,10 @@ aY ba ba aZ -dd -de -de -di +Jl +io +io +pG aZ ba ba @@ -2168,7 +2168,7 @@ ba ba aZ aZ -dl +la aZ aZ aZ diff --git a/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm b/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm index dd93e77a597b..59cc531497d3 100644 --- a/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm +++ b/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm @@ -137,21 +137,6 @@ }, /turf/open/floor/carpet/orange, /area/ruin/space/has_grav/hauntedtradingpost/public/corridor) -"bq" = ( -/obj/structure/cable/layer1, -/obj/machinery/vending/sovietsoda{ - all_products_free = 0 - }, -/obj/structure/sign/poster/contraband/clown/directional/east, -/obj/machinery/duct, -/obj/effect/mapping_helpers/broken_machine, -/obj/machinery/camera/autoname/directional/east{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/catwalk_floor/iron_smooth, -/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "br" = ( /obj/structure/table/wood/fancy/orange, /obj/machinery/recharger{ @@ -728,6 +713,21 @@ /obj/structure/cable/layer1, /turf/open/floor/carpet/orange, /area/ruin/space/has_grav/hauntedtradingpost/public/corridor) +"gy" = ( +/obj/structure/table/wood, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, +/obj/machinery/duct, +/obj/item/storage/wallet/money{ + pixel_y = -5; + desc = "It can hold a few small and personal things. This one is monogrammed 'S.C.'." + }, +/obj/machinery/camera/autoname/directional/west{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/space/has_grav/hauntedtradingpost/employees) "gz" = ( /turf/closed/wall/r_wall, /area/ruin/space/has_grav/hauntedtradingpost/maint/disposals) @@ -810,19 +810,6 @@ }, /turf/open/floor/wood/large, /area/ruin/space/has_grav/hauntedtradingpost/employees/corridor) -"hf" = ( -/obj/structure/cable/layer1, -/obj/structure/sign/poster/contraband/syndicate_pistol/directional/west, -/mob/living/basic/bot/secbot{ - faction = list("Syndicate"); - bot_access_flags = 4; - baton_type = /obj/item/melee/baton/security/cattleprod; - name = "\improper Syndicate Securitron"; - desc = "A little security robot, reprogrammed by the syndicate. He looks downright surly."; - security_mode_flags = 11 - }, -/turf/open/floor/carpet/donk, -/area/ruin/space/has_grav/hauntedtradingpost/employees/breakroom) "hi" = ( /obj/effect/decal/cleanable/dirt/dust, /mob/living/basic/viscerator, @@ -1678,26 +1665,19 @@ /obj/machinery/light/warm/directional/south, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/office/meetingroom) -"oF" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/wood/large, -/area/ruin/space/has_grav/hauntedtradingpost/employees/corridor) -"oJ" = ( -/obj/structure/table/wood, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, -/obj/machinery/duct, -/obj/item/storage/wallet/money{ - pixel_y = -5; - desc = "It can hold a few small and personal things. This one is monogrammed 'S.C.'." - }, +"oE" = ( /obj/machinery/camera/autoname/directional/west{ camera_construction_state = 1; camera_enabled = 0; network = list("donk") }, -/turf/open/floor/catwalk_floor/iron_dark, -/area/ruin/space/has_grav/hauntedtradingpost/employees) +/turf/open/floor/wood, +/area/ruin/space/has_grav/hauntedtradingpost/public) +"oF" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/wood/large, +/area/ruin/space/has_grav/hauntedtradingpost/employees/corridor) "oN" = ( /obj/effect/spawner/random/trash/deluxe_garbage, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -1724,20 +1704,6 @@ }, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/employees/workstation) -"pb" = ( -/obj/structure/cable/multilayer, -/obj/structure/cable/layer1, -/obj/structure/safe/floor, -/obj/item/sticker/syndicate/apc, -/obj/item/grenade/empgrenade, -/obj/item/book/granter/crafting_recipe/donk_secret_recipe, -/obj/item/ai_module/core/full/cybersun, -/obj/item/reagent_containers/condiment/donksauce, -/mob/living/basic/cybersun_ai_core{ - donk_ai_master = 1 - }, -/turf/open/floor/plating/reinforced, -/area/ruin/space/has_grav/hauntedtradingpost/aicore) "pc" = ( /obj/structure/table/wood, /obj/item/cardpack/series_one{ @@ -2525,6 +2491,20 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood/large, /area/ruin/space/has_grav/hauntedtradingpost/employees/corridor) +"vP" = ( +/obj/structure/cable/multilayer, +/obj/structure/cable/layer1, +/obj/structure/safe/floor, +/obj/item/sticker/syndicate/apc, +/obj/item/grenade/empgrenade, +/obj/item/book/granter/crafting_recipe/donk_secret_recipe, +/obj/item/ai_module/law/core/full/cybersun, +/obj/item/reagent_containers/condiment/donksauce, +/mob/living/basic/cybersun_ai_core{ + donk_ai_master = 1 + }, +/turf/open/floor/plating/reinforced, +/area/ruin/space/has_grav/hauntedtradingpost/aicore) "vY" = ( /obj/structure/extinguisher_cabinet/directional/east, /obj/machinery/light/warm/directional/east, @@ -3074,22 +3054,6 @@ /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/space/has_grav/hauntedtradingpost/aicore) -"AQ" = ( -/obj/structure/frame/machine/secured, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, -/obj/item/circuitboard/machine/vendor, -/obj/machinery/duct, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ - pipe_color = "#FF7B00"; - color = "#FF7B00" - }, -/obj/machinery/camera/autoname/directional/south{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/catwalk_floor/iron_smooth, -/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "AU" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, @@ -3249,6 +3213,19 @@ }, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/public) +"CP" = ( +/obj/structure/cable/layer1, +/obj/structure/sign/poster/contraband/syndicate_pistol/directional/west, +/mob/living/basic/bot/secbot{ + faction = list("Syndicate"); + bot_access_flags = 4; + baton_type = /obj/item/melee/baton/security/cattleprod; + name = "\improper Syndicate Securitron"; + desc = "A little security robot, reprogrammed by the syndicate. He looks downright surly."; + security_mode_flags = 11 + }, +/turf/open/floor/carpet/donk, +/area/ruin/space/has_grav/hauntedtradingpost/employees/breakroom) "CR" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/wood/large, @@ -3995,6 +3972,19 @@ }, /turf/open/floor/carpet/donk, /area/ruin/space/has_grav/hauntedtradingpost/employees) +"IE" = ( +/obj/structure/cable/layer1, +/obj/machinery/vending/donksnack{ + all_products_free = 0 + }, +/obj/machinery/duct, +/obj/machinery/camera/autoname/directional/west{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/catwalk_floor/iron_smooth, +/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "IL" = ( /obj/structure/cable/layer1, /obj/machinery/light/red/dim/directional/west, @@ -4084,19 +4074,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/carpet/donk, /area/ruin/space/has_grav/hauntedtradingpost/employees/breakroom) -"Jx" = ( -/obj/structure/cable/layer1, -/obj/structure/frame/machine/secured, -/obj/structure/sign/poster/ripped/directional/east, -/obj/machinery/duct, -/obj/item/shard, -/obj/machinery/camera/autoname/directional/east{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/catwalk_floor/iron_smooth, -/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "JE" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ dir = 4 @@ -4179,19 +4156,6 @@ /obj/item/toy/gun, /turf/open/floor/plating, /area/ruin/space/has_grav/hauntedtradingpost/maint/toystorage) -"KH" = ( -/obj/structure/cable/layer1, -/obj/machinery/vending/donksnack{ - all_products_free = 0 - }, -/obj/machinery/duct, -/obj/machinery/camera/autoname/directional/west{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/catwalk_floor/iron_smooth, -/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "KI" = ( /obj/structure/grille/broken, /obj/effect/decal/cleanable/glass, @@ -4278,16 +4242,6 @@ /obj/effect/spawner/random/medical/minor_healing, /turf/open/floor/catwalk_floor/iron_smooth, /area/ruin/space/has_grav/hauntedtradingpost/public/corridor) -"Lz" = ( -/obj/structure/cable/layer1, -/obj/structure/table/reinforced/plastitaniumglass, -/obj/item/ai_module/supplied/protect_station{ - pixel_x = 2; - pixel_y = 5 - }, -/obj/machinery/light/red/dim/directional/east, -/turf/open/floor/catwalk_floor/iron_dark, -/area/ruin/space/has_grav/hauntedtradingpost/aicore) "LC" = ( /obj/structure/cable/layer1, /obj/machinery/door/firedoor, @@ -4437,14 +4391,6 @@ /obj/item/card/id/away/donk, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/public) -"Mr" = ( -/obj/machinery/camera/autoname/directional/west{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/wood, -/area/ruin/space/has_grav/hauntedtradingpost/public) "Mu" = ( /obj/effect/mapping_helpers/bombable_wall, /turf/closed/wall/r_wall, @@ -4576,6 +4522,16 @@ /obj/structure/cable/layer1, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/public) +"NL" = ( +/obj/structure/cable/layer1, +/obj/structure/table/reinforced/plastitaniumglass, +/obj/item/ai_module/law/supplied/protect_station{ + pixel_x = 2; + pixel_y = 5 + }, +/obj/machinery/light/red/dim/directional/east, +/turf/open/floor/catwalk_floor/iron_dark, +/area/ruin/space/has_grav/hauntedtradingpost/aicore) "NM" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/splatter, @@ -5500,6 +5456,22 @@ }, /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/space/has_grav/hauntedtradingpost/office) +"VN" = ( +/obj/structure/frame/machine/secured, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, +/obj/item/circuitboard/machine/vendor, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ + pipe_color = "#FF7B00"; + color = "#FF7B00" + }, +/obj/machinery/camera/autoname/directional/south{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/catwalk_floor/iron_smooth, +/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "VP" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/navbeacon{ @@ -5698,6 +5670,19 @@ /obj/effect/spawner/random/bureaucracy/briefcase, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/office/meetingroom) +"Xq" = ( +/obj/structure/cable/layer1, +/obj/structure/frame/machine/secured, +/obj/structure/sign/poster/ripped/directional/east, +/obj/machinery/duct, +/obj/item/shard, +/obj/machinery/camera/autoname/directional/east{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/catwalk_floor/iron_smooth, +/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "Xu" = ( /obj/item/ammo_casing/foam_dart{ pixel_x = 10; @@ -5761,6 +5746,14 @@ }, /turf/open/floor/carpet/orange, /area/ruin/space/has_grav/hauntedtradingpost/public/corridor) +"XF" = ( +/obj/machinery/camera/autoname/directional/east{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/wood, +/area/ruin/space/has_grav/hauntedtradingpost/public) "XJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /obj/effect/decal/cleanable/wrapping, @@ -5822,6 +5815,21 @@ }, /turf/open/floor/wood, /area/ruin/space/has_grav/hauntedtradingpost/office) +"Yc" = ( +/obj/structure/cable/layer1, +/obj/machinery/vending/sovietsoda{ + all_products_free = 0 + }, +/obj/structure/sign/poster/contraband/clown/directional/east, +/obj/machinery/duct, +/obj/effect/mapping_helpers/broken_machine, +/obj/machinery/camera/autoname/directional/east{ + camera_construction_state = 1; + camera_enabled = 0; + network = list("donk") + }, +/turf/open/floor/catwalk_floor/iron_smooth, +/area/ruin/space/has_grav/hauntedtradingpost/public/corridor) "Ye" = ( /obj/structure/cable/layer1, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, @@ -6018,14 +6026,6 @@ "ZU" = ( /turf/open/floor/catwalk_floor/iron_dark, /area/ruin/space/has_grav/hauntedtradingpost/maint/toolstorage) -"ZZ" = ( -/obj/machinery/camera/autoname/directional/east{ - camera_construction_state = 1; - camera_enabled = 0; - network = list("donk") - }, -/turf/open/floor/wood, -/area/ruin/space/has_grav/hauntedtradingpost/public) (1,1,1) = {" qR @@ -6407,7 +6407,7 @@ DR rI iW rg -oJ +gy ad zq lW @@ -6506,12 +6506,12 @@ tl Kr Md oO -bq +Yc UW LC Yg ip -Jx +Xq NY gn gv @@ -6598,7 +6598,7 @@ db ix nG LO -Mr +oE uT dt UO @@ -6764,7 +6764,7 @@ iv iv ot XW -pb +vP MR tl YR @@ -6785,7 +6785,7 @@ uZ Vm bC OP -AQ +VN yi Qy kx @@ -6990,7 +6990,7 @@ je Nw Db ca -hf +CP JX co nZ @@ -7003,7 +7003,7 @@ iC Ry sN dD -ZZ +XF dD HR rN @@ -7026,7 +7026,7 @@ hV vx tm Gg -Lz +NL bu cv gb @@ -7096,7 +7096,7 @@ Pk LD Yg Wu -KH +IE lT pg gv diff --git a/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm b/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm index 96b778786af6..667a5167548d 100644 --- a/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm +++ b/_maps/RandomRuins/SpaceRuins/infested_frigate.dmm @@ -12,7 +12,7 @@ }, /obj/structure/barricade/security, /obj/structure/alien/resin/membrane, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "aA" = ( /obj/structure/table_frame, @@ -77,7 +77,7 @@ /obj/effect/mob_spawn/corpse/human/syndicatecommando/lessenedgear{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "be" = ( /obj/structure/broken_flooring/plating/directional/east, @@ -100,7 +100,7 @@ }, /obj/structure/cable, /obj/machinery/suit_storage_unit/open, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "bJ" = ( /obj/structure/cable, @@ -160,7 +160,7 @@ /obj/item/radio{ pixel_x = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "cA" = ( /obj/effect/turf_decal{ @@ -268,7 +268,7 @@ dir = 1 }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "dq" = ( /obj/effect/decal/cleanable/glass, @@ -385,7 +385,7 @@ name = "STERILIZATION ORDERS"; desc = "These are particularly boring for what they entail." }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "eZ" = ( /obj/item/clothing/mask/facehugger/impregnated, @@ -576,7 +576,7 @@ icon_state = "warningline_red"; dir = 9 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "ip" = ( /obj/structure/alien/egg/burst, @@ -649,7 +649,7 @@ name = "syndicate navigation console" }, /obj/effect/decal/cleanable/glass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "kc" = ( /obj/effect/turf_decal{ @@ -825,7 +825,7 @@ /obj/structure/closet/syndicate, /obj/structure/cable, /obj/item/gun/syringe, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "nm" = ( /obj/machinery/vending/cigarette/syndicate, @@ -833,7 +833,7 @@ icon_state = "warningline_white"; dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "nG" = ( /mob/living/basic/alien{ @@ -921,7 +921,7 @@ /obj/structure/sign/warning/fire/directional/east, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/machinery/light/broken/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "pl" = ( /obj/item/shard, @@ -974,7 +974,7 @@ name = "prisoner isolation shutter"; id = "captivity" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "qC" = ( /obj/effect/turf_decal/tile/bar{ @@ -1098,7 +1098,7 @@ }, /obj/item/clothing/head/utility/welding, /obj/effect/decal/cleanable/plastic, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "rO" = ( /obj/structure/broken_flooring/pile/directional/south, @@ -1130,7 +1130,7 @@ /area/ruin/space/has_grav/infested_frigate) "sE" = ( /obj/machinery/door/airlock/hatch, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "sQ" = ( /obj/structure/cable, @@ -1190,7 +1190,7 @@ }, /obj/item/ammo_casing/spent, /obj/effect/spawner/random/entertainment/arcade, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "uy" = ( /obj/structure/cable, @@ -1202,7 +1202,7 @@ dir = 8 }, /obj/machinery/vending/medical/infested_frigate, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "uC" = ( /obj/effect/turf_decal, @@ -1224,7 +1224,7 @@ /mob/living/basic/alien{ faction = list("syndicate","xenomorph") }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "vj" = ( /obj/machinery/door/poddoor{ @@ -1256,7 +1256,7 @@ pixel_x = -8 }, /obj/structure/table/glass/plasmaglass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "vx" = ( /obj/structure/cable, @@ -1303,7 +1303,7 @@ /obj/machinery/computer/operating{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "wc" = ( /obj/structure/cable, @@ -1398,7 +1398,7 @@ /obj/item/ammo_casing/spent, /obj/item/ammo_casing/spent, /obj/effect/mob_spawn/corpse/human/doctor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "wE" = ( /obj/machinery/vending/donksofttoyvendor, @@ -1406,7 +1406,7 @@ icon_state = "warningline_white" }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "wJ" = ( /obj/effect/turf_decal{ @@ -1430,7 +1430,7 @@ /obj/structure/sign/poster/contraband/random/directional/south, /obj/structure/cable, /obj/structure/alien/weeds, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "wP" = ( /turf/closed/wall/mineral/plastitanium, @@ -1597,7 +1597,7 @@ minimum_growtime = 120000; growtime = 150000 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "yP" = ( /obj/item/storage/belt/military, @@ -1662,7 +1662,7 @@ icon_state = "warningline_white" }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Ah" = ( /obj/effect/mob_spawn/corpse/human/syndicatepilot, @@ -1808,7 +1808,7 @@ /obj/item/clothing/under/syndicate/combat, /obj/item/clothing/shoes/combat, /obj/item/clothing/shoes/combat, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "CE" = ( /obj/effect/mapping_helpers/broken_floor, @@ -1899,7 +1899,7 @@ pixel_x = 8; pixel_y = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "EQ" = ( /obj/structure/sign/poster/contraband/random/directional/north, @@ -1977,7 +1977,7 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "FJ" = ( /obj/machinery/suit_storage_unit/open, @@ -1988,7 +1988,7 @@ icon_state = "warningline_white" }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "FP" = ( /obj/effect/mapping_helpers/airlock/locked, @@ -2022,7 +2022,7 @@ /obj/item/food/burger/xeno, /obj/structure/cable, /obj/effect/spawner/random/trash, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Hc" = ( /obj/effect/decal/cleanable/blood/gibs{ @@ -2122,7 +2122,7 @@ /obj/machinery/vending/cola/black{ icon_state = "cola_black-broken" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "IN" = ( /obj/item/shard{ @@ -2165,7 +2165,7 @@ icon_state = "warningline_white" }, /obj/machinery/newscaster/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Jw" = ( /obj/effect/spawner/random/exotic/antag_gear_weak{ @@ -2178,7 +2178,7 @@ /obj/effect/spawner/random/exotic/antag_gear, /obj/structure/table/glass/plasmaglass, /obj/effect/spawner/random/contraband/permabrig_gear, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Jx" = ( /mob/living/basic/alien{ @@ -2271,7 +2271,7 @@ /obj/item/ammo_casing/spent, /obj/effect/decal/cleanable/glass, /obj/effect/decal/cleanable/plastic, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "KG" = ( /obj/structure/cable, @@ -2330,7 +2330,7 @@ /obj/effect/decal/cleanable/glass, /obj/item/ammo_casing/spent, /obj/structure/alien/weeds, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "LF" = ( /obj/structure/cable, @@ -2444,7 +2444,7 @@ dir = 1 }, /obj/effect/spawner/random/exotic/antag_gear, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "MW" = ( /obj/effect/turf_decal{ @@ -2482,7 +2482,7 @@ dir = 4 }, /obj/machinery/computer/operating, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "NX" = ( /obj/structure/table/glass, @@ -2511,7 +2511,7 @@ req_access = list("syndicate") }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Or" = ( /obj/item/stack/rods{ @@ -2687,7 +2687,7 @@ icon_state = "warningline_white"; dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "RG" = ( /obj/effect/turf_decal/tile/bar{ @@ -2746,7 +2746,7 @@ pixel_y = -1 }, /obj/structure/table/glass/plasmaglass, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "SB" = ( /obj/machinery/power/shuttle_engine/large{ @@ -2778,7 +2778,7 @@ /obj/item/ammo_casing/spent, /obj/item/ammo_casing/spent, /obj/item/ammo_casing/spent, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "SM" = ( /obj/structure/cable, @@ -2807,7 +2807,7 @@ dir = 4 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "SX" = ( /obj/effect/decal/cleanable/dirt, @@ -2863,7 +2863,7 @@ icon_state = "gib6-old"; dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Ux" = ( /obj/effect/turf_decal/tile/bar{ @@ -2932,7 +2932,7 @@ icon_state = "warningline_white" }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "VR" = ( /obj/item/tank/internals/oxygen/empty, @@ -3075,7 +3075,7 @@ icon_state = "warningline_white" }, /obj/item/kirbyplants/random, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "YW" = ( /obj/effect/turf_decal/tile/bar{ @@ -3106,7 +3106,7 @@ dir = 1 }, /obj/effect/spawner/random/medical/minor_healing, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/infested_frigate) "Ze" = ( /obj/item/shard{ diff --git a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm index 8bda3cde936f..3093cdb7f38c 100644 --- a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm @@ -596,6 +596,15 @@ /obj/item/ammo_casing/c45/spent, /turf/open/indestructible/white, /area/ruin/space/has_grav/powered/biooutpost) +"ls" = ( +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/structure/flora/bush/fullgrass/style_random, +/obj/item/food/grown/carrotlike/carrot{ + preserved_food = 1 + }, +/turf/open/floor/grass, +/area/ruin/space/has_grav/powered/biooutpost) "lJ" = ( /obj/machinery/door/airlock/science/glass, /obj/effect/mapping_helpers/airlock/welded, @@ -1864,15 +1873,6 @@ /obj/item/grown/bananapeel, /turf/open/floor/iron/dark/textured_large, /area/ruin/space/has_grav/powered/biooutpost/vault) -"JF" = ( -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/flora/bush/fullgrass/style_random, -/obj/item/food/grown/carrotlike/carrot{ - preserved_food = 1 - }, -/turf/open/floor/grass, -/area/ruin/space/has_grav/powered/biooutpost) "Ka" = ( /mob/living/basic/living_floor/white, /obj/effect/decal/cleanable/dirt, @@ -1908,6 +1908,14 @@ /obj/effect/decal/cleanable/blood/splatter, /turf/open/indestructible/white, /area/ruin/space/has_grav/powered/biooutpost) +"KB" = ( +/obj/structure/table/wood/fancy/orange, +/obj/item/ai_module/law/core/full/ten_commandments, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/turf/open/floor/carpet, +/area/ruin/space/has_grav/powered/biooutpost) "KC" = ( /obj/machinery/puzzle/button/directional/north{ id = "md_toeng"; @@ -2512,14 +2520,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/indestructible/dark, /area/ruin/space/has_grav/powered/biooutpost) -"Xm" = ( -/obj/structure/table/wood/fancy/orange, -/obj/item/ai_module/core/full/ten_commandments, -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/turf/open/floor/carpet, -/area/ruin/space/has_grav/powered/biooutpost) "Xo" = ( /obj/structure/showcase/horrific_experiment, /turf/open/indestructible/plating, @@ -3080,7 +3080,7 @@ Ec Ec Jh Kc -JF +ls DJ kl qs @@ -3188,7 +3188,7 @@ Jh Jh Jh eT -Xm +KB Tw tg Jh diff --git a/_maps/RandomRuins/SpaceRuins/oldstation.dmm b/_maps/RandomRuins/SpaceRuins/oldstation.dmm index 85a4b3f82bb1..2a24e4b989a7 100644 --- a/_maps/RandomRuins/SpaceRuins/oldstation.dmm +++ b/_maps/RandomRuins/SpaceRuins/oldstation.dmm @@ -517,15 +517,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating/airless, /area/ruin/space/ancientstation/beta/supermatter) -"cz" = ( -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/table, -/obj/machinery/door/window/brigdoor/right/directional/east, -/obj/item/circuitboard/computer/borgupload/no_lock, -/turf/open/floor/iron/dark, -/area/ruin/space/ancientstation/delta/ai) "cA" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -1235,6 +1226,14 @@ /obj/machinery/light/small/broken/directional/east, /turf/open/floor/iron/dark, /area/ruin/space/ancientstation/delta/ai) +"fC" = ( +/obj/effect/decal/cleanable/blood/gibs/robot_debris, +/obj/structure/alien/weeds, +/obj/structure/alien/egg/burst, +/obj/item/stock_parts/subspace/transmitter, +/obj/item/circuitboard/machine/ai_law_rack, +/turf/open/floor/iron/dark, +/area/ruin/space/ancientstation/delta/ai) "fE" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -2669,6 +2668,12 @@ /obj/effect/decal/cleanable/blood/tracks, /turf/open/floor/iron/dark, /area/ruin/space/ancientstation/delta/ai) +"ma" = ( +/obj/machinery/shower/directional/west{ + has_water_reclaimer = 0 + }, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/ruin/space/ancientstation/delta/biolab) "mb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -3356,12 +3361,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible/layer2, /turf/open/floor/iron, /area/ruin/space/ancientstation/beta/atmos) -"pB" = ( -/obj/effect/decal/cleanable/blood/oil, -/obj/structure/alien/weeds, -/obj/machinery/light/small/broken/directional/west, -/turf/open/floor/iron/dark, -/area/ruin/space/ancientstation/delta/ai) "pD" = ( /obj/effect/mapping_helpers/broken_floor, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2{ @@ -5101,12 +5100,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/engine/airless, /area/ruin/space/ancientstation/beta/supermatter) -"BR" = ( -/obj/machinery/shower/directional/west{ - has_water_reclaimer = 0 - }, -/turf/open/floor/pod/dark, -/area/ruin/space/ancientstation/delta/biolab) "BS" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/east, @@ -5273,6 +5266,20 @@ /obj/structure/cable, /turf/open/floor/iron, /area/ruin/space/ancientstation/delta/hall) +"Dd" = ( +/obj/effect/decal/cleanable/blood/oil, +/obj/structure/alien/weeds, +/obj/machinery/light/small/broken/directional/west, +/obj/item/stack/cable_coil{ + pixel_y = -2; + pixel_x = -2 + }, +/obj/item/stock_parts/subspace/amplifier{ + pixel_x = -8; + pixel_y = 4 + }, +/turf/open/floor/iron/dark, +/area/ruin/space/ancientstation/delta/ai) "Df" = ( /obj/structure/grille/broken, /turf/open/floor/plating/airless, @@ -5949,12 +5956,6 @@ dir = 8 }, /area/ruin/space/ancientstation/delta/biolab) -"HP" = ( -/obj/effect/decal/cleanable/blood/gibs/robot_debris, -/obj/structure/alien/weeds, -/obj/structure/alien/egg/burst, -/turf/open/floor/iron/dark, -/area/ruin/space/ancientstation/delta/ai) "HQ" = ( /obj/structure/transit_tube{ dir = 4 @@ -6798,15 +6799,6 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/iron/white/textured, /area/ruin/space/ancientstation/delta/proto) -"MC" = ( -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/table, -/obj/machinery/door/window/brigdoor/right/directional/east, -/obj/item/circuitboard/computer/aiupload/no_lock, -/turf/open/floor/iron/dark, -/area/ruin/space/ancientstation/delta/ai) "MH" = ( /obj/structure/girder/displaced, /turf/open/floor/plating/airless, @@ -6845,6 +6837,22 @@ }, /turf/open/floor/iron, /area/ruin/space/ancientstation/delta/rnd) +"MO" = ( +/obj/structure/window/reinforced/spawner/directional/west, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/structure/table, +/obj/machinery/door/window/brigdoor/right/directional/east, +/obj/item/ai_module/law/core/full/damaged, +/obj/item/ai_module/law/core/full/maintain{ + pixel_y = 3; + pixel_x = 2 + }, +/obj/item/ai_module/law/core/freeformcore{ + pixel_y = -4 + }, +/turf/open/floor/iron/dark, +/area/ruin/space/ancientstation/delta/ai) "MP" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/filingcabinet/chestdrawer{ @@ -7760,6 +7768,25 @@ dir = 4 }, /area/ruin/space/ancientstation/delta/biolab) +"Tl" = ( +/obj/structure/window/reinforced/spawner/directional/west, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/structure/table, +/obj/machinery/door/window/brigdoor/right/directional/east, +/obj/item/ai_module/law/core/full/asimov{ + pixel_x = -3; + pixel_y = 2 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = -4; + pixel_x = -4 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = -2 + }, +/turf/open/floor/iron/dark, +/area/ruin/space/ancientstation/delta/ai) "Tm" = ( /obj/machinery/power/port_gen/pacman/super{ name = "\improper emergency power generator" @@ -14376,8 +14403,8 @@ Wb NS dT nG -MC -cz +Tl +MO ad ad aa @@ -15229,8 +15256,8 @@ aa aa aa ad -HP -pB +fC +Dd lq ag gk @@ -15314,7 +15341,7 @@ wK UH ae GX -BR +ma AT rp vy diff --git a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm index 663ead0607b4..eeceafe0790e 100644 --- a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm @@ -642,6 +642,16 @@ /obj/structure/lattice, /turf/template_noop, /area/space/nearstation) +"gS" = ( +/obj/structure/rack, +/obj/item/circuitboard/machine/autolathe{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/circuitboard/machine/protolathe/offstation, +/obj/item/circuitboard/machine/ai_law_rack, +/turf/open/floor/circuit/red/off, +/area/ruin/space/ks13/ai/corridor) "gU" = ( /obj/item/kirbyplants/random/dead, /obj/effect/decal/cleanable/dirt, @@ -838,6 +848,19 @@ /obj/structure/cable, /turf/open/floor/plating/airless, /area/ruin/space/ks13/hallway/aft) +"kf" = ( +/obj/structure/rack, +/obj/item/circuitboard/machine/circuit_imprinter/offstation{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/ai_module/law/core/full/drone, +/obj/item/circuitboard/machine/ai_law_rack/core{ + pixel_y = 5; + pixel_x = -4 + }, +/turf/open/floor/circuit/red/off, +/area/ruin/space/ks13/ai/corridor) "kk" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -4042,15 +4065,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating/airless, /area/ruin/space/ks13/engineering/supermatter) -"DK" = ( -/obj/structure/rack, -/obj/item/circuitboard/machine/circuit_imprinter/offstation{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/ai_module/core/full/drone, -/turf/open/floor/circuit/red/off, -/area/ruin/space/ks13/ai/corridor) "DL" = ( /obj/structure/table, /obj/item/stock_parts/power_store/cell/high{ @@ -5587,17 +5601,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/iron/airless, /area/ruin/space/ks13/service/kitchen) -"LL" = ( -/obj/structure/rack, -/obj/item/circuitboard/machine/autolathe{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/circuitboard/machine/protolathe/offstation, -/obj/item/circuitboard/computer/borgupload/no_lock, -/obj/item/circuitboard/computer/aiupload/no_lock, -/turf/open/floor/circuit/red/off, -/area/ruin/space/ks13/ai/corridor) "LM" = ( /obj/machinery/light/small/directional/north, /obj/structure/cable, @@ -13335,7 +13338,7 @@ Rm lq lq Yc -LL +gS Yc aa aa @@ -13561,7 +13564,7 @@ Jy lq YC Yc -DK +kf Yc aa aa diff --git a/_maps/RandomRuins/SpaceRuins/vaporwave.dmm b/_maps/RandomRuins/SpaceRuins/vaporwave.dmm index 830bd7508ec9..2e4b00ff7f2d 100644 --- a/_maps/RandomRuins/SpaceRuins/vaporwave.dmm +++ b/_maps/RandomRuins/SpaceRuins/vaporwave.dmm @@ -102,7 +102,7 @@ "z" = ( /obj/structure/fans/tiny, /obj/machinery/door/airlock/hatch, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/ruin/space/has_grav/powered/aesthetic) "A" = ( /obj/effect/turf_decal/sand, diff --git a/_maps/RandomZLevels/TheBeach.dmm b/_maps/RandomZLevels/TheBeach.dmm index 3b71facaec68..c0ac8f3f84fc 100644 --- a/_maps/RandomZLevels/TheBeach.dmm +++ b/_maps/RandomZLevels/TheBeach.dmm @@ -3842,7 +3842,7 @@ density = 1 }, /obj/effect/decal/fakelattice, -/turf/open/floor/pod/light{ +/turf/open/floor/mineral/plastitanium/pod/light{ density = 1 }, /area/awaymission/beach) diff --git a/_maps/RandomZLevels/heretic.dmm b/_maps/RandomZLevels/heretic.dmm index 619ff9c739f5..c64a0abbb0e7 100644 --- a/_maps/RandomZLevels/heretic.dmm +++ b/_maps/RandomZLevels/heretic.dmm @@ -4,14 +4,14 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "af" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "ah" = ( @@ -32,7 +32,7 @@ "al" = ( /obj/item/radio/intercom/mi13/directional/north, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "am" = ( @@ -110,12 +110,12 @@ "aB" = ( /obj/item/kirbyplants/organic/plant10, /obj/effect/turf_decal/tile/green, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "aC" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "aD" = ( @@ -139,7 +139,7 @@ /area/awaymission/caves/heretic_laboratory) "aK" = ( /obj/structure/chair/comfy/shuttle, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "aL" = ( /obj/structure/fluff/tram_rail{ @@ -175,7 +175,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "aR" = ( @@ -210,21 +210,21 @@ /obj/machinery/computer/records/security{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "bc" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "be" = ( /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "bf" = ( @@ -262,14 +262,14 @@ /obj/structure/bed/double{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "bt" = ( /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "bu" = ( @@ -282,7 +282,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "by" = ( @@ -317,7 +317,7 @@ /area/awaymission/caves/heretic_laboratory) "bJ" = ( /obj/machinery/light/directional/north, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "bK" = ( @@ -379,7 +379,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/blue/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ck" = ( @@ -465,7 +465,7 @@ /area/awaymission/caves/heretic_laboratory) "cK" = ( /obj/effect/turf_decal/tile/bar/anticorner/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "cL" = ( @@ -476,7 +476,7 @@ /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "cO" = ( @@ -516,7 +516,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "cX" = ( @@ -531,7 +531,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "dd" = ( @@ -555,7 +555,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "dk" = ( @@ -585,12 +585,12 @@ dir = 1 }, /obj/effect/turf_decal/stripes/line, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "dr" = ( /obj/structure/table, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "du" = ( @@ -604,7 +604,7 @@ /area/awaymission/caves/heretic_laboratory) "dw" = ( /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "dy" = ( @@ -633,7 +633,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "dF" = ( @@ -664,7 +664,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "dO" = ( @@ -707,7 +707,7 @@ /area/awaymission/beach/heretic) "dY" = ( /obj/structure/bookcase/random, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "eg" = ( @@ -831,7 +831,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "eQ" = ( @@ -846,7 +846,7 @@ }, /obj/effect/turf_decal/stripes/corner, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "eT" = ( @@ -859,7 +859,7 @@ "eW" = ( /obj/structure/table, /obj/item/ammo_box/magazine/m7mm, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "eY" = ( @@ -916,7 +916,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "fo" = ( @@ -936,7 +936,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "fu" = ( @@ -1052,7 +1052,7 @@ /obj/effect/decal/cleanable/blood/tracks{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "fP" = ( @@ -1081,7 +1081,7 @@ /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "fW" = ( @@ -1143,7 +1143,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "gi" = ( @@ -1243,7 +1243,7 @@ "gC" = ( /obj/structure/table, /obj/machinery/recharger, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "gF" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, @@ -1260,7 +1260,7 @@ dir = 1 }, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "gK" = ( @@ -1279,7 +1279,7 @@ id = "crematoriumchapel"; dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory_clean) "gQ" = ( @@ -1296,14 +1296,14 @@ dir = 1 }, /obj/structure/door_assembly/door_assembly_highsecurity, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "gT" = ( /obj/machinery/computer/crew{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "gU" = ( /obj/machinery/conveyor/auto{ @@ -1323,7 +1323,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "gY" = ( @@ -1485,7 +1485,7 @@ /obj/structure/chair/comfy/shuttle{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "hM" = ( /obj/machinery/door/airlock{ @@ -1566,7 +1566,7 @@ /obj/effect/turf_decal/tile/dark{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "ij" = ( @@ -1641,7 +1641,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/dark_green/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "iF" = ( @@ -1695,7 +1695,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "iO" = ( @@ -1711,7 +1711,7 @@ "iQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/catwalk_floor/iron_dark, /area/awaymission/caves/heretic_laboratory) "iR" = ( @@ -1734,7 +1734,7 @@ pixel_x = 17; pixel_y = 0 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "iW" = ( @@ -1765,7 +1765,7 @@ /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "jc" = ( @@ -1775,7 +1775,7 @@ /obj/effect/turf_decal/tile/dark/half{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "je" = ( @@ -1877,7 +1877,7 @@ "jO" = ( /obj/machinery/vending/medical, /obj/effect/turf_decal/tile/green/anticorner/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "jP" = ( @@ -1913,7 +1913,7 @@ /obj/effect/turf_decal/tile/dark/half{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "jX" = ( @@ -2066,7 +2066,7 @@ "kO" = ( /obj/structure/table, /obj/item/fake_items/l6_saw, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "kQ" = ( @@ -2144,14 +2144,14 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "lt" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/light/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "lu" = ( @@ -2166,7 +2166,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "lz" = ( @@ -2209,7 +2209,7 @@ "lH" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, /obj/effect/mapping_helpers/damaged_window, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "lJ" = ( @@ -2219,14 +2219,14 @@ /obj/effect/turf_decal/tile/dark{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "lK" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "lL" = ( @@ -2241,7 +2241,7 @@ "lV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "lW" = ( @@ -2267,7 +2267,7 @@ /obj/structure/bed/double, /obj/effect/spawner/random/bedsheet/double, /obj/effect/turf_decal/tile/bar/anticorner/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "lZ" = ( @@ -2388,7 +2388,7 @@ /area/awaymission/caves/heretic_laboratory) "mJ" = ( /obj/machinery/suit_storage_unit/open, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "mL" = ( @@ -2399,7 +2399,7 @@ /obj/effect/turf_decal/tile/bar{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "mM" = ( @@ -2458,7 +2458,7 @@ /obj/structure/table/reinforced, /obj/item/storage/medkit/toxin, /obj/item/storage/medkit/toxin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "na" = ( /obj/structure/flora/bush/jungle/a/style_random, @@ -2492,7 +2492,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "nj" = ( @@ -2538,7 +2538,7 @@ dir = 1 }, /obj/item/radio/intercom/mi13/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "ny" = ( @@ -2552,7 +2552,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "nD" = ( @@ -2627,7 +2627,7 @@ /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "nR" = ( @@ -2730,7 +2730,7 @@ /obj/machinery/light/floor, /obj/machinery/light/directional/east, /obj/structure/bed/double, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "os" = ( @@ -2755,7 +2755,7 @@ /obj/structure/rack, /obj/item/gun/energy/disabler/smg, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "oC" = ( @@ -2766,7 +2766,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "oF" = ( @@ -2788,7 +2788,7 @@ /obj/effect/turf_decal/tile/dark_green/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "oN" = ( @@ -2813,7 +2813,7 @@ "oQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "oS" = ( @@ -2961,7 +2961,7 @@ /obj/effect/turf_decal/tile/dark_red/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "pv" = ( @@ -3024,7 +3024,7 @@ /obj/structure/table/reinforced, /obj/item/storage/medkit, /obj/item/storage/medkit, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "pH" = ( /obj/structure/fluff/tram_rail/anchor, @@ -3034,12 +3034,12 @@ /area/awaymission/caves/heretic_laboratory) "pJ" = ( /mob/living/basic/bot/cleanbot, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "pL" = ( /obj/machinery/light/directional/south, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "pN" = ( @@ -3079,7 +3079,7 @@ /area/awaymission/caves/heretic_laboratory_clean) "pW" = ( /obj/structure/table, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "pX" = ( @@ -3100,7 +3100,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "qc" = ( @@ -3110,13 +3110,13 @@ /obj/effect/turf_decal/tile/dark_green/half{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "qe" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/effect/spawner/random/heretic_gateway, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "qg" = ( @@ -3236,7 +3236,7 @@ "qL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "qM" = ( @@ -3280,7 +3280,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "qW" = ( @@ -3303,7 +3303,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "rc" = ( @@ -3315,7 +3315,7 @@ /area/awaymission/beach/heretic) "rf" = ( /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "rh" = ( @@ -3364,13 +3364,13 @@ /obj/structure/bed/double, /obj/effect/spawner/random/bedsheet/double, /obj/effect/turf_decal/tile/dark_green/anticorner/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ry" = ( /obj/structure/table, /obj/item/book/bible, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "rA" = ( @@ -3383,7 +3383,7 @@ /obj/structure/table/reinforced, /obj/item/storage/medkit/brute, /obj/item/storage/medkit/brute, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "rC" = ( /obj/structure/chair/wood{ @@ -3498,7 +3498,7 @@ /turf/open/floor/black, /area/awaymission/caves/heretic_laboratory_clean) "se" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "sf" = ( /obj/machinery/vending/donksofttoyvendor, @@ -3532,7 +3532,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "sp" = ( @@ -3657,7 +3657,7 @@ /obj/effect/turf_decal/tile/dark_green/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "sZ" = ( @@ -3665,7 +3665,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ta" = ( @@ -3710,7 +3710,7 @@ /area/awaymission/beach/heretic) "tl" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "tm" = ( @@ -3733,7 +3733,7 @@ /area/awaymission/beach/heretic) "ts" = ( /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "ty" = ( @@ -3800,7 +3800,7 @@ /turf/open/water/beach, /area/awaymission/beach/heretic) "tS" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "tT" = ( @@ -3834,7 +3834,7 @@ dir = 4 }, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "uc" = ( @@ -3873,7 +3873,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ul" = ( @@ -4003,7 +4003,7 @@ "uQ" = ( /obj/machinery/light/directional/south, /obj/effect/turf_decal/tile/dark_green/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "uR" = ( @@ -4061,7 +4061,7 @@ /area/awaymission/beach/heretic) "vf" = ( /obj/structure/closet/secure_closet/personal/cabinet, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "vj" = ( @@ -4070,7 +4070,7 @@ /turf/open/floor/iron/white, /area/awaymission/caves/heretic_laboratory) "vk" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory_clean) "vm" = ( @@ -4133,7 +4133,7 @@ pixel_y = 4 }, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "vw" = ( @@ -4292,7 +4292,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ws" = ( @@ -4329,7 +4329,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "wz" = ( @@ -4351,7 +4351,7 @@ /obj/effect/turf_decal/tile/purple/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "wE" = ( @@ -4364,7 +4364,7 @@ /area/awaymission/caves/heretic_laboratory) "wF" = ( /obj/machinery/suit_storage_unit, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "wH" = ( /obj/structure/table/glass, @@ -4386,7 +4386,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "wJ" = ( @@ -4463,7 +4463,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "wY" = ( @@ -4504,7 +4504,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "xd" = ( @@ -4527,7 +4527,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "xk" = ( @@ -4549,7 +4549,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "xn" = ( @@ -4649,7 +4649,7 @@ /obj/structure/bed/double{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "xJ" = ( @@ -4688,7 +4688,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "xQ" = ( @@ -4703,7 +4703,7 @@ "xT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "xW" = ( @@ -4740,7 +4740,7 @@ /obj/item/ammo_box/magazine/lahtimagazine{ anchored = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "ye" = ( @@ -4766,7 +4766,7 @@ "yk" = ( /obj/structure/curtain/cloth/fancy/mechanical/start_closed, /obj/effect/spawner/structure/window/reinforced/shuttle, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "yo" = ( /obj/structure/disposalpipe/segment, @@ -4845,7 +4845,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "yJ" = ( @@ -4875,7 +4875,7 @@ dir = 8 }, /obj/item/radio/intercom/mi13/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "yN" = ( @@ -4930,7 +4930,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "zb" = ( @@ -4995,7 +4995,7 @@ /area/awaymission/beach/heretic) "zE" = ( /obj/structure/bookcase/random, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "zF" = ( @@ -5167,7 +5167,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "AD" = ( @@ -5177,7 +5177,7 @@ "AG" = ( /obj/structure/bookcase/random, /obj/machinery/light/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "AI" = ( @@ -5187,7 +5187,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "AJ" = ( @@ -5231,7 +5231,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "AR" = ( @@ -5270,7 +5270,7 @@ /area/awaymission/caves/heretic_laboratory) "Be" = ( /obj/machinery/gateway, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "Bf" = ( /obj/item/clothing/head/collectable/welding, @@ -5343,7 +5343,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "By" = ( @@ -5375,7 +5375,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "BS" = ( @@ -5383,7 +5383,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "BU" = ( @@ -5391,7 +5391,7 @@ /turf/open/floor/engine, /area/awaymission/caves/heretic_laboratory) "BV" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "BW" = ( /obj/machinery/door/airlock/highsecurity{ @@ -5439,7 +5439,7 @@ /area/awaymission/caves/heretic_laboratory) "Cm" = ( /mob/living/basic/heretic_summon/stalker, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Cn" = ( @@ -5482,7 +5482,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/dark_green/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Cy" = ( @@ -5528,7 +5528,7 @@ /area/awaymission/beach/heretic) "CG" = ( /obj/effect/turf_decal/tile/yellow/anticorner/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "CI" = ( @@ -5561,7 +5561,7 @@ /turf/open/floor/asphalt, /area/awaymission/beach/heretic) "CT" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "CU" = ( @@ -5583,7 +5583,7 @@ /area/awaymission/beach/heretic) "CX" = ( /obj/machinery/light/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "CZ" = ( @@ -5596,7 +5596,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Dc" = ( @@ -5617,7 +5617,7 @@ /area/awaymission/caves/heretic_laboratory) "Di" = ( /obj/structure/closet/secure_closet/personal/cabinet, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "Dj" = ( @@ -5653,7 +5653,7 @@ /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Dt" = ( @@ -5694,20 +5694,20 @@ dir = 8 }, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "DC" = ( /obj/structure/bookcase/random, /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "DG" = ( /obj/effect/turf_decal/tile/purple/half{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "DJ" = ( @@ -5748,7 +5748,7 @@ /obj/effect/decal/cleanable/blood/tracks{ dir = 6 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/catwalk_floor/iron_dark, /area/awaymission/caves/heretic_laboratory) "DX" = ( @@ -5765,7 +5765,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "DZ" = ( @@ -5787,7 +5787,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "Ec" = ( @@ -5831,7 +5831,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Es" = ( @@ -5975,7 +5975,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Fa" = ( @@ -6032,7 +6032,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "Fn" = ( @@ -6040,7 +6040,7 @@ /obj/effect/turf_decal/tile/green{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Fr" = ( @@ -6088,13 +6088,13 @@ /obj/effect/turf_decal/tile/dark_green/anticorner/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "FC" = ( /obj/machinery/light/directional/south, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "FD" = ( @@ -6102,7 +6102,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "FF" = ( @@ -6118,7 +6118,7 @@ /obj/machinery/light/directional/south, /obj/effect/turf_decal/stripes/corner, /obj/effect/turf_decal/tile/bar, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "FM" = ( @@ -6146,7 +6146,7 @@ "FR" = ( /obj/machinery/recharge_station, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "FS" = ( @@ -6186,7 +6186,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Ga" = ( @@ -6216,7 +6216,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "Gg" = ( @@ -6256,7 +6256,7 @@ /area/awaymission/beach/heretic) "Gp" = ( /mob/living/basic/bot/medbot/mysterious, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "Gq" = ( @@ -6272,7 +6272,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Gu" = ( @@ -6303,7 +6303,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Gz" = ( @@ -6321,17 +6321,17 @@ /area/awaymission/caves/heretic_laboratory_clean) "GD" = ( /obj/structure/rack, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "GE" = ( /obj/machinery/light/directional/east, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "GF" = ( /obj/structure/dresser, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "GH" = ( @@ -6405,12 +6405,12 @@ /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "GZ" = ( /obj/structure/rack, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "Hb" = ( @@ -6420,7 +6420,7 @@ /obj/structure/table, /obj/machinery/light/directional/east, /obj/item/ammo_box/magazine/m7mm, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "Hf" = ( @@ -6487,7 +6487,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Hr" = ( @@ -6543,7 +6543,7 @@ /turf/open/floor/plating, /area/awaymission/beach/heretic) "HC" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/catwalk_floor/iron_dark, /area/awaymission/caves/heretic_laboratory) "HF" = ( @@ -6559,7 +6559,7 @@ /area/awaymission/beach/heretic) "HJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "HL" = ( @@ -6626,7 +6626,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory) "Ib" = ( @@ -6681,7 +6681,7 @@ /area/awaymission/beach/heretic) "Ik" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Il" = ( @@ -6699,7 +6699,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Io" = ( @@ -6760,7 +6760,7 @@ /obj/effect/turf_decal/tile/dark_green/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "IF" = ( @@ -6774,7 +6774,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/dark_green/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "II" = ( @@ -6832,7 +6832,7 @@ /area/awaymission/caves/heretic_laboratory) "IZ" = ( /obj/effect/turf_decal/tile/green/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Ja" = ( @@ -6846,7 +6846,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Jf" = ( @@ -6880,7 +6880,7 @@ /obj/effect/turf_decal/tile/bar{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Jp" = ( @@ -6908,7 +6908,7 @@ /obj/structure/chair/comfy/shuttle{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "Jy" = ( /obj/effect/mob_spawn/corpse/human/old_pirate_captain, @@ -6919,7 +6919,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "JA" = ( @@ -7000,12 +7000,12 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "JR" = ( /obj/effect/turf_decal/tile/dark_green/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "JS" = ( @@ -7042,7 +7042,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "JY" = ( @@ -7055,7 +7055,7 @@ /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory_clean) "Kb" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "Kd" = ( @@ -7092,7 +7092,7 @@ /obj/item/storage/toolbox/emergency, /obj/item/clothing/head/utility/welding, /obj/item/clothing/head/utility/welding, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "Km" = ( /obj/machinery/light/directional/east, @@ -7127,7 +7127,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Ku" = ( @@ -7237,7 +7237,7 @@ /area/awaymission/caves/heretic_laboratory) "Le" = ( /obj/structure/table, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "Lf" = ( @@ -7258,12 +7258,12 @@ }, /obj/item/kirbyplants/random, /obj/effect/turf_decal/stripes/corner, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Ln" = ( /obj/machinery/door/airlock/titanium, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "Lo" = ( /obj/item/paper_bin, @@ -7279,7 +7279,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/mapping_helpers/airlock/locked, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Lv" = ( @@ -7304,7 +7304,7 @@ /obj/effect/turf_decal/tile/dark_green/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "LC" = ( @@ -7350,7 +7350,7 @@ pixel_y = 6 }, /obj/structure/bed/double, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "Ma" = ( @@ -7400,7 +7400,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Ml" = ( @@ -7409,7 +7409,7 @@ /obj/effect/decal/cleanable/blood/tracks{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/catwalk_floor/iron_dark, /area/awaymission/caves/heretic_laboratory) "Mn" = ( @@ -7502,7 +7502,7 @@ "ML" = ( /obj/structure/bookcase/random, /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "MP" = ( @@ -7522,7 +7522,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "MT" = ( @@ -7540,7 +7540,7 @@ /obj/effect/turf_decal/tile/purple/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "MX" = ( @@ -7551,14 +7551,14 @@ /obj/effect/turf_decal/tile/bar{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "MY" = ( /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Na" = ( @@ -7569,7 +7569,7 @@ /obj/machinery/door/airlock/security{ name = "Armory" }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Nc" = ( @@ -7589,7 +7589,7 @@ /area/awaymission/beach/heretic) "Nj" = ( /obj/machinery/light/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Nm" = ( @@ -7648,7 +7648,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "NB" = ( @@ -7675,7 +7675,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /mob/living/basic/migo, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "NO" = ( @@ -7772,7 +7772,7 @@ dir = 8 }, /obj/structure/sign/poster/contraband/free_tonto/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Oq" = ( @@ -7787,7 +7787,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Ow" = ( @@ -7798,7 +7798,7 @@ /obj/effect/turf_decal/tile/dark_green/half{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Oz" = ( @@ -7868,7 +7868,7 @@ "OP" = ( /obj/machinery/light/directional/north, /obj/structure/chair/comfy/shuttle, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/beach/heretic) "OQ" = ( /obj/machinery/vending/boozeomat, @@ -7900,7 +7900,7 @@ /obj/effect/turf_decal/tile/dark/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Pa" = ( @@ -7928,18 +7928,18 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Pc" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/catwalk_floor/iron_dark, /area/awaymission/caves/heretic_laboratory) "Ph" = ( /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "Pj" = ( /obj/machinery/light/directional/south, @@ -7958,7 +7958,7 @@ /area/awaymission/caves/heretic_laboratory_clean) "Pq" = ( /obj/machinery/vending/sustenance, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Pw" = ( @@ -8000,7 +8000,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /mob/living/basic/faithless, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "PF" = ( @@ -8026,13 +8026,13 @@ "PK" = ( /obj/machinery/door/airlock/titanium, /obj/effect/mapping_helpers/airlock/locked, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/beach/heretic) "PL" = ( /obj/structure/window/spawner/directional/east, /obj/structure/window/spawner/directional/west, /obj/structure/flora/bush/jungle/c/style_random, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/misc/grass/jungle, /area/awaymission/caves/heretic_laboratory) "PN" = ( @@ -8140,7 +8140,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/dark_green/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Qn" = ( @@ -8165,7 +8165,7 @@ /obj/effect/turf_decal/tile/dark_red/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Qx" = ( @@ -8201,7 +8201,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "QF" = ( @@ -8235,7 +8235,7 @@ /obj/effect/turf_decal/tile/purple/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "QM" = ( @@ -8346,7 +8346,7 @@ /area/awaymission/caves/heretic_laboratory) "Rv" = ( /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Rw" = ( @@ -8376,7 +8376,7 @@ /obj/effect/turf_decal/tile/bar/anticorner/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "RE" = ( @@ -8418,7 +8418,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "RR" = ( @@ -8469,7 +8469,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Sb" = ( @@ -8528,7 +8528,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /obj/structure/sign/poster/contraband/blood_geometer/directional/north, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) @@ -8551,7 +8551,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/dark_green/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Su" = ( @@ -8582,7 +8582,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "SI" = ( @@ -8649,7 +8649,7 @@ /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Td" = ( @@ -8672,7 +8672,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Ti" = ( @@ -8724,12 +8724,12 @@ /area/awaymission/caves/heretic_laboratory) "Ts" = ( /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "TA" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "TB" = ( @@ -8743,7 +8743,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/bar/fourcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "TF" = ( @@ -8753,7 +8753,7 @@ /area/awaymission/caves/heretic_laboratory) "TG" = ( /obj/machinery/light/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "TH" = ( @@ -8789,7 +8789,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "TN" = ( @@ -8826,7 +8826,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Ua" = ( @@ -8854,7 +8854,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Uj" = ( @@ -8877,13 +8877,13 @@ /area/awaymission/caves/heretic_laboratory) "Uo" = ( /obj/machinery/light/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory_clean) "Up" = ( /obj/machinery/vending/security, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Uq" = ( @@ -8904,7 +8904,7 @@ /obj/item/kirbyplants/fern, /obj/machinery/light/directional/south, /obj/effect/turf_decal/tile/green/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "UA" = ( @@ -8933,7 +8933,7 @@ /obj/structure/rack, /obj/effect/spawner/random/armory/disablers, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "UF" = ( @@ -8954,7 +8954,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "UK" = ( @@ -9000,7 +9000,7 @@ /obj/effect/turf_decal/tile/dark/half{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "UX" = ( @@ -9034,7 +9034,7 @@ /obj/effect/turf_decal/tile/dark{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "Vi" = ( @@ -9053,12 +9053,12 @@ /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Vn" = ( /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Vr" = ( @@ -9083,7 +9083,7 @@ /turf/open/floor/engine, /area/awaymission/caves/heretic_laboratory) "VA" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "VB" = ( @@ -9120,7 +9120,7 @@ /obj/item/fake_items/lahti_l39{ pixel_x = 16 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "VI" = ( @@ -9173,7 +9173,7 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "VZ" = ( @@ -9208,7 +9208,7 @@ /turf/open/floor/iron/white, /area/awaymission/caves/heretic_laboratory) "Wj" = ( -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Wk" = ( @@ -9226,7 +9226,7 @@ dir = 4 }, /obj/structure/sign/poster/contraband/clown/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Wn" = ( @@ -9239,11 +9239,6 @@ /obj/structure/window/reinforced/plasma/spawner/directional/east, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory_clean) -"Wr" = ( -/obj/effect/blessing/invisible, -/obj/effect/blessing/invisible, -/turf/open/floor/plating, -/area/awaymission/caves/heretic_laboratory_clean) "Ws" = ( /obj/structure/fluff/beach_umbrella/science, /turf/open/misc/beach/sand, @@ -9336,7 +9331,7 @@ /area/awaymission/caves/heretic_laboratory) "WN" = ( /obj/item/radio/intercom/mi13/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron, /area/awaymission/caves/heretic_laboratory_clean) "WO" = ( @@ -9346,7 +9341,7 @@ "WP" = ( /obj/machinery/door/airlock/hatch, /obj/structure/holosign/barrier/atmos/sturdy, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/plating, /area/awaymission/caves/heretic_laboratory_clean) "WR" = ( @@ -9381,7 +9376,7 @@ /obj/effect/turf_decal/tile/dark_green/anticorner/contrasted{ dir = 4 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Xa" = ( @@ -9433,7 +9428,7 @@ /obj/effect/turf_decal/tile/dark_green/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Xm" = ( @@ -9504,7 +9499,7 @@ /area/awaymission/caves/heretic_laboratory) "XC" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "XI" = ( @@ -9539,7 +9534,7 @@ dir = 4 }, /obj/item/radio/intercom/mi13/directional/east, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "XR" = ( @@ -9547,7 +9542,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "XS" = ( @@ -9588,7 +9583,7 @@ "Yb" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/tile/bar/half/contrasted, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Yc" = ( @@ -9631,7 +9626,7 @@ /obj/structure/window/spawner/directional/west, /obj/structure/window/spawner/directional/south, /obj/structure/flora/bush/jungle/c/style_random, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/misc/grass/jungle, /area/awaymission/caves/heretic_laboratory) "Yn" = ( @@ -9673,7 +9668,7 @@ /obj/item/ammo_box/magazine/lahtimagazine{ anchored = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory_clean) "Yv" = ( @@ -9697,7 +9692,7 @@ /obj/effect/turf_decal/tile/bar/half/contrasted{ dir = 1 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Yy" = ( @@ -9856,7 +9851,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "Zy" = ( @@ -9867,7 +9862,7 @@ "ZD" = ( /obj/machinery/vending/wardrobe/sec_wardrobe, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ZG" = ( @@ -9891,7 +9886,7 @@ dir = 8 }, /obj/item/radio/intercom/mi13/directional/west, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/small, /area/awaymission/caves/heretic_laboratory) "ZL" = ( @@ -9929,7 +9924,7 @@ pixel_y = 4 }, /obj/effect/turf_decal/tile/dark/opposingcorners, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/floor/iron/dark, /area/awaymission/caves/heretic_laboratory) "ZV" = ( @@ -9957,7 +9952,7 @@ /obj/structure/window/spawner/directional/east, /obj/structure/window/spawner/directional/west, /obj/structure/flora/bush/jungle/c/style_random, -/obj/effect/blessing/invisible, +/obj/effect/mapping_helpers/blessing/invisible, /turf/open/misc/grass/jungle, /area/awaymission/caves/heretic_laboratory) @@ -23476,7 +23471,7 @@ vk vk vk vk -Wr +vk xl xl nd diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index 232f152d0583..44a446d9480d 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -162,7 +162,7 @@ desc = "Second place prize for excellence in archaeology. Wonder who got first place?"; name = "silver trophy (second place)" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "aQ" = ( /obj/structure/extinguisher_cabinet/directional/west, @@ -2475,7 +2475,7 @@ /obj/structure/fluff/paper/corner, /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "ra" = ( /obj/effect/decal/cleanable/dirt, @@ -3130,7 +3130,7 @@ /obj/structure/closet/crate/bin, /obj/item/food/badrecipe/moldy/bacteria, /obj/structure/fluff/paper, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "vk" = ( /obj/structure/extinguisher_cabinet/directional/north, @@ -3269,7 +3269,7 @@ /obj/item/stock_parts/power_store/cell/crap/empty{ pixel_x = -4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "wh" = ( /obj/structure/alien/weeds/node, @@ -3362,7 +3362,7 @@ /area/awaymission/moonoutpost19/arrivals) "xa" = ( /obj/machinery/light/cold/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/moonoutpost19/tent) "xb" = ( /obj/structure/dresser, @@ -3813,7 +3813,7 @@ /turf/open/misc/asteroid/moon, /area/awaymission/moonoutpost19/mines) "Au" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/awaymission/moonoutpost19/tent) "Ay" = ( /obj/machinery/door/firedoor, @@ -3970,7 +3970,7 @@ /area/awaymission/moonoutpost19/arrivals/shed) "Bq" = ( /obj/structure/filingcabinet/chestdrawer, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "Bs" = ( /obj/machinery/door/airlock/research/glass{ @@ -4017,7 +4017,7 @@ "BE" = ( /obj/structure/cable, /obj/effect/spawner/structure/window/survival_pod, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "BG" = ( /obj/effect/decal/cleanable/blood/old, @@ -4214,7 +4214,7 @@ /area/awaymission/moonoutpost19/main) "CP" = ( /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/moonoutpost19/tent) "CU" = ( /obj/structure/fluff/minepost, @@ -4810,7 +4810,7 @@ "HJ" = ( /obj/structure/safe, /obj/item/camera, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "HN" = ( /obj/item/stack/rods, @@ -4959,7 +4959,7 @@ name = "\improper Historical Ripley MK-II wreckage" }, /obj/effect/decal/cleanable/blood/oil, -/obj/item/food/grown/moonflower{ +/obj/item/food/grown/flower/moonflower{ pixel_x = 7; pixel_y = -18 }, @@ -4988,10 +4988,10 @@ /obj/machinery/door/airlock/survival_pod/glass, /obj/structure/fans/tiny, /obj/effect/turf_decal/lunar_sand/plating, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/moonoutpost19/tent) "IP" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/awaymission/moonoutpost19/tent) "IW" = ( /obj/effect/turf_decal/stripes/corner{ @@ -6011,7 +6011,7 @@ /area/awaymission/moonoutpost19/main) "Qx" = ( /obj/effect/turf_decal/lunar_sand/plating, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/moonoutpost19/tent) "Qy" = ( /obj/effect/turf_decal/tile/red{ @@ -6559,7 +6559,7 @@ /obj/structure/chair/office{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "Uy" = ( /obj/structure/table, @@ -6750,7 +6750,7 @@ pixel_y = 4 }, /obj/machinery/cell_charger, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) "Wg" = ( /obj/structure/flora/rock/style_random{ @@ -7198,7 +7198,7 @@ /turf/open/misc/asteroid/moon/dug, /area/awaymission/moonoutpost19/mines) "Zo" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/awaymission/moonoutpost19/tent) "Zp" = ( /obj/effect/decal/cleanable/cobweb, @@ -7327,7 +7327,7 @@ pixel_x = -6 }, /obj/item/pen/red, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/awaymission/moonoutpost19/tent) (1,1,1) = {" diff --git a/_maps/RandomZLevels/museum.dmm b/_maps/RandomZLevels/museum.dmm index f480983e5806..e8030c47c99f 100644 --- a/_maps/RandomZLevels/museum.dmm +++ b/_maps/RandomZLevels/museum.dmm @@ -197,7 +197,9 @@ /area/awaymission/museum) "bF" = ( /obj/effect/mapping_helpers/bombable_wall, -/turf/closed/indestructible/reinforced, +/turf/closed/indestructible/reinforced{ + baseturfs = /turf/open/indestructible/plating + }, /area/awaymission/museum) "bG" = ( /obj/effect/decal/cleanable/dirt/dust, @@ -433,6 +435,11 @@ /obj/structure/fluff/beach_umbrella, /turf/open/misc/beach/sand, /area/awaymission/museum/mothroachvoid) +"dA" = ( +/turf/closed/indestructible/reinforced{ + baseturfs = /turf/open/indestructible/plating + }, +/area/awaymission/museum) "dF" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue{ @@ -5421,10 +5428,6 @@ /area/awaymission/museum) "Ta" = ( /obj/machinery/light/directional/west, -/obj/effect/decal/cleanable/crayon/puzzle/pin{ - pixel_x = -32; - puzzle_id = "museum_right_wing" - }, /turf/open/floor/iron/white/small, /area/awaymission/museum) "Tr" = ( @@ -27539,7 +27542,7 @@ IP Zg fJ FK -FK +dA zE Hs eN @@ -27551,7 +27554,7 @@ aZ FO FK ta -FK +FO AQ FK re @@ -36291,7 +36294,7 @@ FK hw Be XM -FK +FO dZ dZ PC @@ -36548,7 +36551,7 @@ FK FK FK Yd -FO +FK NO dZ mn diff --git a/_maps/_basemap.dm b/_maps/_basemap.dm index 4eb456c92b49..de2df9689e56 100644 --- a/_maps/_basemap.dm +++ b/_maps/_basemap.dm @@ -1,30 +1,22 @@ -//#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it. -//#define ABSOLUTE_MINIMUM //uncomment this to load a smaller centcom and smaller runtime station, only works together with LOWMEMORYMODE //#define SHOW_INVENTORY_ICONS //uncomment this to make mapping software use inventory icons rather then world icons // DARKPACK EDIT ADD -#ifdef ABSOLUTE_MINIMUM -#define LOWMEMORYMODE -#endif - -#ifndef ABSOLUTE_MINIMUM +#ifndef MINIMAL_CENTCOM #include "map_files\Vampire\admin_central.dmm" // DARKPACK EDIT CHANGE - ORIGINAL: #include "map_files\generic\CentCom.dmm" #else #include "map_files\generic\CentCom_minimal.dmm" #endif -#ifndef LOWMEMORYMODE - #ifdef ALL_MAPS - // DARKPACK EDIT CHANGE START - #include "map_files\Vampire\westfield_mall\westfield_mall.dmm" - #include "map_files\Vampire\runtimetown.dmm" - #include "map_files\debug\multiz.dmm" - #include "map_files\debug\runtimestation.dmm" - // DARKPACK EDIT CHANGE END - // APOC EDIT ADD START - (Special Fran) - #include "map_files\Vampire\special_fran\special_francisco.dmm" - // APOC EDIT ADD END - #endif - #ifdef ALL_TEMPLATES - #include "templates.dm" - #endif +#ifdef ALL_MAPS + // DARKPACK EDIT CHANGE START + #include "map_files\Vampire\westfield_mall\westfield_mall.dmm" + #include "map_files\Vampire\runtimetown.dmm" + #include "map_files\debug\multiz.dmm" + #include "map_files\debug\runtimestation.dmm" + // DARKPACK EDIT CHANGE END + // APOC EDIT ADD START - (Special Fran) + #include "map_files\Vampire\special_fran\special_francisco.dmm" + // APOC EDIT ADD END +#endif +#ifdef ALL_TEMPLATES + #include "templates.dm" #endif diff --git a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm index a24c14cf07aa..ba90617d069d 100644 --- a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm +++ b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm @@ -1810,11 +1810,14 @@ /area/station/maintenance/starboard/upper) "ayR" = ( /obj/structure/table, +/obj/machinery/airalarm/directional/west, /obj/item/storage/crayons{ pixel_x = -3; pixel_y = 1 }, -/obj/machinery/airalarm/directional/west, +/obj/item/storage/photo_album/library{ + pixel_x = 16 + }, /turf/open/floor/wood, /area/station/service/library/printer) "ayU" = ( @@ -2497,6 +2500,29 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet, /area/station/security/courtroom) +"aIe" = ( +/obj/structure/railing{ + dir = 8 + }, +/obj/effect/decal/cleanable/blood/oil, +/obj/effect/decal/cleanable/dirt, +/obj/structure/railing{ + dir = 8 + }, +/obj/structure/table/wood/fancy/blue, +/obj/effect/spawner/random/aimodule/neutral{ + pixel_y = 10; + pixel_x = 1 + }, +/obj/effect/spawner/random/aimodule/neutral{ + pixel_x = 3 + }, +/obj/structure/extinguisher_cabinet/directional/south, +/obj/effect/spawner/random/aimodule/neutral{ + pixel_y = 4 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "aIh" = ( /obj/machinery/camera/preset/ordnance{ c_tag = "Cannon's exit" @@ -2741,20 +2767,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, /turf/open/floor/plating, /area/station/maintenance/starboard) -"aKz" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/structure/table/wood/fancy/blue, -/obj/effect/spawner/random/aimodule/neutral{ - pixel_y = 10 - }, -/obj/effect/spawner/random/aimodule/neutral{ - pixel_x = 3; - pixel_y = -3 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "aKH" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -3496,6 +3508,12 @@ /obj/machinery/computer/pandemic, /turf/open/floor/iron/dark/textured_large, /area/station/medical/virology) +"aVk" = ( +/obj/effect/turf_decal/tile/yellow/opposingcorners{ + dir = 1 + }, +/turf/closed/wall, +/area/station/engineering/storage/tech) "aVm" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -4742,10 +4760,6 @@ }, /turf/open/floor/catwalk_floor, /area/station/engineering/engine_smes) -"bjK" = ( -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "bkc" = ( /obj/machinery/light/warm/directional/west, /obj/machinery/computer/mech_bay_power_console{ @@ -6017,22 +6031,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white/smooth_half, /area/station/ai/satellite/foyer) -"bBD" = ( -/obj/machinery/light/directional/north, -/obj/structure/table/wood, -/obj/item/food/grown/harebell{ - pixel_y = 5 - }, -/obj/item/food/grown/harebell{ - pixel_y = 5 - }, -/obj/item/food/grown/harebell{ - pixel_y = 5 - }, -/turf/open/floor/iron/chapel{ - dir = 4 - }, -/area/station/service/chapel) "bBI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -6419,25 +6417,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room/upper) -"bGh" = ( -/obj/effect/turf_decal/trimline/green/filled/line, -/obj/effect/turf_decal/trimline/green/filled/mid_joiner, -/obj/structure/table/glass, -/obj/item/storage/box/beakers{ - pixel_x = -5; - pixel_y = 13 - }, -/obj/item/storage/box/syringes{ - pixel_x = 3; - pixel_y = 7 - }, -/obj/item/reagent_containers/dropper{ - pixel_x = -3; - pixel_y = -4 - }, -/obj/item/storage/pill_bottle/sepsisillin, -/turf/open/floor/iron/dark/textured_large, -/area/station/medical/virology) "bGm" = ( /obj/structure/sign/poster/official/random/directional/east, /turf/open/floor/plating, @@ -8065,19 +8044,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"ccr" = ( -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/computer/upload/ai/no_lock{ - dir = 4 - }, -/obj/machinery/door/window/left/directional/east{ - name = "AI Upload Console Window"; - req_access = list("ai_upload") - }, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "ccs" = ( /obj/effect/spawner/random/trash/cigbutt, /turf/open/floor/plating, @@ -9452,6 +9418,7 @@ /obj/effect/turf_decal/tile/dark_green{ dir = 4 }, +/obj/item/storage/photo_album/bar, /turf/open/floor/iron/checker, /area/station/service/bar/backroom) "cwe" = ( @@ -9951,6 +9918,13 @@ }, /turf/open/floor/iron/dark, /area/station/commons/storage/art) +"cBJ" = ( +/obj/machinery/atmospherics/pipe/smart/simple/purple/visible{ + dir = 1 + }, +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/atmos/upper) "cBT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -10939,6 +10913,16 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/service/chapel) +"cPo" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/assembly/flash/handheld, +/obj/item/assembly/flash/handheld, +/obj/effect/turf_decal/tile/yellow/opposingcorners{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "cPq" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -11025,10 +11009,6 @@ }, /turf/open/floor/iron/dark, /area/station/commons/vacant_room/office) -"cQi" = ( -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/atmos/office) "cQu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/door/airlock/maintenance, @@ -16814,6 +16794,10 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"euv" = ( +/obj/effect/landmark/event_spawn, +/turf/open/floor/glass/reinforced/plasma, +/area/station/science/ordnance) "eux" = ( /obj/machinery/camera/directional/north{ c_tag = "Atmospherics - co2 Cell"; @@ -17910,6 +17894,14 @@ }, /turf/open/openspace, /area/station/engineering/supermatter/room/upper) +"eKF" = ( +/obj/machinery/door/poddoor/shutters/preopen{ + id = "rdordnance"; + name = "Ordnance Lab Shutters" + }, +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/science/ordnance) "eKL" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/broken/directional/east, @@ -18968,6 +18960,10 @@ /obj/effect/spawner/random/trash/cigbutt, /turf/open/floor/plating, /area/station/maintenance/starboard/aft/upper) +"eZr" = ( +/obj/machinery/atmospherics/components/unary/thermomachine, +/turf/open/floor/engine, +/area/station/engineering/atmos/hfr_room) "eZt" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -19266,6 +19262,12 @@ /obj/structure/sign/departments/medbay/alt/directional/south, /turf/open/openspace, /area/station/medical/medbay/central) +"feL" = ( +/obj/effect/spawner/random/structure/crate, +/obj/effect/mapping_helpers/broken_floor, +/obj/structure/sign/poster/contraband/random/directional/west, +/turf/open/floor/plating, +/area/station/maintenance/starboard/fore) "feM" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible, /obj/machinery/camera/autoname/directional/east, @@ -22023,13 +22025,6 @@ /obj/machinery/holopad, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"fPv" = ( -/obj/machinery/atmospherics/pipe/smart/simple/purple/visible{ - dir = 1 - }, -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/atmos/upper) "fPy" = ( /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 @@ -22381,11 +22376,6 @@ /obj/structure/cable, /turf/open/floor/catwalk_floor/iron, /area/station/hallway/primary/central) -"fUc" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/turf/open/floor/wood, -/area/station/maintenance/starboard) "fUi" = ( /obj/structure/lattice/catwalk, /obj/item/stack/cable_coil/thirty, @@ -28648,6 +28638,12 @@ /obj/machinery/door/firedoor, /turf/open/floor/wood/large, /area/station/service/bar) +"hDn" = ( +/obj/machinery/ai_law_rack/base/core{ + pixel_y = 12 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "hDt" = ( /obj/effect/turf_decal/delivery, /obj/item/radio/intercom/directional/north, @@ -29364,13 +29360,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) -"hNb" = ( -/obj/item/kirbyplants/organic/plant9{ - pixel_y = 14; - pixel_x = 4 - }, -/turf/open/floor/carpet/black, -/area/station/ai/upload/chamber) "hNf" = ( /obj/structure/cable, /turf/open/floor/plating, @@ -32195,6 +32184,25 @@ /obj/structure/lattice/catwalk, /turf/open/openspace, /area/station/maintenance/starboard/central/upper) +"izc" = ( +/obj/effect/turf_decal/trimline/green/filled/line, +/obj/effect/turf_decal/trimline/green/filled/mid_joiner, +/obj/structure/table/glass, +/obj/item/storage/box/beakers{ + pixel_x = -5; + pixel_y = 13 + }, +/obj/item/storage/box/syringes{ + pixel_x = 3; + pixel_y = 7 + }, +/obj/item/reagent_containers/dropper{ + pixel_x = -3; + pixel_y = -4 + }, +/obj/item/storage/pill_bottle/sepsisillin, +/turf/open/floor/iron/dark/textured_large, +/area/station/medical/virology) "izg" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/red/opposingcorners{ @@ -33037,6 +33045,10 @@ /obj/effect/turf_decal/box, /turf/open/floor/iron/dark/textured_corner, /area/station/science/ordnance/storage) +"iKn" = ( +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/atmos/office) "iKw" = ( /obj/effect/turf_decal/tile/red/fourcorners, /obj/structure/table, @@ -33125,6 +33137,9 @@ /obj/machinery/status_display/evac/directional/west, /turf/open/floor/wood/large, /area/station/hallway/primary/central) +"iLs" = ( +/turf/open/floor/glass/reinforced/plasma, +/area/station/science/ordnance) "iLt" = ( /obj/machinery/airalarm/directional/east, /obj/machinery/camera/autoname/directional/east, @@ -34033,19 +34048,6 @@ "jap" = ( /turf/open/floor/engine/hull/air, /area/station/commons/dorms) -"jau" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/turf/open/floor/wood, -/area/station/maintenance/starboard) "jaz" = ( /obj/effect/turf_decal/tile/dark_green/half/contrasted, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -34385,19 +34387,6 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) -"jfi" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/door/window/left/directional/east{ - name = "Cyborg Upload Console Window"; - req_access = list("ai_upload") - }, -/obj/machinery/computer/upload/borg/no_lock{ - dir = 4 - }, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "jfl" = ( /obj/effect/turf_decal/tile/blue/fourcorners, /obj/machinery/atmospherics/components/binary/pump/off{ @@ -35181,6 +35170,11 @@ /obj/item/radio/intercom/directional/east, /turf/open/floor/iron, /area/station/engineering/lobby) +"jri" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell, +/turf/open/floor/wood, +/area/station/maintenance/starboard) "jrt" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/trimline/purple/filled/line, @@ -37866,9 +37860,6 @@ }, /turf/open/floor/iron/dark, /area/station/science/lobby) -"kbu" = ( -/turf/open/floor/plating, -/area/station/engineering/atmos/upper) "kbA" = ( /obj/machinery/atmospherics/components/binary/pump/off{ name = "Chamber to Mix" @@ -38004,13 +37995,6 @@ }, /turf/open/floor/iron/freezer, /area/station/commons/toilet/auxiliary) -"kdt" = ( -/obj/machinery/porta_turret/ai{ - dir = 4 - }, -/obj/machinery/camera/autoname/motion/directional/south, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "kdv" = ( /obj/structure/railing{ dir = 1 @@ -38437,6 +38421,11 @@ /obj/effect/mapping_helpers/airlock/access/any/science/maintenance, /turf/open/floor/plating, /area/station/maintenance/aft) +"kkB" = ( +/obj/item/radio/intercom/directional/west, +/obj/machinery/status_display/ai/directional/west, +/turf/open/floor/carpet/black, +/area/station/ai/upload/chamber) "kkF" = ( /obj/structure/chair/office/light{ dir = 4 @@ -38502,22 +38491,6 @@ }, /turf/open/floor/iron/kitchen, /area/station/service/kitchen) -"klf" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_x = -5; - pixel_y = 4 - }, -/obj/item/food/grown/poppy{ - pixel_x = -6; - pixel_y = -4 - }, -/obj/item/food/grown/poppy{ - pixel_x = 3; - pixel_y = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "klh" = ( /obj/machinery/atmospherics/pipe/smart/manifold/yellow/visible{ dir = 1 @@ -38619,6 +38592,18 @@ }, /turf/open/space/openspace, /area/space/nearstation) +"kmk" = ( +/obj/structure/lattice/catwalk, +/obj/structure/table, +/obj/item/food/grown/flower/poppy{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 5 + }, +/turf/open/openspace, +/area/station/maintenance/port/fore) "kml" = ( /obj/structure/bed{ dir = 4 @@ -40155,6 +40140,11 @@ /obj/effect/turf_decal/tile/brown/full, /turf/open/floor/iron/large, /area/station/maintenance/starboard) +"kHM" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/atmos) "kHU" = ( /obj/effect/spawner/random/trash/grille_or_waste, /turf/open/floor/plating, @@ -41047,16 +41037,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/port) -"kSB" = ( -/obj/machinery/porta_turret/ai{ - dir = 4 - }, -/obj/structure/sign/plaques/kiddie{ - pixel_y = 32; - dir = 1 - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "kSD" = ( /obj/effect/turf_decal/siding/thinplating_new/dark/end{ dir = 1 @@ -42052,9 +42032,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/engineering/storage_shared) -"lhw" = ( -/turf/open/floor/glass/reinforced/plasma, -/area/station/science/ordnance) "lhy" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42255,6 +42232,30 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/command/heads_quarters/hop) +"lkl" = ( +/obj/effect/decal/cleanable/blood/oil, +/obj/structure/sign/poster/official/state_laws/directional/east, +/obj/machinery/light_switch/directional/north{ + pixel_x = -7 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) +"lkn" = ( +/obj/machinery/flasher/directional/west, +/obj/structure/table/wood/fancy/black, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = -3 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_x = -2; + pixel_y = 1 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 4; + pixel_x = 2 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "lkt" = ( /obj/effect/turf_decal/tile/blue{ dir = 8 @@ -43457,14 +43458,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/wood, /area/station/service/chapel) -"lAu" = ( -/obj/structure/cable, -/obj/structure/rack, -/obj/item/aicard, -/obj/item/pai_card, -/obj/item/circuitboard/aicore, -/turf/open/floor/wood, -/area/station/command/heads_quarters/rd) "lAF" = ( /obj/machinery/light/warm/dim/directional/south, /obj/effect/turf_decal/tile/dark_green{ @@ -43582,6 +43575,15 @@ }, /turf/open/floor/iron, /area/station/science/lobby) +"lBt" = ( +/obj/structure/railing/corner{ + dir = 8 + }, +/obj/structure/railing/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "lBy" = ( /obj/structure/railing/corner/end/flip{ dir = 8 @@ -45040,18 +45042,6 @@ /obj/machinery/portable_atmospherics/canister/nitrogen, /turf/open/floor/iron/dark/textured_large, /area/station/engineering/atmos) -"lWw" = ( -/obj/structure/lattice/catwalk, -/obj/structure/table, -/obj/item/food/grown/poppy{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/food/grown/poppy{ - pixel_x = 5 - }, -/turf/open/openspace, -/area/station/maintenance/port/fore) "lWS" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -45353,6 +45343,24 @@ /obj/structure/table, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room/upper) +"lZX" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5 + }, +/obj/machinery/camera/autoname/directional/north, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5; + pixel_x = 5 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5; + pixel_x = -5 + }, +/turf/open/floor/iron/chapel{ + dir = 1 + }, +/area/station/service/chapel) "mae" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -46099,17 +46107,6 @@ /obj/structure/extinguisher_cabinet/directional/west, /turf/open/floor/iron, /area/station/engineering/storage_shared) -"mlj" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/ai_module/reset, -/obj/item/assembly/flash/handheld, -/obj/item/assembly/flash/handheld, -/obj/effect/turf_decal/tile/yellow/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "mlx" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -46234,10 +46231,6 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"mnf" = ( -/obj/machinery/atmospherics/components/unary/thermomachine, -/turf/open/floor/engine, -/area/station/engineering/atmos/hfr_room) "mnn" = ( /obj/effect/mapping_helpers/airlock/locked, /obj/effect/mapping_helpers/airlock/welded, @@ -46479,6 +46472,19 @@ /obj/item/stack/rods/two, /turf/open/space/basic, /area/space/nearstation) +"mqE" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/turf/open/floor/wood, +/area/station/maintenance/starboard) "mqJ" = ( /obj/machinery/airalarm/directional/south, /obj/effect/turf_decal/siding/wood/end, @@ -46497,6 +46503,25 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) +"mqP" = ( +/obj/structure/lattice/catwalk, +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/table, +/obj/item/food/grown/flower/poppy{ + pixel_x = -6; + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 2; + pixel_y = 6 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = -12 + }, +/turf/open/openspace, +/area/station/maintenance/port/fore) "mqQ" = ( /obj/effect/turf_decal/tile/blue/opposingcorners{ dir = 1 @@ -48591,6 +48616,22 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/central/upper) +"mSO" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = -6; + pixel_y = 14 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 4; + pixel_y = 2 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "mSQ" = ( /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/east, @@ -51048,13 +51089,6 @@ /obj/item/storage/toolbox/emergency, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"nCw" = ( -/obj/effect/decal/cleanable/generic, -/obj/structure/railing{ - dir = 5 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "nCI" = ( /obj/machinery/component_printer, /obj/effect/turf_decal/bot_white, @@ -53081,6 +53115,10 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden/abandoned) +"ocU" = ( +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/atmos) "ocW" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -56351,6 +56389,26 @@ /obj/effect/turf_decal/siding/yellow, /turf/open/floor/engine, /area/station/command/heads_quarters/ce) +"oVX" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/effect/decal/cleanable/generic, +/obj/effect/decal/cleanable/generic, +/obj/structure/railing{ + dir = 5 + }, +/obj/structure/table, +/obj/effect/spawner/random/aimodule/harmless{ + pixel_y = 1; + pixel_x = -4 + }, +/obj/effect/spawner/random/aimodule/harmless{ + pixel_x = -11; + pixel_y = 4 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "oWf" = ( /obj/effect/turf_decal/siding/dark/corner, /turf/open/floor/iron/dark/telecomms, @@ -58405,6 +58463,27 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/engine/hull/air, /area/station/commons/fitness/recreation) +"pAC" = ( +/obj/structure/railing{ + dir = 8 + }, +/obj/structure/table, +/obj/structure/lattice/catwalk, +/obj/item/clipboard{ + pixel_x = 1; + pixel_y = -2 + }, +/obj/item/screwdriver, +/obj/item/multitool{ + pixel_x = 2; + pixel_y = 0 + }, +/obj/item/reagent_containers/cup/glass/mug{ + pixel_x = -5; + pixel_y = -15 + }, +/turf/open/openspace, +/area/station/ai/upload/chamber) "pAJ" = ( /obj/item/radio/intercom/directional/east, /turf/open/floor/wood, @@ -58693,6 +58772,11 @@ }, /turf/open/openspace, /area/station/science/xenobiology) +"pFM" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/engineering/tracking_beacon, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "pFR" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/dark_green{ @@ -58857,6 +58941,18 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plating, /area/station/maintenance/port) +"pJb" = ( +/obj/machinery/camera/motion/directional/south{ + c_tag = "AI Upload Chamber - Lower"; + network = list("aiupload") + }, +/obj/machinery/airalarm/directional/south, +/obj/machinery/camera/autoname/motion/directional/south, +/obj/machinery/porta_turret/ai{ + dir = 4 + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "pJe" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -59278,24 +59374,6 @@ /obj/machinery/firealarm/directional/east, /turf/open/floor/iron/dark/smooth_large, /area/station/service/hydroponics) -"pNg" = ( -/obj/structure/railing/corner{ - dir = 1 - }, -/obj/structure/railing{ - dir = 1 - }, -/obj/structure/table/wood/fancy/green, -/obj/effect/spawner/random/aimodule/harmless{ - pixel_x = 2; - pixel_y = 8 - }, -/obj/effect/spawner/random/aimodule/harmless{ - pixel_x = -3; - pixel_y = -2 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "pNh" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -62854,7 +62932,6 @@ "qKC" = ( /obj/structure/lattice/catwalk, /obj/structure/table/reinforced, -/obj/item/storage/photo_album/qm, /turf/open/openspace, /area/station/cargo/storage) "qKG" = ( @@ -63067,14 +63144,6 @@ /obj/effect/landmark/start/assistant, /turf/open/floor/iron, /area/station/commons/dorms) -"qMQ" = ( -/obj/machinery/light/directional/east, -/obj/machinery/ai_slipper{ - uses = 8 - }, -/obj/machinery/light_switch/directional/east, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "qNd" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment, @@ -63856,6 +63925,25 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/warden) +"qXQ" = ( +/obj/effect/decal/cleanable/generic, +/obj/effect/decal/cleanable/dirt, +/obj/item/ai_module/law/supplied/protect_station, +/obj/item/ai_module/law/supplied/quarantine{ + pixel_y = 3; + pixel_x = 2 + }, +/obj/structure/table/wood/fancy/red, +/obj/effect/spawner/random/aimodule/harmful{ + pixel_y = 7 + }, +/obj/machinery/door/window/brigdoor/left/directional/north{ + req_access = list("ai_upload"); + name = "AI Modules" + }, +/obj/structure/window/reinforced/spawner/directional/west, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "qXR" = ( /obj/effect/turf_decal/trimline/red/filled/mid_joiner{ dir = 4 @@ -65999,19 +66087,6 @@ /obj/structure/closet/firecloset, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"rCQ" = ( -/obj/structure/table/wood/fancy/red, -/obj/effect/spawner/random/aimodule/harmful{ - pixel_y = 10 - }, -/obj/item/ai_module/reset/purge, -/obj/machinery/door/window/brigdoor/left/directional/north{ - req_access = list("ai_upload"); - name = "AI Modules" - }, -/obj/structure/window/reinforced/spawner/directional/west, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "rDc" = ( /turf/open/floor/iron/dark/smooth_large, /area/station/medical/surgery) @@ -66141,14 +66216,6 @@ }, /turf/open/floor/grass, /area/station/science/cytology) -"rFj" = ( -/obj/machinery/door/poddoor/shutters/preopen{ - id = "rdordnance"; - name = "Ordnance Lab Shutters" - }, -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/science/ordnance) "rFk" = ( /obj/effect/turf_decal/tile/blue/opposingcorners{ dir = 1 @@ -67213,6 +67280,22 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"rVr" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_x = -5; + pixel_y = 4 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = -6; + pixel_y = -4 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 3; + pixel_y = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "rVs" = ( /obj/effect/turf_decal/tile/red/opposingcorners{ dir = 1 @@ -68650,6 +68733,22 @@ }, /turf/open/floor/wood/large, /area/station/service/library) +"soA" = ( +/obj/machinery/light/directional/north, +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = 5 + }, +/turf/open/floor/iron/chapel{ + dir = 4 + }, +/area/station/service/chapel) "soK" = ( /obj/structure/cable, /obj/effect/turf_decal/weather/snow, @@ -69517,10 +69616,6 @@ /obj/structure/sign/poster/contraband/missing_gloves/directional/south, /turf/open/floor/iron/smooth_large, /area/station/maintenance/department/medical) -"sAI" = ( -/obj/item/food/grown/sunflower, -/turf/open/floor/iron/vaporwave, -/area/station/maintenance/starboard/aft/upper) "sAR" = ( /obj/effect/decal/cleanable/dirt, /obj/item/radio/intercom/directional/south, @@ -69875,6 +69970,17 @@ }, /turf/open/floor/glass/reinforced, /area/station/medical/chemistry) +"sGH" = ( +/obj/machinery/light/directional/east, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/obj/structure/sign/plaques/kiddie{ + pixel_x = 32; + dir = 4 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "sGQ" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/blue/full, @@ -70104,10 +70210,6 @@ }, /turf/open/floor/carpet/black, /area/station/maintenance/hallway/abandoned_recreation) -"sKF" = ( -/obj/machinery/status_display/ai/directional/west, -/turf/open/floor/carpet/black, -/area/station/ai/upload/chamber) "sKL" = ( /obj/structure/chair/stool/bar/directional/south, /obj/effect/landmark/start/hangover, @@ -71327,19 +71429,6 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/iron, /area/station/service/kitchen/abandoned) -"taf" = ( -/obj/structure/sign/poster/contraband/random/directional/west, -/obj/effect/spawner/random/structure/crate, -/obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/plating, -/area/station/maintenance/starboard/fore) -"tai" = ( -/obj/structure/railing/corner{ - dir = 8 - }, -/obj/effect/spawner/random/engineering/tracking_beacon, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "tan" = ( /obj/effect/turf_decal/tile/blue/half, /obj/effect/landmark/event_spawn, @@ -72502,10 +72591,6 @@ /obj/structure/cable, /turf/open/openspace, /area/station/security/checkpoint/supply) -"tpL" = ( -/obj/machinery/portable_atmospherics/canister/plasma, -/turf/open/floor/engine, -/area/station/engineering/atmos/hfr_room) "tpR" = ( /obj/effect/landmark/blobstart, /obj/effect/landmark/generic_maintenance_landmark, @@ -74137,11 +74222,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/engineering/storage_shared) -"tMN" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/atmos) "tMY" = ( /obj/structure/cable, /turf/open/floor/iron/dark, @@ -74513,26 +74593,6 @@ }, /turf/open/openspace, /area/station/ai/upload/chamber) -"tRV" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/structure/table, -/obj/item/reagent_containers/cup/glass/mug{ - pixel_x = -5; - pixel_y = -15 - }, -/obj/item/ai_module/toy_ai{ - pixel_x = -8; - pixel_y = 9 - }, -/obj/structure/lattice/catwalk, -/obj/item/clipboard{ - pixel_x = 1; - pixel_y = -2 - }, -/turf/open/openspace, -/area/station/ai/upload/chamber) "tRY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/landmark/event_spawn, @@ -74638,6 +74698,15 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"tTV" = ( +/obj/structure/table/wood, +/obj/effect/decal/cleanable/cobweb, +/obj/effect/decal/cleanable/dirt, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/turf/open/floor/plating, +/area/station/maintenance/port/fore) "tUn" = ( /obj/machinery/door/airlock/maintenance, /obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, @@ -74985,6 +75054,26 @@ /obj/structure/cable, /turf/open/floor/wood/large, /area/station/service/theater_dressing) +"tYC" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/railing/corner{ + dir = 1 + }, +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/table, +/obj/effect/spawner/random/aimodule/harmless{ + pixel_x = -3; + pixel_y = 2 + }, +/obj/item/ai_module/law/core/freeformcore{ + pixel_x = 4 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "tYE" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -76574,12 +76663,6 @@ }, /turf/open/floor/iron/dark/smooth_large, /area/station/hallway/secondary/service) -"uuk" = ( -/obj/effect/decal/cleanable/blood/oil, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/structure/sign/poster/official/state_laws/directional/east, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "uuA" = ( /obj/structure/table/reinforced, /obj/effect/decal/cleanable/dirt, @@ -77181,6 +77264,9 @@ /obj/machinery/digital_clock/directional/north, /turf/open/floor/catwalk_floor/iron_dark, /area/station/cargo/bitrunning/den) +"uBi" = ( +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "uBj" = ( /obj/structure/cable, /turf/closed/wall/r_wall, @@ -79320,24 +79406,6 @@ /obj/machinery/holopad, /turf/open/floor/iron/dark, /area/station/commons/vacant_room/commissary) -"vcX" = ( -/obj/machinery/atmospherics/pipe/smart/simple/green/visible, -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/atmos) -"vcY" = ( -/obj/machinery/flasher/directional/west, -/obj/structure/table/wood/fancy/black, -/obj/item/ai_module/supplied/freeform{ - pixel_x = -2; - pixel_y = -2 - }, -/obj/item/ai_module/reset{ - pixel_x = 2; - pixel_y = 8 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/ai/upload/chamber) "vdj" = ( /obj/structure/chair/office{ dir = 1 @@ -80502,10 +80570,6 @@ }, /turf/open/floor/wood/large, /area/station/commons/lounge) -"vsT" = ( -/obj/machinery/holopad, -/turf/open/floor/glass/reinforced/plasma, -/area/station/science/ordnance) "vsW" = ( /obj/machinery/flasher/directional/west, /obj/structure/table/wood/fancy/cyan, @@ -83736,6 +83800,11 @@ /obj/item/soap, /turf/open/floor/iron/freezer, /area/station/command/heads_quarters/captain/private) +"wkB" = ( +/obj/machinery/atmospherics/pipe/smart/simple/green/visible, +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/atmos) "wkC" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/engineering/canister, @@ -85008,24 +85077,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/wood/large, /area/station/hallway/primary/central) -"wBz" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell{ - pixel_y = 5 - }, -/obj/machinery/camera/autoname/directional/north, -/obj/item/food/grown/harebell{ - pixel_y = 5; - pixel_x = 5 - }, -/obj/item/food/grown/harebell{ - pixel_y = 5; - pixel_x = -5 - }, -/turf/open/floor/iron/chapel{ - dir = 1 - }, -/area/station/service/chapel) "wBJ" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -85869,10 +85920,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet, /area/station/commons/storage/tools) -"wNN" = ( -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/atmos) "wNO" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -86445,22 +86492,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/carpet, /area/station/commons/storage/tools) -"wWs" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/item/food/grown/poppy{ - pixel_x = -6; - pixel_y = 14 - }, -/obj/item/food/grown/poppy{ - pixel_x = 4; - pixel_y = 2 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "wWx" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -86647,6 +86678,17 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/upper) +"wXT" = ( +/obj/structure/cable, +/obj/structure/rack, +/obj/item/aicard, +/obj/item/pai_card, +/obj/item/circuitboard/aicore, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/wood, +/area/station/command/heads_quarters/rd) "wXU" = ( /obj/machinery/newscaster/directional/south, /obj/machinery/suit_storage_unit/captain, @@ -87213,6 +87255,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/catwalk_floor, /area/station/maintenance/starboard/central/upper) +"xge" = ( +/obj/item/food/grown/flower/sunflower, +/turf/open/floor/iron/vaporwave, +/area/station/maintenance/starboard/aft/upper) "xgC" = ( /obj/machinery/light_switch{ pixel_x = -9; @@ -87666,6 +87712,10 @@ /obj/structure/cable, /turf/open/floor/eighties, /area/station/maintenance/hallway/abandoned_recreation) +"xmo" = ( +/obj/machinery/holopad, +/turf/open/floor/glass/reinforced/plasma, +/area/station/science/ordnance) "xms" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -88370,10 +88420,6 @@ }, /turf/open/floor/iron/smooth, /area/station/science/robotics) -"xwF" = ( -/obj/effect/landmark/event_spawn, -/turf/open/floor/glass/reinforced/plasma, -/area/station/science/ordnance) "xwJ" = ( /obj/effect/spawner/structure/window/reinforced/tinted, /turf/open/floor/plating, @@ -90358,25 +90404,6 @@ }, /turf/open/floor/iron/dark/smooth_large, /area/station/hallway/secondary/entry) -"xWw" = ( -/obj/structure/lattice/catwalk, -/obj/structure/railing{ - dir = 1 - }, -/obj/structure/table, -/obj/item/food/grown/poppy{ - pixel_x = -6; - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_x = 2; - pixel_y = 6 - }, -/obj/item/food/grown/harebell{ - pixel_y = -12 - }, -/turf/open/openspace, -/area/station/maintenance/port/fore) "xWI" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -90458,15 +90485,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/catwalk_floor/iron, /area/station/hallway/primary/central) -"xXr" = ( -/obj/structure/table/wood, -/obj/effect/decal/cleanable/cobweb, -/obj/effect/decal/cleanable/dirt, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/turf/open/floor/plating, -/area/station/maintenance/port/fore) "xXv" = ( /obj/effect/spawner/random/structure/grille, /obj/structure/lattice, @@ -90876,6 +90894,10 @@ /obj/effect/landmark/firealarm_sanity, /turf/open/openspace, /area/station/hallway/primary/central) +"ydk" = ( +/obj/machinery/portable_atmospherics/canister/plasma, +/turf/open/floor/engine, +/area/station/engineering/atmos/hfr_room) "ydo" = ( /obj/structure/railing/corner/end{ dir = 8 @@ -119673,13 +119695,13 @@ oDc vgU kzR ixF -klf +rVr glz lum xMH glz qHI -wWs +mSO gPW pOt vKt @@ -120187,7 +120209,7 @@ pqr fSX kZA ixF -wBz +lZX lQL uID kRz @@ -120444,7 +120466,7 @@ xQp mAn bMS ixF -bBD +soA jMs mFL kSN @@ -121562,9 +121584,9 @@ vkk qUW xyH fQp -vcY -sKF -hNb +lkn +kkB +mvq qIp uXx vsW @@ -121821,8 +121843,8 @@ nKs fQp pLG mvq -ccr -jfi +uBi +hDn mvq pLG fQp @@ -122333,12 +122355,12 @@ tRY wXL uEZ fQp -kSB +pLG mvq vSW vyV tPC -kdt +pJb fQp lmC nRu @@ -128763,7 +128785,7 @@ arT ecB tXs fuu -rFj +eKF dbH iwB eYC @@ -129020,7 +129042,7 @@ arT uRz qfr esC -rFj +eKF jxt iwB elx @@ -130238,7 +130260,7 @@ nBR dCm dCm dCm -mlj +cPo jjZ udl udl @@ -130495,7 +130517,7 @@ joA uaI uaI dCm -udl +aVk udl udl vXi @@ -130752,7 +130774,7 @@ xGb hnI dCm dCm -taf +feL dXy dCm hAb @@ -133547,7 +133569,7 @@ iWu heR bWY ule -tMN +kHM lLT ePI shl @@ -133804,7 +133826,7 @@ mXN heR bWY vNb -tMN +kHM sZk cPr shl @@ -134575,7 +134597,7 @@ ftG kGD pQS heR -wNN +ocU aim sSx fQT @@ -134660,7 +134682,7 @@ wRT vTh bBL lFF -jau +mqE uAp lFF lFF @@ -135180,7 +135202,7 @@ lFF lFF syT oKy -fUc +jri lFF lFF uAp @@ -135603,7 +135625,7 @@ sFb kil pQS heR -wNN +ocU aim ybM tSB @@ -136631,7 +136653,7 @@ gIn aUW pQS heR -wNN +ocU aim vyG dkb @@ -137659,7 +137681,7 @@ jRb oBp pQS heR -wNN +ocU aim rwV dkb @@ -138438,7 +138460,7 @@ dkb dkb aEg gdW -vcX +wkB woN vwp nYV @@ -138687,7 +138709,7 @@ hMt kGV pQS heR -wNN +ocU aim ogR dkb @@ -138695,7 +138717,7 @@ lEl dkb aEg qeV -wNN +ocU bWY bWY bWY @@ -138952,7 +138974,7 @@ dkb dkb aEg aim -wNN +ocU dcE heR dcE @@ -139466,7 +139488,7 @@ bLP cwy aEg aim -wNN +ocU dcE heR dcE @@ -139715,7 +139737,7 @@ gZO bhn pQS heR -wNN +ocU aim nTf dkb @@ -139980,7 +140002,7 @@ ygK ygK geJ aim -wNN +ocU dcE heR dcE @@ -140237,7 +140259,7 @@ mDs dra feM cUj -wNN +ocU dcE heR dcE @@ -140487,13 +140509,13 @@ dcE heR dcE pZJ -wNN +ocU pZJ pZJ -wNN +ocU pZJ pZJ -wNN +ocU pZJ dcE heR @@ -179071,8 +179093,8 @@ uRd bOk oUE uRd -xWw -lWw +mqP +kmk bOk vbe uRd @@ -182714,7 +182736,7 @@ ihl keh tMo pkK -bGh +izc cDs xyz xyz @@ -184435,7 +184457,7 @@ nlm nlm nlm bOk -xXr +tTV uSg bOk nSs @@ -188128,7 +188150,7 @@ ruD fQp eDj tRR -tRV +pAC oYs vkd wZn @@ -188384,10 +188406,10 @@ hmz mUm fQp pvT -pNg +tYC xBq -tai -aKz +lBt +aIe fQp gAf nHg @@ -188641,8 +188663,8 @@ hmz gyh fQp qZQ -nCw -bjK +oVX +pFM aIU fWW fQp @@ -188897,11 +188919,11 @@ gih bsK oTQ fQp -uuk +lkl jQL -qMQ +sGH hZq -rCQ +qXQ fQp nHg tjn @@ -191955,7 +191977,7 @@ eRE hhu noz nIQ -lAu +wXT mmq iHy khT @@ -193528,7 +193550,7 @@ vvD ubs ubs gsm -rFj +eKF jAz wNO btv @@ -193788,8 +193810,8 @@ gsm bQe gXR wNO -lhw -lhw +iLs +iLs awP nsp nVg @@ -194042,11 +194064,11 @@ kVQ ubs ubs gsm -rFj +eKF vAL dbg -xwF -vsT +euv +xmo dvw oUn riZ @@ -194302,8 +194324,8 @@ gsm bQe vkZ cBf -lhw -lhw +iLs +iLs awP dRX nFB @@ -194556,7 +194578,7 @@ arT ubs ubs gsm -rFj +eKF nJd pgM weO @@ -201388,9 +201410,9 @@ bNX bNX bNX dKM -mnf -mnf -mnf +eZr +eZr +eZr sey bxd mDG @@ -202275,11 +202297,11 @@ fXr blF cPB rue -sAI +xge rue -sAI +xge rue -sAI +xge rue cPB blF @@ -202530,7 +202552,7 @@ lwA blF fXr blF -sAI +xge nUi nUi nUi @@ -202538,7 +202560,7 @@ nUi nUi nUi nUi -sAI +xge blF pul vSA @@ -202688,7 +202710,7 @@ xYa jkM kHF aJs -cQi +iKn wgZ lHz hqj @@ -203052,7 +203074,7 @@ uMt uMt uMt nUi -sAI +xge blF blF bLp @@ -203202,7 +203224,7 @@ gfi klh fTv awR -cQi +iKn iuQ gnm kAn @@ -203444,7 +203466,7 @@ bNX bNX bNX lGE -tpL +ydk lmH lmH sUR @@ -203566,7 +203588,7 @@ nUi nUi nUi nUi -sAI +xge usZ mOk vSA @@ -203718,7 +203740,7 @@ ucy xcZ shQ shQ -cQi +iKn shQ gmD gmD @@ -203817,11 +203839,11 @@ mQG blF cPB nOT -sAI +xge nOT -sAI +xge nOT -sAI +xge nOT nUi blF @@ -204479,8 +204501,8 @@ pGP pGP pGP gjd -fPv -fPv +cBJ +cBJ fHb wIi wkX @@ -205507,8 +205529,8 @@ pGP pGP pGP xKo -fPv -fPv +cBJ +cBJ tbE wIi kbA @@ -205765,7 +205787,7 @@ gtC gtC gtC fHO -kbu +unl bYd bYd eaU diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 4d95af7df73f..11cee3833fc6 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -157,6 +157,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"abZ" = ( +/obj/item/radio/intercom/directional/south, +/obj/structure/table/glass, +/obj/item/food/grown/flower/poppy/geranium, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "acn" = ( /obj/machinery/door/airlock/medical{ name = "Medbay Cold Room" @@ -5422,15 +5432,6 @@ }, /turf/open/floor/iron/white, /area/station/science/lab) -"bpT" = ( -/obj/machinery/ai_slipper{ - uses = 10 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "bpV" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -7966,6 +7967,23 @@ }, /turf/open/space/basic, /area/space/nearstation) +"bTK" = ( +/obj/structure/sign/plaques/kiddie/badger{ + pixel_y = 32 + }, +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "bTN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -8611,6 +8629,12 @@ /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/iron/dark, /area/station/service/library) +"cbU" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/corner, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "cbW" = ( /obj/structure/table/wood, /obj/item/pinpointer/nuke, @@ -18910,16 +18934,6 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron/white/smooth_large, /area/station/medical/chemistry) -"eIf" = ( -/obj/structure/table/wood/fancy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) "eIg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -22201,6 +22215,23 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron, /area/station/security/checkpoint/supply) +"fyl" = ( +/obj/structure/table/glass, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/cable, +/obj/item/storage/box/beakers{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/storage/pill_bottle/sepsisillin{ + pixel_x = -6; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/turf/open/floor/iron, +/area/station/medical/virology) "fym" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/structure/bed/medical/emergency, @@ -23211,18 +23242,6 @@ dir = 6 }, /area/station/security/office) -"fKM" = ( -/obj/structure/table/reinforced, -/obj/item/ai_module/reset{ - pixel_x = 2; - pixel_y = 8 - }, -/obj/item/radio/intercom/directional/west, -/obj/machinery/status_display/evac/directional/south, -/obj/item/ai_module/supplied/freeform, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "fKP" = ( /obj/effect/turf_decal/trimline/white/warning, /obj/effect/mapping_helpers/burnt_floor, @@ -23336,23 +23355,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/pharmacy) -"fLV" = ( -/obj/structure/table/glass, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/cable, -/obj/item/storage/box/beakers{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/storage/pill_bottle/sepsisillin{ - pixel_x = -6; - pixel_y = 2 - }, -/obj/item/storage/box/syringes, -/turf/open/floor/iron, -/area/station/medical/virology) "fMb" = ( /obj/structure/window/reinforced/spawner/directional/north, /obj/structure/window/reinforced/spawner/directional/west, @@ -23404,6 +23406,16 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/lesser) +"fNk" = ( +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/table/wood, +/obj/item/flashlight/lantern, +/obj/item/food/grown/flower/harebell, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "fNm" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -25361,14 +25373,6 @@ /obj/structure/sign/poster/official/report_crimes/directional/north, /turf/open/floor/iron, /area/station/security/checkpoint/escape) -"gjl" = ( -/obj/machinery/computer/upload/borg/no_lock{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "gjB" = ( /obj/structure/cable, /obj/structure/closet/emcloset, @@ -28557,17 +28561,6 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/plating, /area/station/maintenance/department/chapel) -"gUJ" = ( -/obj/machinery/button/door/directional/west{ - id = "Dorm4"; - name = "Dormitory Door Lock"; - normaldoorcontrol = 1; - specialfunctions = 4 - }, -/obj/structure/table/wood, -/obj/item/food/grown/poppy/geranium, -/turf/open/floor/wood, -/area/station/commons/dorms) "gUU" = ( /obj/machinery/door/firedoor, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -28974,6 +28967,16 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron, /area/station/engineering/storage/tech) +"hbx" = ( +/obj/structure/table/wood/fancy, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "hby" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -29340,6 +29343,15 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/ai/satellite/exterior) +"hfo" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy/lily, +/obj/structure/sign/painting/library{ + pixel_y = -32 + }, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/wood, +/area/station/commons/dorms) "hfB" = ( /obj/effect/spawner/random/vending/colavend, /obj/effect/turf_decal/delivery, @@ -29996,6 +30008,28 @@ }, /turf/open/floor/plating, /area/station/science/research/abandoned) +"hpf" = ( +/obj/machinery/status_display/ai/directional/north, +/obj/structure/table/wood/fancy/red, +/obj/machinery/door/window/brigdoor/left/directional/east{ + name = "High-Risk Modules"; + req_access = list("captain") + }, +/obj/effect/spawner/random/aimodule/harmful{ + pixel_y = -16 + }, +/obj/structure/sign/nanotrasen/directional/west, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) +"hpj" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/obj/effect/turf_decal/stripes/white/line, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "hpm" = ( /obj/structure/rack, /obj/item/controller, @@ -30679,6 +30713,17 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/maintenance/department/science) +"hxX" = ( +/obj/machinery/button/door/directional/west{ + id = "Dorm4"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + specialfunctions = 4 + }, +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy/geranium, +/turf/open/floor/wood, +/area/station/commons/dorms) "hxY" = ( /obj/effect/spawner/random/trash/mess, /obj/effect/decal/cleanable/dirt, @@ -32850,13 +32895,6 @@ }, /turf/open/floor/iron/grimy, /area/station/security/detectives_office) -"icA" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/holopad/secure, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "icD" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -33548,11 +33586,10 @@ /turf/open/floor/iron, /area/station/engineering/atmos/project) "ilx" = ( -/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ dir = 5 }, -/obj/machinery/suit_storage_unit/industrial/loader, +/obj/item/kirbyplants/random, /turf/open/floor/iron, /area/station/command/heads_quarters/qm) "ilG" = ( @@ -39544,6 +39581,14 @@ /obj/structure/tank_holder/extinguisher, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"jIs" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "jIB" = ( /obj/machinery/meter, /obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, @@ -41918,23 +41963,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/aft) -"klm" = ( -/obj/structure/sign/plaques/kiddie/badger{ - pixel_y = 32 - }, -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "klr" = ( /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron, @@ -45408,16 +45436,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/service/abandoned_gambling_den/gaming) -"liQ" = ( -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/table/wood, -/obj/item/flashlight/lantern, -/obj/item/food/grown/harebell, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) "liV" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/delivery, @@ -48907,6 +48925,15 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"mal" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/holopad/secure, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "maI" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ @@ -51904,14 +51931,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/service/abandoned_gambling_den) -"mMd" = ( -/obj/machinery/computer/upload/ai/no_lock{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "mMr" = ( /obj/effect/turf_decal/stripes/end{ dir = 1 @@ -52159,15 +52178,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) -"mPo" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy/lily, -/obj/structure/sign/painting/library{ - pixel_y = -32 - }, -/obj/item/radio/intercom/directional/west, -/turf/open/floor/wood, -/area/station/commons/dorms) "mPr" = ( /obj/machinery/recharge_station, /obj/effect/turf_decal/bot/right, @@ -54430,22 +54440,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/science/xenobiology) -"nwW" = ( -/obj/machinery/status_display/ai/directional/north, -/obj/structure/table/wood/fancy/red, -/obj/machinery/door/window/brigdoor/left/directional/east{ - name = "High-Risk Modules"; - req_access = list("captain") - }, -/obj/effect/spawner/random/aimodule/harmful{ - pixel_y = -16 - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/obj/structure/sign/nanotrasen/directional/west, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "nwY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -55606,6 +55600,13 @@ }, /turf/open/floor/iron, /area/station/maintenance/port/aft) +"nKx" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "nKA" = ( /obj/effect/spawner/structure/window, /obj/machinery/door/poddoor/preopen{ @@ -62789,6 +62790,21 @@ dir = 8 }, /area/station/hallway/primary/fore) +"pBe" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy, +/obj/machinery/status_display/evac/directional/north, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "pBg" = ( /obj/machinery/button/door/directional/west{ id = "gulagdoor"; @@ -67840,6 +67856,24 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron, /area/station/maintenance/department/security) +"qJm" = ( +/obj/structure/table/reinforced, +/obj/item/radio/intercom/directional/west, +/obj/machinery/status_display/evac/directional/south, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = -1; + pixel_x = -1 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/item/ai_module/law/supplied/freeform{ + pixel_x = 2; + pixel_y = 5 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 3 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "qJs" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -68147,6 +68181,12 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"qMR" = ( +/obj/structure/table/reinforced, +/obj/item/assembly/flash/handheld, +/obj/item/assembly/flash/handheld, +/turf/open/floor/iron, +/area/station/engineering/storage/tech) "qMS" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/delivery, @@ -69875,16 +69915,6 @@ }, /turf/open/floor/iron, /area/station/maintenance/port/aft) -"rlL" = ( -/obj/item/radio/intercom/directional/south, -/obj/structure/table/glass, -/obj/item/food/grown/poppy/geranium, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "rlQ" = ( /obj/structure/chair{ dir = 4 @@ -76348,6 +76378,14 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/fore) +"sNJ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/ai_law_rack/base/core, +/obj/effect/turf_decal/stripes/white/full, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "sNN" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table/reinforced, @@ -76492,15 +76530,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) -"sQp" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy/lily, -/obj/item/food/grown/poppy/geranium{ - pixel_x = 6; - pixel_y = 6 - }, -/turf/open/floor/carpet/royalblack, -/area/station/service/chapel/office) "sQr" = ( /obj/item/kirbyplants/random, /obj/machinery/newscaster/directional/north, @@ -77416,6 +77445,7 @@ dir = 1 }, /obj/effect/decal/cleanable/dirt, +/obj/machinery/suit_storage_unit/industrial/loader, /turf/open/floor/iron, /area/station/cargo/warehouse) "tbJ" = ( @@ -79598,6 +79628,14 @@ }, /turf/open/floor/iron, /area/station/maintenance/port/fore) +"tEY" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/line{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "tFa" = ( /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, @@ -79895,6 +79933,18 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/construction, /turf/open/floor/iron, /area/station/maintenance/port) +"tIu" = ( +/obj/machinery/ai_slipper{ + uses = 10 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/line{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "tIK" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -80349,6 +80399,15 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/general, /turf/open/floor/iron, /area/station/medical/storage) +"tOL" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy/lily, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = 6; + pixel_y = 6 + }, +/turf/open/floor/carpet/royalblack, +/area/station/service/chapel/office) "tOM" = ( /obj/structure/cable, /obj/effect/turf_decal/delivery, @@ -86101,6 +86160,14 @@ }, /turf/open/floor/iron/dark, /area/station/science/genetics) +"viS" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/ai_slipper{ + uses = 10 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "viX" = ( /obj/item/wrench, /obj/machinery/atmospherics/components/trinary/filter{ @@ -88908,6 +88975,13 @@ "vTY" = ( /turf/open/floor/iron, /area/station/cargo/storage) +"vUc" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/corner{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "vUe" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -90126,6 +90200,20 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) +"wkH" = ( +/obj/structure/table/reinforced, +/obj/machinery/status_display/ai/directional/east, +/obj/item/storage/briefcase/secure, +/obj/item/aicard, +/obj/item/circuitboard/aicore, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 + }, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/iron/white, +/area/station/command/heads_quarters/rd) "wkP" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -91896,6 +91984,14 @@ }, /turf/open/floor/plating, /area/station/service/abandoned_gambling_den) +"wFU" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/stripes/white/line{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "wGa" = ( /obj/structure/disposalpipe/junction{ dir = 8 @@ -93690,13 +93786,6 @@ /obj/effect/landmark/start/scientist, /turf/open/floor/iron/white, /area/station/science/research) -"xgl" = ( -/obj/structure/table/reinforced, -/obj/item/ai_module/reset, -/obj/item/assembly/flash/handheld, -/obj/item/assembly/flash/handheld, -/turf/open/floor/iron, -/area/station/engineering/storage/tech) "xgt" = ( /obj/structure/cable, /obj/structure/table/wood, @@ -94629,17 +94718,6 @@ /obj/effect/spawner/random/bedsheet/any/double, /turf/open/floor/wood, /area/station/maintenance/port/aft) -"xsp" = ( -/obj/structure/table/reinforced, -/obj/machinery/status_display/ai/directional/east, -/obj/item/storage/briefcase/secure, -/obj/item/aicard, -/obj/item/circuitboard/aicore, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/command/heads_quarters/rd) "xsq" = ( /obj/structure/table, /obj/item/storage/box/lights/mixed, @@ -95100,15 +95178,6 @@ }, /turf/open/floor/glass, /area/station/maintenance/space_hut/observatory) -"xxL" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/light/floor, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "xxN" = ( /obj/structure/table/wood, /obj/machinery/computer/security/telescreen/entertainment/directional/north, @@ -98246,21 +98315,6 @@ }, /turf/open/floor/iron, /area/station/command/heads_quarters/ce) -"ylr" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/food/grown/poppy{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/poppy, -/obj/machinery/status_display/evac/directional/north, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "ylG" = ( /obj/structure/sign/warning/radiation/directional/south, /obj/effect/turf_decal/tile/brown{ @@ -105644,10 +105698,10 @@ bPC bPC bPC cdt -nwW +hpf iuH eCk -fKM +qJm cdt cdt qYo @@ -106159,7 +106213,7 @@ qwY bPC vWU hmU -sdK +viS xpt sdK sdK @@ -106416,9 +106470,9 @@ kkh bPC hmU hmU -sdK -mMd -hmU +cbU +wFU +nKx cfu ptq cdt @@ -106672,10 +106726,10 @@ pFl wLX aCK kjZ -kjZ -xxL -icA -bpT +mal +hpj +sNJ +tIu kmX uoO sGj @@ -106930,9 +106984,9 @@ kKz bPC apT hmU -sdK -gjl -hmU +jIs +tEY +vUc cfu asO cdt @@ -107187,7 +107241,7 @@ aHr bPC vWU hmU -sdK +viS tDB hmU hmU @@ -128212,7 +128266,7 @@ oYs oYs qdn inS -rlL +abZ oYs oYs clK @@ -128758,7 +128812,7 @@ kAD wVU aJU aJU -xgl +qMR cnR cnR cnR @@ -129320,7 +129374,7 @@ hwS sIX qCG eqh -xsp +wkH sIX sIX sIX @@ -133223,7 +133277,7 @@ jyT wjV sFR wOz -fLV +fyl gqm qYo qYo @@ -143481,7 +143535,7 @@ hlj tFG eKi oGK -klm +bTK utS qCp wHF @@ -143738,7 +143792,7 @@ ckO mLV kdC oGK -ylr +pBe mHE qCp wHF @@ -146302,7 +146356,7 @@ qMf gHE hxS azo -sQp +tOL xsi vOO gMK @@ -146565,7 +146619,7 @@ tdC kZc mVY qKs -eIf +hbx aCz cVh bom @@ -148368,7 +148422,7 @@ wCv flE gcB jNZ -liQ +fNk brb aaa aaa @@ -148587,7 +148641,7 @@ yaI aEV sKL tYs -mPo +hfo yaI xgA lru @@ -150384,7 +150438,7 @@ mrf bhg gfp yaI -gUJ +hxX bhg bsd yaI diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 386dc7e02d4f..5bc3b534fa68 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -1528,6 +1528,20 @@ dir = 1 }, /area/station/engineering/atmos) +"avq" = ( +/obj/structure/table, +/obj/effect/turf_decal/tile/dark_green{ + dir = 4 + }, +/obj/structure/noticeboard/directional/north, +/obj/item/storage/toolbox/electrical, +/obj/item/assembly/flash/handheld{ + pixel_x = -4; + pixel_y = -2 + }, +/obj/item/radio/intercom/directional/east, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "avs" = ( /obj/structure/railing{ dir = 8 @@ -1904,22 +1918,6 @@ /obj/effect/turf_decal/siding/white, /turf/open/floor/iron/dark, /area/station/commons/fitness) -"aAi" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/table/wood/fancy/red, -/obj/effect/spawner/random/aimodule/harmful, -/obj/machinery/door/window/brigdoor/left/directional/east{ - name = "High-Risk Modules"; - req_access = list("captain") - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 4 - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "aAv" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -7168,6 +7166,15 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/engineering/engine_smes) +"bXu" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 9 + }, +/obj/machinery/porta_turret/ai{ + dir = 4 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "bXy" = ( /turf/open/openspace, /area/station/security/armory/upper) @@ -9326,19 +9333,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"cBU" = ( -/obj/structure/table, -/obj/machinery/camera/motion/directional/east{ - c_tag = "AI Upload East"; - network = list("aiupload") - }, -/obj/item/folder/blue, -/obj/effect/turf_decal/tile/dark_green{ - dir = 4 - }, -/obj/machinery/light/directional/east, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "cBY" = ( /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/underground/unexplored/rivers/deep/shoreline) @@ -11459,18 +11453,6 @@ }, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/underground/explored) -"dhL" = ( -/obj/machinery/computer/upload/ai/no_lock{ - dir = 1 - }, -/obj/machinery/flasher/directional/south{ - id = "AI" - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 9 - }, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "dhR" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/bookcase/random, @@ -12152,6 +12134,10 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/underground/explored) +"dra" = ( +/obj/structure/filingcabinet/chestdrawer, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "drb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14465,6 +14451,20 @@ /obj/structure/sign/poster/official/no_erp/directional/north, /turf/open/floor/iron, /area/station/commons/dorms/laundry) +"eav" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 2; + pixel_y = 4 + }, +/obj/item/food/grown/flower/harebell, +/obj/machinery/status_display/evac/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "eay" = ( /obj/effect/turf_decal/tile/neutral/diagonal_edge, /obj/structure/cable, @@ -14738,18 +14738,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room) -"efH" = ( -/obj/structure/table/glass, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_y = 3 - }, -/obj/item/food/grown/harebell, -/turf/open/floor/iron/chapel, -/area/station/service/chapel) "efI" = ( /obj/structure/lattice, /turf/open/misc/asteroid/snow/icemoon, @@ -15184,6 +15172,18 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/plating, /area/station/maintenance/aft/greater) +"elO" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/structure/table, +/obj/item/circuitboard/aicore, +/obj/machinery/airalarm/directional/west, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/iron/smooth, +/area/station/command/heads_quarters/rd) "elT" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/layer2{ dir = 8 @@ -15195,6 +15195,13 @@ dir = 1 }, /area/station/hallway/primary/starboard) +"emr" = ( +/obj/effect/turf_decal/tile/dark_green, +/obj/machinery/ai_slipper{ + uses = 10 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "emv" = ( /obj/machinery/door/window/right/directional/north{ name = "Public Mining" @@ -20049,21 +20056,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/medical/central) -"fIX" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/structure/rack, -/obj/item/aicard, -/obj/item/ai_module/reset, -/obj/machinery/status_display/ai/directional/south, -/obj/machinery/firealarm/directional/west, -/obj/effect/turf_decal/tile/dark_blue/full, -/obj/effect/turf_decal/siding/dark{ - dir = 5 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/engineering/storage/tech) "fJe" = ( /obj/machinery/door/airlock/external{ name = "Atmospherics External Airlock" @@ -24255,15 +24247,6 @@ }, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/surface/outdoors/less_spawns) -"gSN" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 10 - }, -/obj/structure/table, -/obj/item/circuitboard/aicore, -/obj/machinery/airalarm/directional/west, -/turf/open/floor/iron/smooth, -/area/station/command/heads_quarters/rd) "gSO" = ( /obj/effect/turf_decal/tile/brown/half/contrasted, /turf/open/floor/iron/dark/side, @@ -25219,14 +25202,6 @@ dir = 8 }, /area/station/security/brig/entrance) -"hho" = ( -/obj/machinery/vending/cigarette, -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/obj/structure/sign/nanotrasen/directional/west, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/captain) "hhr" = ( /obj/structure/weightmachine/weightlifter, /obj/effect/turf_decal/box, @@ -26309,13 +26284,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/service/lawoffice) -"hwC" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/turf/open/floor/iron, -/area/station/command/bridge) "hwE" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable, @@ -28435,16 +28403,6 @@ }, /turf/open/floor/plating, /area/station/service/chapel) -"ibY" = ( -/obj/structure/table, -/obj/item/ai_module/reset, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/dark_green, -/obj/machinery/firealarm/directional/north, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "ich" = ( /obj/structure/sign/warning/electric_shock/directional/north, /turf/open/openspace/icemoon/keep_below, @@ -32074,6 +32032,16 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron, /area/station/command/gateway) +"jfV" = ( +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/storage/photo_album/prison, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/table/wood, +/turf/open/floor/iron/dark, +/area/station/security/prison/rec) "jgl" = ( /obj/effect/turf_decal/trimline/dark_blue/corner{ dir = 1 @@ -32510,6 +32478,13 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"jnA" = ( +/obj/machinery/vending/cigarette, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/captain) "jnR" = ( /obj/effect/spawner/structure/window/hollow/reinforced/middle{ dir = 4 @@ -33959,6 +33934,19 @@ }, /turf/open/floor/plating, /area/station/cargo/sorting) +"jJi" = ( +/obj/structure/window/reinforced/spawner/directional/north, +/obj/structure/table/wood/fancy/red, +/obj/effect/spawner/random/aimodule/harmful, +/obj/machinery/door/window/brigdoor/left/directional/east{ + name = "High-Risk Modules"; + req_access = list("captain") + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 4 + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "jJl" = ( /obj/effect/landmark/start/botanist, /obj/effect/turf_decal/tile/green/opposingcorners{ @@ -34638,17 +34626,6 @@ /obj/machinery/light/small/directional/west, /turf/open/floor/iron/dark, /area/mine/eva) -"jRg" = ( -/obj/structure/table, -/obj/machinery/camera/motion/directional/west{ - c_tag = "AI Upload West"; - network = list("aiupload") - }, -/obj/item/ai_module/supplied/freeform, -/obj/effect/turf_decal/tile/dark_green, -/obj/machinery/light/directional/west, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "jRi" = ( /obj/structure/fence{ dir = 8 @@ -35455,20 +35432,6 @@ /obj/item/trash/energybar, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"kfl" = ( -/obj/structure/table/wood, -/obj/item/radio/intercom/directional/south, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy{ - pixel_x = 2; - pixel_y = 3 - }, -/obj/item/food/grown/poppy{ - pixel_x = 5; - pixel_y = -2 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "kfo" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/yellow, @@ -35641,6 +35604,20 @@ /obj/machinery/pdapainter/supply, /turf/open/floor/carpet, /area/station/command/heads_quarters/qm) +"kiq" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/rack, +/obj/item/aicard, +/obj/machinery/status_display/ai/directional/south, +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/tile/dark_blue/full, +/obj/effect/turf_decal/siding/dark{ + dir = 5 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/engineering/storage/tech) "kir" = ( /obj/machinery/door/airlock/external{ name = "External Access" @@ -37091,6 +37068,16 @@ }, /turf/open/floor/wood, /area/station/service/library) +"kCl" = ( +/obj/machinery/porta_turret/ai{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 5 + }, +/obj/structure/sign/nanotrasen/directional/west, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "kCn" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39132,6 +39119,10 @@ }, /turf/open/floor/engine, /area/station/science/xenobiology) +"lfx" = ( +/obj/structure/tank_holder/extinguisher, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "lfA" = ( /obj/effect/turf_decal/trimline/green/filled/line{ dir = 1 @@ -40782,14 +40773,6 @@ dir = 1 }, /area/station/engineering/lobby) -"lCI" = ( -/obj/effect/spawner/random/vending/snackvend, -/obj/effect/turf_decal/siding/wood{ - dir = 8 - }, -/obj/structure/sign/nanotrasen/directional/east, -/turf/open/floor/iron/dark, -/area/station/command/meeting_room) "lCY" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -41770,10 +41753,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"lRx" = ( -/obj/effect/turf_decal/tile/dark_green, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "lRy" = ( /obj/structure/showcase/cyborg/old{ dir = 8; @@ -42784,6 +42763,15 @@ /obj/structure/flora/grass/both/style_random, /turf/open/misc/asteroid/snow/standard_air, /area/station/hallway/secondary/exit/departure_lounge) +"mim" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 5 + }, +/obj/machinery/porta_turret/ai{ + dir = 4 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "miw" = ( /obj/effect/turf_decal/tile/yellow{ dir = 8 @@ -45246,10 +45234,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/engineering/storage/tech) -"mVp" = ( -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/ai/upload/chamber) "mVw" = ( /obj/machinery/requests_console/directional/west{ department = "Captain's Desk"; @@ -49262,6 +49246,21 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"nWt" = ( +/obj/structure/table/glass, +/obj/item/storage/box/beakers{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/obj/item/stack/sheet/mineral/plasma{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/effect/turf_decal/trimline/green/filled/corner, +/obj/item/storage/pill_bottle/sepsisillin, +/turf/open/floor/iron/white, +/area/station/medical/virology) "nWy" = ( /obj/effect/turf_decal/tile/brown/half/contrasted{ dir = 8 @@ -50048,6 +50047,25 @@ /obj/structure/urinal/directional/north, /turf/open/floor/iron/freezer, /area/station/commons/toilet) +"oiB" = ( +/obj/structure/table, +/obj/machinery/camera/motion/directional/west{ + c_tag = "AI Upload West"; + network = list("aiupload") + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 1; + pixel_x = -2 + }, +/obj/effect/turf_decal/tile/dark_green, +/obj/machinery/light/directional/west, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 6 + }, +/obj/item/ai_module/law/supplied/freeform, +/obj/machinery/airalarm/directional/west, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "oiC" = ( /obj/effect/spawner/random/engineering/canister, /turf/open/floor/plating, @@ -53148,13 +53166,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/white, /area/station/maintenance/port/fore) -"oXg" = ( -/obj/effect/turf_decal/tile/dark_green, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "oXm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/mapping_helpers/burnt_floor, @@ -53339,6 +53350,11 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"oZJ" = ( +/obj/structure/cable, +/obj/machinery/ai_law_rack/base/core, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/ai/upload/chamber) "oZL" = ( /obj/machinery/atmospherics/components/binary/pump{ dir = 4; @@ -54321,15 +54337,6 @@ /obj/effect/mapping_helpers/airlock/access/all/security/brig, /turf/open/floor/iron/dark/textured, /area/station/security/execution/transfer) -"pos" = ( -/obj/structure/table, -/obj/effect/spawner/round_default_module, -/obj/effect/turf_decal/tile/dark_green{ - dir = 4 - }, -/obj/structure/extinguisher_cabinet/directional/north, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "pou" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, @@ -55869,6 +55876,17 @@ }, /turf/open/floor/iron/freezer, /area/station/commons/toilet/locker) +"pLG" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/item/radio/intercom/prison/directional/north, +/obj/item/food/grown/flower/harebell{ + pixel_y = -3; + pixel_x = -2 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/rec) "pLJ" = ( /obj/structure/cable, /turf/open/floor/plating, @@ -56096,15 +56114,6 @@ /obj/structure/stairs/east, /turf/open/floor/iron/dark/textured, /area/station/security/armory) -"pPN" = ( -/obj/machinery/porta_turret/ai{ - dir = 8 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 10 - }, -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) "pPO" = ( /obj/effect/turf_decal/trimline/blue/filled/warning, /obj/effect/turf_decal/trimline/blue/filled/line{ @@ -56749,6 +56758,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/engineering/atmos) +"pYk" = ( +/obj/machinery/porta_turret/ai{ + dir = 8 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 9 + }, +/obj/structure/sign/nanotrasen/directional/east, +/turf/open/floor/circuit, +/area/station/ai/upload/chamber) "pYn" = ( /obj/structure/table/wood, /obj/machinery/computer/security/telescreen/prison/directional/north, @@ -58788,16 +58807,6 @@ }, /turf/open/floor/iron, /area/station/commons/dorms) -"qCP" = ( -/obj/machinery/airalarm/directional/west, -/obj/machinery/porta_turret/ai{ - dir = 4 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 5 - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "qCU" = ( /obj/machinery/airalarm/directional/east, /turf/open/floor/plating, @@ -62017,17 +62026,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron, /area/station/cargo/storage) -"rtF" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/radio/intercom/prison/directional/north, -/obj/item/food/grown/harebell{ - pixel_y = -3; - pixel_x = -2 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/rec) "rtO" = ( /obj/machinery/door/firedoor, /obj/machinery/door/window/left/directional/west{ @@ -63567,21 +63565,6 @@ "rSC" = ( /turf/open/floor/glass/reinforced, /area/station/engineering/lobby) -"rSN" = ( -/obj/machinery/computer/upload/borg/no_lock{ - dir = 1 - }, -/obj/item/radio/intercom/directional/south{ - broadcasting = 1; - frequency = 1447; - listening = 0; - name = "station intercom (AI Private)" - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 5 - }, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "rSP" = ( /obj/structure/closet/secure_closet/engineering_chief, /obj/machinery/power/apc/auto_name/directional/east, @@ -63827,29 +63810,6 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/general, /turf/open/floor/iron/smooth, /area/station/maintenance/department/medical/central) -"rWI" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 6 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 6 - }, -/obj/structure/table/glass, -/obj/machinery/light/small/directional/east, -/obj/machinery/firealarm/directional/east, -/obj/item/food/grown/poppy{ - pixel_x = 3; - pixel_y = -1 - }, -/obj/item/food/grown/poppy/geranium{ - pixel_x = 2; - pixel_y = 5 - }, -/obj/item/food/grown/poppy/lily{ - pixel_x = -2 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "rWO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -64482,6 +64442,19 @@ /obj/effect/spawner/random/structure/steam_vent, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"sgo" = ( +/obj/structure/table, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/dark_green, +/obj/structure/sign/plaques/kiddie{ + pixel_y = 32 + }, +/obj/effect/spawner/round_default_module, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "sgA" = ( /obj/effect/turf_decal/box, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -65084,21 +65057,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"soA" = ( -/obj/structure/table/glass, -/obj/item/storage/box/beakers{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/item/storage/box/syringes, -/obj/item/stack/sheet/mineral/plasma{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/effect/turf_decal/trimline/green/filled/corner, -/obj/item/storage/pill_bottle/sepsisillin, -/turf/open/floor/iron/white, -/area/station/medical/virology) "soE" = ( /obj/structure/reagent_dispensers/plumbed{ name = "service reservoir" @@ -69205,15 +69163,6 @@ /obj/structure/closet/firecloset, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"twK" = ( -/obj/machinery/porta_turret/ai{ - dir = 8 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 9 - }, -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) "twU" = ( /obj/effect/landmark/start/hangover, /turf/open/floor/iron/freezer, @@ -69900,6 +69849,20 @@ }, /turf/open/floor/wood, /area/station/commons/lounge) +"tHd" = ( +/obj/structure/table/wood, +/obj/item/radio/intercom/directional/south, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy{ + pixel_x = 2; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 5; + pixel_y = -2 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "tHj" = ( /obj/machinery/status_display/evac/directional/east, /turf/open/floor/iron, @@ -69993,20 +69956,6 @@ /obj/structure/sign/warning/biohazard/directional/east, /turf/open/floor/iron/white, /area/station/medical/virology) -"tIw" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = 2; - pixel_y = 4 - }, -/obj/item/food/grown/harebell, -/obj/machinery/status_display/evac/directional/south, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "tIy" = ( /obj/machinery/status_display/ai/directional/north, /turf/open/openspace, @@ -71862,6 +71811,14 @@ }, /turf/open/floor/iron/dark, /area/station/security/checkpoint/science) +"uka" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/machinery/status_display/ai/directional/south, +/turf/open/floor/iron, +/area/station/command/bridge) "ukd" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/trash/cigbutt, @@ -72666,6 +72623,13 @@ /obj/item/chair/wood, /turf/open/floor/wood, /area/station/maintenance/port/aft) +"uwg" = ( +/obj/effect/spawner/random/vending/snackvend, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/meeting_room) "uwp" = ( /obj/machinery/rnd/production/protolathe/department/engineering, /turf/open/floor/iron/dark, @@ -75666,6 +75630,15 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/science/lab) +"vqR" = ( +/obj/effect/turf_decal/tile/dark_green{ + dir = 4 + }, +/obj/machinery/flasher/directional/south{ + id = "AI" + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "vqS" = ( /obj/structure/rack, /obj/effect/spawner/random/maintenance, @@ -76553,6 +76526,15 @@ /obj/machinery/door/poddoor/incinerator_ordmix, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/burnchamber) +"vEf" = ( +/obj/effect/turf_decal/tile/dark_green{ + dir = 4 + }, +/obj/machinery/ai_slipper{ + uses = 10 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "vEy" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -77231,14 +77213,6 @@ /obj/effect/landmark/carpspawn, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/less_spawns) -"vOD" = ( -/obj/machinery/status_display/ai/directional/north, -/obj/effect/turf_decal/tile/dark_green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "vOG" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -81080,16 +81054,6 @@ }, /turf/open/floor/wood, /area/station/maintenance/port/aft) -"wVs" = ( -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/storage/photo_album/prison, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/table/wood, -/turf/open/floor/iron/dark, -/area/station/security/prison/rec) "wVu" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -81595,6 +81559,16 @@ }, /turf/open/lava/plasma/ice_moon, /area/icemoon/underground/explored) +"xcG" = ( +/obj/effect/turf_decal/tile/dark_green, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/machinery/flasher/directional/south{ + id = "AI" + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "xcS" = ( /obj/machinery/portable_atmospherics/canister/water_vapor, /obj/effect/turf_decal/tile/purple{ @@ -82068,6 +82042,18 @@ /obj/machinery/duct, /turf/open/floor/wood, /area/station/hallway/secondary/service) +"xid" = ( +/obj/structure/table/glass, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell, +/turf/open/floor/iron/chapel, +/area/station/service/chapel) "xie" = ( /obj/machinery/camera/directional/west{ c_tag = "Engineering MiniSat Access" @@ -82317,6 +82303,28 @@ dir = 1 }, /area/mine/eva/lower) +"xlM" = ( +/obj/structure/table, +/obj/machinery/camera/motion/directional/east{ + c_tag = "AI Upload East"; + network = list("aiupload") + }, +/obj/item/folder/blue, +/obj/effect/turf_decal/tile/dark_green{ + dir = 4 + }, +/obj/machinery/light/directional/east, +/obj/item/paper{ + pixel_y = 1; + pixel_x = -3 + }, +/obj/item/pen{ + pixel_x = 6; + pixel_y = 4 + }, +/obj/machinery/firealarm/directional/east, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "xlN" = ( /obj/machinery/door/poddoor/preopen{ id = "maint2" @@ -83343,15 +83351,6 @@ /obj/structure/closet/crate, /turf/open/floor/plating, /area/station/maintenance/department/medical/morgue) -"xzT" = ( -/obj/machinery/porta_turret/ai{ - dir = 4 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 6 - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "xAa" = ( /obj/structure/cable, /turf/open/floor/iron/dark, @@ -83969,6 +83968,29 @@ }, /turf/open/floor/iron/smooth_large, /area/station/cargo/warehouse) +"xHY" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 6 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 6 + }, +/obj/structure/table/glass, +/obj/machinery/light/small/directional/east, +/obj/machinery/firealarm/directional/east, +/obj/item/food/grown/flower/poppy{ + pixel_x = 3; + pixel_y = -1 + }, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = 2; + pixel_y = 5 + }, +/obj/item/food/grown/flower/poppy/lily{ + pixel_x = -2 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "xId" = ( /obj/structure/rack, /obj/item/clothing/suit/hooded/wintercoat/eva{ @@ -110668,9 +110690,9 @@ usE wJz rfu rfu -rtF +pLG bFS -wVs +jfV mkg kMW pjj @@ -180112,7 +180134,7 @@ qhR jqT jqT kPH -fIX +kiq jqT jqT fhL @@ -190407,7 +190429,7 @@ kET drR oQY oQY -soA +nWt dvK nid xDb @@ -198610,7 +198632,7 @@ uJt uJt uJt oTA -kfl +tHd wrX xMq xMq @@ -199124,7 +199146,7 @@ uJt uJt bap oTA -tIw +eav wrX xMq xMq @@ -244357,7 +244379,7 @@ nYv qVp xKJ dvA -lCI +uwg age olG bEC @@ -244870,10 +244892,10 @@ lCg wEG ybv ybv -ibY -jRg -qCP -aAi +sgo +oiB +kCl +jJi ybv ybv nBk @@ -245126,11 +245148,11 @@ gER nOH xVc ybv -xzT +lfx cHO aer cHO -rOC +vqR ybv ybv paM @@ -245384,7 +245406,7 @@ nOH mOL ybv oqz -lRx +emr oJP bdK dzi @@ -245644,7 +245666,7 @@ jjq fHo hMc rOC -dhL +bXu ybv ybv aqd @@ -245899,7 +245921,7 @@ dEI uab bzC jKK -mVp +oZJ mNJ lVc ybv @@ -246158,7 +246180,7 @@ xwm vzT wfc oqz -rSN +mim ybv ybv aqd @@ -246409,10 +246431,10 @@ dqR rUo paK lhv -hwC +uka ybv -vOD -rOC +cHO +vEf vzT cHO oYu @@ -246668,11 +246690,11 @@ gER wLt bIL ybv -pPN +dra oqz kOX oqz -oXg +xcG ybv ybv paM @@ -246926,9 +246948,9 @@ lhv xpw ybv ybv -pos -cBU -twK +avq +xlM +pYk pyh ybv ybv @@ -247441,7 +247463,7 @@ iFs qVp lpM aJE -hho +jnA cNK lKw lpM @@ -251550,7 +251572,7 @@ ryJ xgy faW dHf -rWI +xHY cOy sTH lso @@ -261088,7 +261110,7 @@ nyH lFW jzH mde -gSN +elO jbU wnX pMF @@ -265159,7 +265181,7 @@ wrX tuQ oHH tAA -efH +xid mJu vWU qPL diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 859999fbc45b..b964ab1b2183 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -42,26 +42,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/security/execution/transfer) -"aau" = ( -/obj/machinery/light/directional/east, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/structure/table, -/obj/effect/turf_decal/bot, -/obj/machinery/camera/directional/east{ - c_tag = "Cargo Office"; - name = "cargo camera"; - network = list("ss13","qm") - }, -/obj/machinery/fax{ - fax_name = "Cargo Office"; - name = "Cargo Office Fax Machine" - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/office) "aaw" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -110,11 +90,15 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"aba" = ( -/obj/structure/transit_tube/diagonal/crossing/topleft, -/obj/structure/lattice/catwalk, -/turf/open/space/basic, -/area/space/nearstation) +"aaV" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/public/glass{ + name = "Chapel Hallway" + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/fore) "abg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/decal/cleanable/blood/old, @@ -309,23 +293,6 @@ /obj/structure/flora/bush/pale/style_random, /turf/open/misc/asteroid, /area/space/nearstation) -"acZ" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/box/corners{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "add" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/structure/flora/bush/lavendergrass/style_random, @@ -505,17 +472,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden) -"afH" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/landmark/event_spawn, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/main) "afL" = ( /turf/closed/wall, /area/station/command/heads_quarters/hos) @@ -534,13 +490,18 @@ /obj/machinery/computer/security/telescreen/minisat/directional/west, /turf/open/floor/engine, /area/station/ai/satellite/foyer) -"agc" = ( -/obj/structure/table/wood, -/obj/structure/cable, -/obj/item/pen/fountain, +"afY" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/grimy, -/area/station/service/chapel/office) +/obj/effect/turf_decal/siding/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lobby) "agt" = ( /turf/closed/wall/rust, /area/space/nearstation) @@ -660,6 +621,14 @@ }, /turf/open/floor/iron, /area/station/security/office) +"aiR" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/status_display/evac/directional/north, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "ajb" = ( /obj/effect/turf_decal/tile/brown/half/contrasted{ dir = 4 @@ -744,6 +713,16 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"ajR" = ( +/obj/structure/table/wood/fancy/blue, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/obj/item/reagent_containers/cup/glass/trophy/bronze_cup, +/obj/machinery/camera/autoname/directional/west, +/obj/machinery/light/warm/dim/directional/west, +/turf/open/floor/carpet/royalblue, +/area/station/service/chapel/office) "ajZ" = ( /obj/structure/flora/tree/jungle/small, /turf/open/floor/grass, @@ -853,14 +832,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"alP" = ( -/obj/machinery/flasher/portable, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/armory) "alQ" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -877,17 +848,6 @@ "alV" = ( /turf/closed/wall/r_wall/rust, /area/station/ai/satellite/interior) -"ami" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 9 - }, -/obj/structure/cable, -/turf/open/floor/iron/dark/corner{ - dir = 1 - }, -/area/station/hallway/primary/fore) "amn" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/machinery/newscaster/directional/east, @@ -985,6 +945,27 @@ /obj/structure/cable, /turf/open/floor/iron/solarpanel/airless, /area/space/nearstation) +"anw" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 4; + name = "Plasma to Incinerator" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/hidden{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "anz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -995,6 +976,13 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/lockers) +"anE" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/holopad/secure, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "anF" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ @@ -1013,16 +1001,6 @@ "anH" = ( /turf/closed/wall, /area/space/nearstation) -"anI" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "anN" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable, @@ -1037,21 +1015,6 @@ /obj/structure/sign/nanotrasen, /turf/closed/wall, /area/space/nearstation) -"aoi" = ( -/obj/structure/table/wood/fancy, -/obj/machinery/light/directional/west, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/machinery/firealarm/directional/south, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "aor" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -1104,25 +1067,6 @@ /obj/structure/flora/bush/grassy/style_random, /turf/open/floor/grass, /area/station/service/hydroponics) -"apl" = ( -/obj/machinery/light/directional/south, -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen, -/obj/item/stamp/denied{ - pixel_x = 8; - pixel_y = 6 - }, -/obj/item/stamp/granted{ - pixel_x = 8; - pixel_y = 2 - }, -/obj/machinery/firealarm/directional/south, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "apm" = ( /obj/effect/decal/cleanable/blood/gibs/old, /turf/open/misc/asteroid/lowpressure, @@ -1185,15 +1129,6 @@ }, /turf/open/floor/plating, /area/station/security/execution/education) -"aqe" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 8 - }, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "aqq" = ( /obj/effect/turf_decal/tile/red{ dir = 4 @@ -1216,6 +1151,17 @@ /obj/effect/mapping_helpers/airlock/access/any/medical/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"aqu" = ( +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/machinery/duct, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/pharmacy) "aqv" = ( /obj/structure/lattice/catwalk, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -1226,6 +1172,29 @@ }, /turf/open/space/basic, /area/space/nearstation) +"aqx" = ( +/obj/item/radio/intercom/directional/west, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) +"aqE" = ( +/obj/machinery/requests_console/directional/south{ + department = "Janitorial"; + name = "Janitorial Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/turf_decal/stripes/corner, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple/half/contrasted, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron, +/area/station/service/janitor) "aqQ" = ( /obj/effect/turf_decal/sand/plating, /obj/item/stack/sheet/glass, @@ -1287,6 +1256,18 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"asc" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "asj" = ( /turf/closed/wall/rust, /area/station/maintenance/department/electrical) @@ -1318,18 +1299,6 @@ "asZ" = ( /turf/closed/wall/r_wall, /area/station/ai/satellite/chamber) -"atc" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/effect/mapping_helpers/burnt_floor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "ate" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -1421,6 +1390,22 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) +"avl" = ( +/obj/machinery/button/flasher{ + id = "visitorflash"; + pixel_x = -6; + pixel_y = 24; + req_access = list("brig") + }, +/obj/machinery/button/door/directional/north{ + id = "visitation"; + name = "Visitation Shutters"; + pixel_x = 8; + req_access = list("brig") + }, +/obj/machinery/light/dim/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/security/execution/transfer) "avo" = ( /obj/structure/sign/warning/biohazard, /turf/closed/wall/rust, @@ -1436,6 +1421,11 @@ /obj/item/stack/license_plates/empty/fifty, /turf/open/floor/plating, /area/station/security/prison) +"avx" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/end, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "avy" = ( /obj/structure/table/wood, /obj/effect/turf_decal/siding/wood{ @@ -1655,15 +1645,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"azg" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark/corner, -/area/station/hallway/primary/port) "azo" = ( /obj/structure/rack, /obj/effect/turf_decal/bot, @@ -1685,16 +1666,6 @@ "aAg" = ( /turf/closed/wall/r_wall/rust, /area/station/ai/satellite/chamber) -"aAq" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "aAu" = ( /turf/closed/wall/rust, /area/station/medical/office) @@ -1763,6 +1734,19 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/atmos) +"aBk" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted, +/obj/effect/turf_decal/tile/purple, +/obj/structure/sign/poster/contraband/lusty_xenomorph/directional/north, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "aBB" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/gibs/robot_debris, @@ -1785,18 +1769,13 @@ /obj/effect/landmark/navigate_destination/dorms, /turf/open/floor/iron/dark, /area/station/commons/locker) -"aBN" = ( -/obj/structure/table, -/obj/item/storage/box/bodybags{ - pixel_y = 5 - }, -/obj/item/pen, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/medbay/lobby) +"aCg" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/purple/half/contrasted, +/obj/effect/turf_decal/tile/purple, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "aCi" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line{ @@ -1836,36 +1815,20 @@ /obj/structure/marker_beacon/burgundy, /turf/open/floor/plating/airless, /area/space/nearstation) -"aDo" = ( -/obj/structure/filingcabinet/chestdrawer, -/obj/effect/turf_decal/bot, -/obj/machinery/firealarm/directional/south, -/obj/machinery/camera/directional/south{ - c_tag = "Recovery Room"; - name = "medical camera"; - network = list("ss13","medical") - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 +"aCN" = ( +/obj/machinery/computer/shuttle/labor{ + dir = 1 }, -/obj/effect/turf_decal/siding/blue{ - dir = 4 +/obj/structure/sign/warning/vacuum/external/directional/south, +/obj/machinery/camera/directional/south{ + c_tag = "Transferring Centre Dock" }, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) -"aDD" = ( -/obj/effect/turf_decal/tile/blue/anticorner/contrasted, -/obj/structure/closet/secure_closet/paramedic, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/red/half/contrasted, +/obj/effect/turf_decal/tile/neutral, /obj/structure/cable, -/obj/machinery/camera/autoname/directional/east, -/obj/machinery/light/directional/east, -/obj/machinery/airalarm/directional/east, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/paramedic) +/turf/open/floor/iron, +/area/station/security/processing) "aDF" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -1888,12 +1851,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/medical/pharmacy) -"aDO" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/light/directional/north, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/central/fore) "aDQ" = ( /turf/open/misc/asteroid, /area/space/nearstation) @@ -2093,6 +2050,29 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"aFh" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 1 + }, +/obj/machinery/iv_drip, +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/loading_area{ + dir = 8 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/treatment_center) +"aFj" = ( +/obj/effect/turf_decal/tile/brown/half/contrasted, +/obj/effect/turf_decal/tile/yellow, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "aFn" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -2134,15 +2114,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) -"aGK" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/service/chapel/dock) "aGM" = ( /obj/structure/chair/stool/directional/south, /obj/effect/landmark/start/atmospheric_technician, @@ -2195,22 +2166,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"aHC" = ( -/obj/machinery/camera/directional/east{ - c_tag = "Cryogenics"; - name = "medical camera"; - network = list("ss13","medical") - }, -/obj/machinery/status_display/evac/directional/east, -/obj/machinery/light/directional/north, -/obj/structure/bed/medical{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/treatment_center) "aHD" = ( /obj/effect/spawner/random/vending/snackvend, /obj/machinery/light/directional/north, @@ -2223,6 +2178,23 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/office) +"aHM" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/item/kirbyplants/organic/plant21, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "aHQ" = ( /obj/effect/turf_decal/delivery, /obj/structure/closet/secure_closet/chief_medical, @@ -2262,12 +2234,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/security/office) -"aIH" = ( -/obj/structure/transit_tube/crossing, -/obj/effect/turf_decal/sand/plating, -/obj/structure/window/reinforced/spawner/directional/south, -/turf/open/floor/plating/airless, -/area/space/nearstation) "aII" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/vending/wardrobe/cargo_wardrobe, @@ -2383,6 +2349,17 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/interior) +"aKU" = ( +/obj/machinery/door/airlock/external{ + name = "Prison External Airlock" + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "kilo-maint-1" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "aKY" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall/r_wall, @@ -2412,15 +2389,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) -"aLC" = ( -/obj/machinery/computer/pandemic, -/obj/effect/turf_decal/delivery, -/obj/item/radio/intercom/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/virology) "aLI" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/brown/half/contrasted{ @@ -2432,6 +2400,18 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/cargo/miningoffice) +"aLJ" = ( +/obj/machinery/computer/security/mining{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/status_display/evac/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/miningdock) "aLK" = ( /obj/structure/closet/crate, /obj/item/hand_labeler, @@ -2660,17 +2640,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/command/gateway) -"aOS" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/sorting/mail/flip{ - dir = 8; - name = "virology sorting disposal pipe" - }, -/obj/effect/mapping_helpers/mail_sorting/medbay/virology, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "aPg" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, @@ -2691,16 +2660,6 @@ }, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"aPr" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "aPx" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 @@ -2797,6 +2756,13 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/command/bridge) +"aRy" = ( +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "aRB" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -2962,6 +2928,17 @@ /obj/structure/flora/rock/pile/style_random, /turf/open/misc/asteroid/airless, /area/space/nearstation) +"aUE" = ( +/obj/machinery/newscaster/directional/south, +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/table/wood/fancy/red, +/obj/structure/sign/painting/large/library{ + dir = 8; + pixel_x = -29 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/station/service/library) "aUU" = ( /obj/structure/table, /obj/machinery/light/small/directional/west, @@ -3065,6 +3042,19 @@ }, /turf/open/floor/iron, /area/station/security/office) +"aWw" = ( +/obj/structure/table, +/obj/item/clipboard, +/obj/item/airlock_painter{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/airlock_painter, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/hallway) "aWD" = ( /turf/closed/wall/r_wall, /area/station/engineering/atmos/storage/gas) @@ -3184,23 +3174,6 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, /area/station/engineering/lobby) -"aYX" = ( -/obj/machinery/button/door/directional/north{ - id = "Biohazard"; - name = "Emergency Research Lockdown"; - pixel_x = -8; - req_access = list("research") - }, -/obj/structure/table, -/obj/machinery/recharger, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/checkpoint/science) "aZe" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/machinery/atmospherics/pipe/bridge_pipe/green/visible, @@ -3297,20 +3270,6 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"baS" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "bbl" = ( /obj/machinery/door/airlock/external{ name = "Abandoned External Airlock" @@ -3342,31 +3301,14 @@ "bbO" = ( /turf/closed/wall/r_wall, /area/station/service/chapel/storage) +"bcm" = ( +/obj/structure/girder, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "bcs" = ( /obj/structure/sign/warning/explosives, /turf/closed/wall/r_wall, /area/station/science/ordnance/bomb) -"bcE" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 8 - }, -/obj/effect/turf_decal/box/red, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/obj/machinery/portable_atmospherics/canister, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) -"bcQ" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/airalarm/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/structure/closet/secure_closet/atmospherics, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "bcY" = ( /obj/structure/table, /obj/machinery/reagentgrinder, @@ -3487,6 +3429,19 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"bfh" = ( +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/command{ + name = "Chief Engineer's Office" + }, +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 4 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/ce, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) "bfA" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -3500,18 +3455,46 @@ }, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) -"bfI" = ( -/obj/machinery/light/directional/west, -/obj/item/radio/intercom/directional/west, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 8 +"bfQ" = ( +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 }, -/obj/effect/turf_decal/tile/yellow{ +/obj/structure/cable, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ dir = 1 }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/effect/turf_decal/box, /turf/open/floor/iron, -/area/station/cargo/storage) +/area/station/hallway/secondary/exit/departure_lounge) +"bgj" = ( +/obj/structure/table, +/obj/structure/extinguisher_cabinet/directional/east, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/item/clothing/glasses/hud/security/sunglasses/gars{ + pixel_x = 3; + pixel_y = -2 + }, +/obj/item/clothing/glasses/hud/security/sunglasses/gars{ + pixel_x = 3; + pixel_y = 2 + }, +/obj/item/clothing/glasses/hud/security/sunglasses{ + pixel_x = -3; + pixel_y = 2 + }, +/obj/item/clothing/glasses/hud/security/sunglasses{ + pixel_x = -3; + pixel_y = -2 + }, +/obj/machinery/light/small/red/directional/south, +/turf/open/floor/iron/dark, +/area/station/security/armory) "bgl" = ( /turf/closed/wall/r_wall, /area/station/command/heads_quarters/hop) @@ -3546,13 +3529,6 @@ }, /turf/open/floor/plating, /area/station/science/genetics) -"bht" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/purple/half/contrasted, -/obj/effect/turf_decal/tile/purple, -/obj/machinery/light/small/directional/south, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) "bhB" = ( /obj/machinery/atmospherics/components/unary/thermomachine/freezer/on, /obj/effect/turf_decal/delivery, @@ -3639,11 +3615,6 @@ /obj/structure/sign/warning/radiation/directional/east, /turf/open/floor/iron/dark, /area/station/engineering/main) -"bji" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/wall_healer/directional/south, -/turf/open/floor/iron, -/area/station/hallway/primary/central/fore) "bjz" = ( /obj/structure/flora/grass/jungle/a/style_random, /turf/open/misc/asteroid, @@ -3728,6 +3699,15 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) +"blm" = ( +/obj/effect/landmark/blobstart, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, +/turf/open/floor/iron/dark/telecomms, +/area/station/science/server) "bln" = ( /obj/structure/table/reinforced, /obj/machinery/door/firedoor, @@ -3763,6 +3743,16 @@ /obj/effect/landmark/navigate_destination/dockarrival, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) +"bly" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "blA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -3857,6 +3847,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/service/chapel/storage) +"bnS" = ( +/obj/machinery/teleport/station, +/obj/machinery/status_display/evac/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/foyer) "bnT" = ( /obj/effect/turf_decal/stripes/end{ dir = 1 @@ -3880,6 +3878,16 @@ /obj/structure/cable, /turf/open/space/basic, /area/space/nearstation) +"boj" = ( +/obj/item/clothing/head/helmet/toggleable/justice/escape{ + name = "justice helmet" + }, +/obj/structure/sign/poster/official/the_owl/directional/north, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/item/clothing/suit/costume/justice, +/turf/open/floor/plating, +/area/station/maintenance/aft) "boo" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, /obj/machinery/door/window/left/directional/north{ @@ -4006,18 +4014,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"bpU" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "bpV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -4089,15 +4085,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/storage/tech) -"brz" = ( -/obj/effect/turf_decal/box/corners{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "brA" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/light/directional/west, @@ -4123,16 +4110,14 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) -"brK" = ( -/obj/machinery/gulag_teleporter, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/stripes/corner, -/turf/open/floor/iron/dark, -/area/station/security/processing) -"brW" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/grass, -/area/station/science/auxlab) +"brG" = ( +/obj/structure/cable, +/obj/machinery/power/terminal{ + dir = 8 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/catwalk_floor/iron_smooth, +/area/station/maintenance/department/chapel/monastery) "bsa" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, @@ -4301,6 +4286,21 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"btQ" = ( +/obj/effect/decal/cleanable/blood/oil, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, +/obj/machinery/light/small/broken/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/chapel/storage) +"buf" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/spawner/xmastree, +/obj/effect/landmark/event_spawn, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "buk" = ( /obj/machinery/camera/directional/north{ c_tag = "Atmospherics Tank - O2"; @@ -4335,21 +4335,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/cmo) -"buS" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/turf/open/floor/iron/dark, -/area/station/service/chapel/dock) -"bvk" = ( -/obj/effect/landmark/secequipment, -/obj/effect/turf_decal/bot, -/obj/machinery/status_display/evac/directional/north, -/obj/machinery/light/small/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/security/office) "bvA" = ( /obj/structure/chair/comfy/brown{ color = "#c45c57"; @@ -4384,17 +4369,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"bwc" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/decal/cleanable/dirt, -/obj/effect/landmark/event_spawn, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "bwk" = ( /obj/machinery/holopad, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -4416,6 +4390,13 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/brig) +"bwF" = ( +/obj/machinery/newscaster/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/science/lobby) "bwN" = ( /obj/effect/turf_decal/box/white, /obj/effect/turf_decal/arrows/white{ @@ -4472,10 +4453,40 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"bya" = ( +/obj/item/kirbyplants/organic/plant2{ + pixel_y = 3 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/firealarm/directional/north, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/processing) "byb" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/department/cargo) +"byd" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/vending/cigarette, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/aft) "bys" = ( /obj/effect/turf_decal/siding/blue{ dir = 5 @@ -4522,16 +4533,6 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/aft) -"bzb" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/plating/airless, -/area/space/nearstation) "bzc" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron, @@ -4568,21 +4569,6 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/dark, /area/station/service/chapel/dock) -"bzy" = ( -/obj/structure/closet/radiation, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/maintenance/disposal/incinerator) -"bzS" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/firealarm/directional/south, -/obj/item/kirbyplants/organic/plant21, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "bAc" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible{ dir = 4 @@ -4597,39 +4583,6 @@ /obj/structure/sign/departments/security, /turf/closed/wall, /area/station/maintenance/fore) -"bAs" = ( -/obj/structure/table/wood/fancy/black, -/obj/item/food/grown/poppy/geranium{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/geranium{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/geranium, -/obj/machinery/firealarm/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/service/chapel) -"bAQ" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/shower/directional/east{ - name = "emergency shower" - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/structure/extinguisher_cabinet/directional/west, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/small/directional/south, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/pharmacy) "bAS" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/firealarm/directional/west, @@ -4650,6 +4603,11 @@ /obj/effect/mapping_helpers/airlock/access/any/medical/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"bBb" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "bBe" = ( /obj/machinery/airalarm/directional/south, /obj/effect/turf_decal/bot, @@ -4705,6 +4663,22 @@ /obj/item/seeds/watermelon, /turf/open/floor/grass, /area/station/security/prison/garden) +"bCe" = ( +/obj/structure/table, +/obj/item/hfr_box/core, +/obj/item/hfr_box/corner, +/obj/item/hfr_box/corner, +/obj/item/hfr_box/corner, +/obj/item/hfr_box/corner, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "bCz" = ( /obj/structure/table/reinforced, /obj/machinery/door/firedoor, @@ -4756,12 +4730,6 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/port) -"bDp" = ( -/obj/effect/turf_decal/bot, -/obj/structure/tank_dispenser/oxygen, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "bDr" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -4866,13 +4834,6 @@ }, /turf/open/floor/plating/rust, /area/station/maintenance/department/crew_quarters/bar) -"bEv" = ( -/obj/machinery/light/directional/west, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "bFm" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -4913,6 +4874,24 @@ }, /turf/open/floor/plating/airless, /area/space/nearstation) +"bGa" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/conveyor{ + dir = 1; + id = "QMLoad"; + name = "off ramp" + }, +/obj/machinery/camera/directional/east{ + c_tag = "Cargo Ramps"; + name = "cargo camera"; + network = list("ss13","qm") + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark, +/area/station/cargo/storage) "bGj" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -4950,6 +4929,14 @@ /obj/structure/cable/layer3, /turf/open/floor/engine, /area/station/ai/satellite/chamber) +"bGK" = ( +/obj/structure/table/reinforced/plastitaniumglass, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/cell_charger, +/obj/item/stock_parts/power_store/cell, +/obj/machinery/light/small/broken/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/chapel/storage) "bHj" = ( /obj/structure/table, /obj/item/paper_bin/construction{ @@ -5003,25 +4990,6 @@ }, /turf/open/floor/iron, /area/station/command/gateway) -"bHL" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/holopad, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) -"bHM" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 4 - }, -/obj/structure/sink/directional/west, -/obj/item/reagent_containers/cup/watering_can, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/prison/garden) "bHP" = ( /obj/structure/flora/grass/jungle/a/style_random, /obj/structure/chair, @@ -5036,6 +5004,13 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/drone_bay) +"bHT" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/machinery/computer/security/telescreen/entertainment/directional/east, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "bId" = ( /obj/structure/cable, /obj/effect/decal/cleanable/dirt, @@ -5074,6 +5049,12 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/security/execution/transfer) +"bIx" = ( +/obj/effect/landmark/event_spawn, +/obj/machinery/holopad, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/showroomfloor, +/area/station/security/execution/transfer) "bIR" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/burnt_floor, @@ -5136,12 +5117,6 @@ "bKc" = ( /turf/closed/wall, /area/station/maintenance/department/medical/central) -"bKj" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "bKl" = ( /obj/machinery/door/airlock/virology{ name = "Virology Isolation B" @@ -5151,15 +5126,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/medical/virology) -"bKM" = ( -/obj/machinery/light/directional/west, -/obj/machinery/power/emitter/welded{ +"bKH" = ( +/obj/structure/table, +/obj/item/storage/toolbox/electrical{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/storage/toolbox/electrical, +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 }, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/engineering/supermatter/room) +/turf/open/floor/iron/dark, +/area/station/commons/storage/primary) "bKU" = ( /obj/effect/turf_decal/bot, /obj/machinery/airalarm/directional/north, @@ -5175,6 +5154,14 @@ /obj/effect/turf_decal/tile/purple/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) +"bLu" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Security Maintenance" + }, +/obj/effect/mapping_helpers/airlock/access/all/security/general, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/maintenance/aft) "bLF" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/disposalpipe/segment{ @@ -5185,19 +5172,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"bLN" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/blood/gibs/up, -/obj/structure/sign/poster/official/wtf_is_co2/directional/north, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/light/small/directional/north, -/obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/plating, -/area/station/security/execution/education) "bLR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -5226,6 +5200,18 @@ /obj/item/toy/basketball, /turf/open/floor/iron, /area/station/security/prison) +"bMD" = ( +/obj/machinery/atmospherics/components/binary/tank_compressor{ + dir = 8 + }, +/obj/effect/turf_decal/delivery, +/obj/structure/sign/poster/random/directional/north, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "bMH" = ( /obj/structure/extinguisher_cabinet/directional/south, /obj/effect/turf_decal/delivery, @@ -5250,17 +5236,17 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"bNg" = ( +/obj/structure/flora/bush/flowers_br/style_random, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/grass, +/area/station/service/chapel) "bNl" = ( /obj/docking_port/stationary/escape_pod{ dir = 4 }, /turf/open/space/basic, /area/space) -"bNv" = ( -/obj/machinery/telecomms/server/presets/common, -/obj/machinery/light/directional/west, -/turf/open/floor/circuit/green/telecomms/mainframe, -/area/station/tcommsat/server) "bNz" = ( /obj/effect/turf_decal/box/corners{ dir = 1 @@ -5274,12 +5260,6 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"bNM" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line, -/obj/structure/cable, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "bNO" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -5287,6 +5267,32 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/security/prison/safe) +"bOm" = ( +/obj/item/storage/fancy/cigarettes/cigars{ + pixel_x = 2; + pixel_y = 6 + }, +/obj/item/storage/fancy/cigarettes/cigars/havana{ + pixel_x = 2; + pixel_y = 8 + }, +/obj/item/storage/fancy/cigarettes/cigars/cohiba{ + pixel_x = 2; + pixel_y = 10 + }, +/obj/item/holosign_creator/robot_seat/bar, +/obj/machinery/status_display/evac/directional/west, +/obj/structure/table, +/obj/machinery/camera/directional/west{ + c_tag = "Bar"; + name = "bar camera" + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/dark, +/area/station/service/bar) "bOD" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -5353,20 +5359,6 @@ /obj/structure/flora/bush/flowers_yw/style_random, /turf/open/floor/grass, /area/station/service/hydroponics) -"bQn" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/command/heads_quarters/rd) "bQD" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/cleanable/dirt, @@ -5411,10 +5403,6 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/plating, /area/station/maintenance/department/bridge) -"bRh" = ( -/obj/structure/transit_tube/diagonal/topleft, -/turf/open/space/basic, -/area/space/nearstation) "bRo" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -5513,6 +5501,23 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/fore) +"bST" = ( +/obj/machinery/ai_slipper{ + uses = 10 + }, +/obj/effect/turf_decal/box, +/obj/machinery/flasher/directional/north{ + id = "AI"; + name = "Meatbag Pacifier"; + pixel_x = 26 + }, +/obj/machinery/camera/directional/south{ + c_tag = "AI Chamber Core"; + name = "core camera"; + network = list("aicore") + }, +/turf/open/floor/engine, +/area/station/ai/satellite/chamber) "bSX" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -5523,19 +5528,31 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"bTe" = ( -/obj/machinery/computer/communications{ +"bTC" = ( +/obj/machinery/airalarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 1 }, -/obj/structure/plaque/static_plaque/golden/captain{ - pixel_y = -32 +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 }, /obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/green/half/contrasted, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) +"bTF" = ( +/obj/effect/turf_decal/bot, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/machinery/computer/operating{ + dir = 1 + }, /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 1 }, +/obj/machinery/vitals_reader/directional/south, /turf/open/floor/iron/dark, -/area/station/command/heads_quarters/captain) +/area/station/medical/surgery/theatre) "bUd" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -5700,46 +5717,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/closed/wall/r_wall, /area/station/engineering/supermatter/room) -"bWI" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/blood/old, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/airlock_pump, -/turf/open/floor/iron/dark, -/area/station/maintenance/fore) -"bWP" = ( -/obj/structure/table, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = 10 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = -2 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = 4 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = -8 - }, -/obj/item/clothing/under/rank/prisoner{ - pixel_x = 8; - pixel_y = 5 - }, -/obj/machinery/camera/directional/north{ - c_tag = "Prison Visitation"; - network = list("ss13","prison") - }, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) "bWZ" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -5836,21 +5813,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) -"bXS" = ( -/obj/machinery/door/airlock/external{ - name = "Prison External Airlock" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 4 - }, -/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/maintenance/port/lesser) "bXX" = ( /obj/structure/cable, /obj/effect/decal/cleanable/dirt, @@ -5865,22 +5827,20 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plating/airless, /area/station/hallway/secondary/entry) -"bYK" = ( -/obj/machinery/button/flasher{ - id = "visitorflash"; - pixel_x = -6; - pixel_y = 24; - req_access = list("brig") - }, -/obj/machinery/holopad, -/obj/machinery/button/door/directional/north{ - id = "visitation"; - name = "Visitation Shutters"; - pixel_x = 8; - req_access = list("brig") +"bYg" = ( +/obj/machinery/firealarm/directional/west, +/obj/machinery/computer/atmos_control/ordnancemix{ + dir = 4 }, /turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) +/area/station/science/ordnance) +"bYT" = ( +/obj/effect/turf_decal/bot, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/machinery/power/shieldwallgen, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/command/teleporter) "bZg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -5920,6 +5880,16 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/funeral) +"bZS" = ( +/obj/structure/rack, +/obj/effect/spawner/random/techstorage/rnd_secure_all, +/obj/machinery/status_display/shuttle{ + pixel_y = -32 + }, +/turf/open/floor/circuit/green{ + luminosity = 2 + }, +/area/station/engineering/storage/tech) "bZY" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/structure/crate, @@ -6036,15 +6006,6 @@ "cbL" = ( /turf/closed/wall/rust, /area/station/science) -"ccd" = ( -/obj/structure/railing{ - dir = 4 - }, -/obj/effect/turf_decal/siding/wideplating/dark/corner{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "ccf" = ( /obj/effect/turf_decal/stripes/line{ dir = 6 @@ -6065,6 +6026,19 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/interior) +"ccw" = ( +/obj/structure/closet/secure_closet/security/engine, +/obj/effect/turf_decal/delivery, +/obj/structure/reagent_dispensers/wall/peppertank/directional/south, +/obj/item/crowbar, +/obj/item/book/manual/wiki/security_space_law, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/security/checkpoint/engineering) "ccz" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/old, @@ -6093,6 +6067,19 @@ }, /turf/open/floor/iron, /area/station/engineering/main) +"ccS" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 8 + }, +/turf/open/floor/plating, +/area/station/maintenance/solars/starboard/fore) "ccW" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -6144,13 +6131,6 @@ /obj/effect/turf_decal/box, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"cdt" = ( -/obj/machinery/status_display/evac/directional/south, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/machinery/light/very_dim/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "cdu" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -6286,18 +6266,6 @@ }, /turf/open/floor/plating, /area/station/ai/satellite/chamber) -"ces" = ( -/obj/machinery/light/directional/north, -/obj/structure/table, -/obj/item/food/ready_donk/donkrange_chicken, -/obj/item/food/ready_donk/donkhiladas{ - pixel_y = 5 - }, -/obj/item/food/ready_donk/country_chicken{ - pixel_y = 10 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/miningoffice) "cev" = ( /obj/structure/transit_tube/diagonal/topleft, /obj/structure/lattice, @@ -6334,15 +6302,15 @@ "ceG" = ( /turf/closed/wall/r_wall/rust, /area/station/ai/satellite/atmos) -"ceM" = ( -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 +"ceO" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 }, -/turf/open/floor/iron, -/area/station/commons/fitness/recreation) +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "ceT" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible{ dir = 1 @@ -6359,6 +6327,21 @@ /obj/machinery/recharge_station, /turf/open/floor/iron/dark, /area/station/engineering/storage_shared) +"ceY" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "cfm" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/girder/displaced, @@ -6366,22 +6349,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"cfs" = ( -/obj/machinery/camera/directional/east{ - c_tag = "Supermatter Waste Line"; - name = "engineering camera"; - network = list("ss13","engine") - }, -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/button/door/directional/east{ - id = "engineaccess"; - name = "Engine Access Lockdown"; - req_access = list("engineering") - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/machinery/duct, -/turf/open/floor/engine, -/area/station/engineering/supermatter/room) "cfu" = ( /obj/structure/sign/warning/xeno_mining, /turf/closed/wall, @@ -6431,13 +6398,6 @@ }, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) -"cgb" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/status_display/evac/directional/north, -/turf/open/floor/engine, -/area/station/engineering/supermatter/room) "cgj" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/duct, @@ -6454,6 +6414,15 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/interior) +"cgr" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/airalarm/directional/north, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron, +/area/station/security/prison/garden) "cgH" = ( /obj/item/storage/medkit/regular{ pixel_x = 3; @@ -6624,18 +6593,26 @@ }, /turf/open/floor/engine, /area/station/ai/satellite/foyer) +"cir" = ( +/obj/structure/cable, +/obj/machinery/wall_healer/directional/north, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "cit" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 }, /turf/open/floor/engine, /area/station/ai/satellite/foyer) -"civ" = ( -/obj/effect/spawner/random/structure/girder, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/maintenance/central) "ciA" = ( /obj/effect/turf_decal/tile/blue, /obj/effect/turf_decal/tile/blue/half/contrasted{ @@ -6676,17 +6653,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet/royalblue, /area/station/service/chapel/office) -"ciT" = ( -/obj/structure/rack, -/obj/effect/turf_decal/bot, -/obj/item/wrench, -/obj/effect/decal/cleanable/cobweb, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/north, -/obj/item/tank/internals/oxygen, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/atmos) "ciW" = ( /obj/effect/turf_decal/siding/wideplating_new/dark, /turf/open/floor/grass, @@ -6769,18 +6735,6 @@ }, /turf/open/floor/glass/reinforced, /area/station/service/chapel) -"cjS" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/structure/table, -/obj/item/soap/nanotrasen, -/obj/item/hand_labeler, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/medbay/central) "cka" = ( /obj/structure/railing{ dir = 1 @@ -6882,14 +6836,50 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/ai/satellite/foyer) -"cln" = ( -/obj/structure/railing/corner{ +"clo" = ( +/obj/structure/extinguisher_cabinet/directional/south, +/obj/structure/table, +/obj/item/stock_parts/micro_laser{ + pixel_x = -7; + pixel_y = -3 + }, +/obj/item/stock_parts/micro_laser{ + pixel_x = -1; + pixel_y = 3 + }, +/obj/item/stock_parts/micro_laser{ + pixel_x = -5; + pixel_y = 4 + }, +/obj/item/stock_parts/micro_laser{ + pixel_x = -3; + pixel_y = -2 + }, +/obj/item/stock_parts/scanning_module{ + pixel_x = 12; + pixel_y = -1 + }, +/obj/item/stock_parts/scanning_module{ + pixel_x = 10; + pixel_y = 1 + }, +/obj/item/stock_parts/scanning_module{ + pixel_x = 13; + pixel_y = 4 + }, +/obj/item/stock_parts/scanning_module{ + pixel_x = 8; + pixel_y = -1 + }, +/obj/effect/turf_decal/stripes/corner{ dir = 8 }, -/obj/machinery/light/directional/east, -/obj/item/kirbyplants/random, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, /turf/open/floor/iron/dark, -/area/station/service/chapel) +/area/station/cargo/drone_bay) "clt" = ( /obj/machinery/atmospherics/pipe/smart/manifold/purple/visible, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -6914,21 +6904,6 @@ }, /turf/open/floor/plating/airless, /area/space/nearstation) -"clH" = ( -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ - dir = 1 - }, -/obj/machinery/iv_drip, -/obj/machinery/light/directional/north, -/obj/machinery/airalarm/directional/north, -/obj/effect/turf_decal/loading_area{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/treatment_center) "clL" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line, @@ -6944,6 +6919,18 @@ }, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) +"clM" = ( +/obj/item/kirbyplants/organic/plant5, +/obj/structure/noticeboard/directional/east, +/obj/item/paper/monitorkey, +/obj/machinery/camera/directional/east{ + c_tag = "Chief Engineer's Office"; + name = "engineering camera"; + network = list("ss13","engine") + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) "clN" = ( /obj/structure/chair/office{ dir = 1 @@ -6995,13 +6982,6 @@ }, /turf/open/floor/plating/airless, /area/space/nearstation) -"cmc" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Security Maintenance" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/general, -/turf/open/floor/iron/dark, -/area/station/maintenance/aft) "cml" = ( /turf/closed/wall/rust, /area/station/service/library) @@ -7021,6 +7001,12 @@ "cmQ" = ( /turf/open/floor/cult, /area/station/service/chapel/office) +"cmR" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "cmU" = ( /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating/airless, @@ -7142,15 +7128,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"cpi" = ( -/obj/machinery/door/airlock/public/glass{ - name = "Auxiliary Dock" - }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "cpC" = ( /obj/effect/turf_decal/stripes/line{ dir = 6 @@ -7163,6 +7140,14 @@ /obj/machinery/status_display/evac/directional/south, /turf/open/floor/plating, /area/station/maintenance/solars/port/aft) +"cpW" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/south, +/obj/effect/mapping_helpers/apc/cell_10k, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/engineering/supermatter/room) "cpY" = ( /obj/item/pickaxe, /turf/open/floor/plating/airless, @@ -7178,14 +7163,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"cqc" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 1 - }, -/obj/machinery/portable_atmospherics/canister, -/obj/effect/turf_decal/box, -/turf/open/floor/iron/dark, -/area/station/engineering/supermatter/room) "cqm" = ( /obj/structure/table, /obj/item/clipboard, @@ -7220,6 +7197,16 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) +"cqD" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/prison/garden) "cqE" = ( /obj/structure/flora/bush/fullgrass/style_random, /turf/open/floor/grass, @@ -7358,19 +7345,6 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"csL" = ( -/obj/structure/table/wood/fancy/black, -/obj/item/food/grown/poppy/lily{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/lily{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/lily, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) "csM" = ( /obj/structure/table, /obj/machinery/recharger, @@ -7423,14 +7397,6 @@ /obj/effect/decal/cleanable/blood/oil, /turf/open/misc/asteroid/airless, /area/station/hallway/secondary/entry) -"ctv" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/holopad, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/warden) "ctx" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/purple{ @@ -7452,22 +7418,6 @@ /obj/effect/spawner/random/structure/crate, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"cua" = ( -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) -"cud" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/obj/machinery/portable_atmospherics/canister, -/obj/effect/turf_decal/box, -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "cup" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -7482,6 +7432,18 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/armory) +"cuB" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/camera/directional/north{ + c_tag = "Head of Security's Office" + }, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/machinery/photocopier, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/hos) "cuD" = ( /obj/structure/sign/departments/security/directional/south, /obj/structure/flora/grass/jungle/b/style_random, @@ -7599,16 +7561,27 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) -"cwj" = ( -/obj/machinery/modular_computer/preset/id, -/obj/effect/turf_decal/bot, -/obj/machinery/computer/security/telescreen/ce/directional/north, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) "cwl" = ( /obj/structure/girder, /turf/open/floor/plating, /area/station/maintenance/department/security) +"cxa" = ( +/obj/machinery/computer/records/security{ + dir = 1 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/security/checkpoint/medical) +"cxB" = ( +/obj/machinery/computer/station_alert, +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/north, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) "cxO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -7631,18 +7604,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/armory) -"cxR" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/burnt_floor, -/obj/effect/spawner/random/structure/steam_vent, -/turf/open/floor/plating, -/area/station/maintenance/starboard) -"cyc" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "cyp" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -7731,6 +7692,19 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"czR" = ( +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/structure/table/glass, +/obj/machinery/microwave{ + pixel_y = 5 + }, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/break_room) "czT" = ( /obj/structure/cable, /obj/effect/spawner/structure/window/reinforced, @@ -7748,6 +7722,31 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"cAk" = ( +/obj/structure/table, +/obj/item/stack/sheet/plasteel/fifty{ + amount = 10; + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/stack/sheet/rglass{ + amount = 50 + }, +/obj/item/stack/sheet/rglass{ + amount = 50 + }, +/obj/item/clothing/shoes/magboots{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clothing/shoes/magboots, +/obj/machinery/airalarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/cold/dim/directional/south, +/turf/open/floor/iron/dark, +/area/station/command/eva) "cAl" = ( /obj/effect/landmark/start/scientist, /obj/effect/turf_decal/tile/purple/anticorner/contrasted{ @@ -7792,21 +7791,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/port) -"cBc" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/computer/security/labor, -/obj/machinery/requests_console/directional/north{ - department = "Security"; - name = "Security Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/supplies, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/machinery/light/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/cobweb, -/obj/machinery/airalarm/directional/west, -/turf/open/floor/iron/dark, -/area/station/security/checkpoint/supply) "cBD" = ( /obj/structure/flora/grass/jungle/a/style_random, /obj/effect/turf_decal/sand/plating, @@ -7957,6 +7941,18 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/command/heads_quarters/hos) +"cGd" = ( +/obj/structure/table, +/obj/item/storage/box/bodybags{ + pixel_y = 5 + }, +/obj/item/pen, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/cold/directional/south, +/turf/open/floor/iron/dark, +/area/station/medical/medbay/lobby) "cGl" = ( /obj/machinery/disposal/bin/tagger, /obj/effect/turf_decal/bot, @@ -7974,19 +7970,6 @@ /obj/item/analyzer, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"cGo" = ( -/obj/structure/table, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/construction/plumbing, -/obj/item/construction/plumbing, -/obj/machinery/light_switch/directional/north, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) "cGr" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple/layer2{ dir = 8 @@ -8043,6 +8026,14 @@ /obj/structure/lattice/catwalk, /turf/open/floor/plating/airless, /area/space/nearstation) +"cHv" = ( +/obj/effect/turf_decal/tile/brown/half/contrasted, +/obj/effect/turf_decal/tile/yellow, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "cHz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -8057,21 +8048,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) -"cHG" = ( -/obj/machinery/conveyor{ - dir = 4; - id = "packagereturn"; - name = "crate return belt" - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/south, -/obj/machinery/camera/directional/south{ - c_tag = "Delivery Office"; - name = "cargo camera"; - network = list("ss13","qm") - }, -/turf/open/floor/iron/dark, -/area/station/cargo/sorting) "cHI" = ( /obj/structure/table, /obj/item/paper_bin{ @@ -8385,30 +8361,6 @@ /obj/effect/turf_decal/tile/yellow/anticorner/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) -"cMV" = ( -/obj/machinery/light/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ - dir = 9 - }, -/obj/machinery/computer/turbine_computer{ - dir = 1; - mapping_id = "main_turbine" - }, -/turf/open/floor/engine, -/area/station/maintenance/disposal/incinerator) -"cNP" = ( -/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line, -/obj/structure/sign/warning/vacuum/external/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/duct, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "cNZ" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/turf_decal/sand/plating, @@ -8438,14 +8390,6 @@ /obj/structure/sign/warning/docking, /turf/closed/wall/rust, /area/space/nearstation) -"cOt" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/green/anticorner/contrasted{ - dir = 8 - }, -/obj/machinery/light/cold/directional/south, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "cON" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -8458,12 +8402,6 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) -"cOR" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/service/chapel/dock) "cOT" = ( /obj/effect/landmark/navigate_destination/research, /obj/machinery/door/airlock/public/glass{ @@ -8483,25 +8421,6 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) -"cPu" = ( -/obj/machinery/light/directional/west, -/obj/machinery/computer/records/security{ - dir = 4 - }, -/obj/machinery/requests_console/directional/north{ - department = "Security"; - name = "Security Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/supplies, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/checkpoint/science) "cPw" = ( /obj/effect/turf_decal/bot, /turf/open/floor/iron, @@ -8560,6 +8479,11 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/starboard) +"cQN" = ( +/obj/effect/spawner/structure/window/hollow/reinforced/middle, +/obj/structure/transit_tube, +/turf/open/floor/plating, +/area/space/nearstation) "cQR" = ( /turf/closed/wall, /area/station/maintenance/department/cargo) @@ -8588,18 +8512,15 @@ "cRv" = ( /turf/closed/wall, /area/station/service/chapel/monastery) -"cRG" = ( -/obj/machinery/light/directional/east, -/obj/machinery/status_display/ai/directional/east, +"cRM" = ( +/obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/components/tank/air/layer4{ - dir = 8 + dir = 1 }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron, -/area/station/security/processing) +/area/station/hallway/secondary/exit/departure_lounge) "cRO" = ( /obj/item/toy/figure/qm{ pixel_x = -17 @@ -8618,6 +8539,21 @@ }, /turf/open/floor/carpet/orange, /area/station/command/heads_quarters/qm) +"cRQ" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) +"cRV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/blood/splatter/oil, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/storage) "cRW" = ( /obj/machinery/door/poddoor/shutters/preopen{ dir = 4; @@ -8627,6 +8563,19 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/medical/pharmacy) +"cSj" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner, +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/landmark/firealarm_sanity, +/turf/open/floor/iron/dark, +/area/station/maintenance/port/greater) "cSl" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/firealarm/directional/north, @@ -8675,6 +8624,26 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/security/warden) +"cSZ" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "cTh" = ( /obj/machinery/porta_turret/ai{ dir = 1 @@ -8743,10 +8712,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/execution/transfer) -"cUc" = ( -/obj/structure/transit_tube, -/turf/open/space/basic, -/area/space/nearstation) "cUk" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -8804,21 +8769,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/engine, /area/station/ai/satellite/foyer) -"cUY" = ( -/obj/machinery/door/airlock/external{ - name = "Prison External Airlock" - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "kilo-maint-1" - }, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 1 - }, -/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "cVb" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible{ dir = 4 @@ -8833,14 +8783,6 @@ }, /turf/open/floor/engine, /area/station/tcommsat/computer) -"cVn" = ( -/obj/machinery/light/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/machinery/firealarm/directional/south, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "cVr" = ( /obj/structure/sign/warning/vacuum/external, /turf/closed/wall, @@ -8855,21 +8797,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) -"cVI" = ( -/obj/effect/turf_decal/bot, -/obj/structure/closet/secure_closet/personal, -/obj/item/storage/backpack, -/obj/item/storage/backpack/satchel, -/obj/item/clothing/suit/hooded/wintercoat, -/obj/item/clothing/shoes/winterboots, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/small/directional/north, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/locker) "cVS" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line{ @@ -8907,6 +8834,20 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/central/fore) +"cWp" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/gulag_item_reclaimer{ + dir = 8; + pixel_x = 30 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red/anticorner/contrasted, +/obj/effect/turf_decal/tile/neutral, +/obj/structure/cable, +/turf/open/floor/iron, +/area/station/security/processing) "cWu" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -9015,6 +8956,20 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"cXz" = ( +/obj/effect/turf_decal/bot, +/obj/structure/closet/secure_closet/personal, +/obj/item/storage/backpack, +/obj/item/storage/backpack/satchel, +/obj/item/clothing/suit/hooded/wintercoat, +/obj/item/clothing/shoes/winterboots, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/commons/locker) "cXH" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -9119,22 +9074,16 @@ }, /turf/open/floor/iron/dark, /area/station/science/genetics) -"cYG" = ( -/obj/structure/cable, -/obj/structure/chair/sofa/right/maroon{ - dir = 4 - }, -/obj/machinery/light/directional/south, -/obj/structure/railing{ - dir = 8 - }, -/obj/machinery/airalarm/directional/south, +"cYK" = ( +/obj/machinery/flasher/portable, /obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ +/obj/machinery/firealarm/directional/north, +/obj/effect/turf_decal/box, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ dir = 8 }, /turf/open/floor/iron/dark, -/area/station/service/bar/atrium) +/area/station/security/armory) "cYN" = ( /obj/structure/chair{ dir = 8 @@ -9173,6 +9122,16 @@ }, /turf/open/floor/iron, /area/station/cargo/miningdock) +"cZN" = ( +/obj/effect/turf_decal/siding/red{ + dir = 9 + }, +/obj/effect/turf_decal/tile/red/anticorner/contrasted{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "cZT" = ( /obj/effect/turf_decal/bot, /obj/machinery/light/small/directional/south, @@ -9213,32 +9172,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos) -"dbi" = ( -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -5 - }, -/obj/item/pen{ - pixel_x = -5 - }, -/obj/machinery/light/directional/east, -/obj/machinery/flasher/directional/east{ - id = "hopflash"; - name = "Crowd Pacifier" - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/status_display/evac/directional/north, -/obj/structure/desk_bell{ - pixel_x = 7; - pixel_y = 10 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/hop) "dbp" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -9311,6 +9244,15 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/service/library) +"dcA" = ( +/obj/structure/sign/warning/biohazard/directional/east, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "dcD" = ( /obj/structure/table, /obj/item/surgical_drapes, @@ -9445,20 +9387,23 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron, /area/station/cargo/miningdock) -"deG" = ( -/obj/structure/table, -/obj/item/storage/toolbox/electrical{ - pixel_x = -4; +"deF" = ( +/obj/machinery/portable_atmospherics/canister/air{ + pixel_x = 4 + }, +/obj/item/clothing/mask/breath{ + pixel_x = 4; pixel_y = 4 }, -/obj/item/storage/toolbox/electrical, -/obj/machinery/light/directional/west, -/obj/machinery/firealarm/directional/west, +/obj/structure/sign/poster/contraband/random/directional/north, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer4{ + dir = 8 }, /turf/open/floor/iron/dark, -/area/station/commons/storage/primary) +/area/station/hallway/secondary/entry) "deI" = ( /obj/effect/turf_decal/bot, /obj/structure/grille, @@ -9469,6 +9414,15 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"dfc" = ( +/obj/machinery/status_display/ai/directional/west, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/cable/layer3, +/turf/open/floor/engine, +/area/station/ai/satellite/chamber) "dfi" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -9532,11 +9486,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/service/hydroponics) -"dfV" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "dfW" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/landmark/start/chief_medical_officer, @@ -9552,18 +9501,6 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) -"dgd" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "atmos_end_big_lad" - }, -/obj/machinery/door/airlock/external{ - name = "Atmospherics External Airlock" - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/atmos, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/plating, -/area/station/maintenance/disposal/incinerator) "dgg" = ( /obj/effect/turf_decal/delivery, /obj/machinery/pipedispenser/disposal, @@ -9594,6 +9531,16 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"dgp" = ( +/obj/structure/closet/emcloset/anchored, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/plating, +/area/station/service/chapel) "dgu" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -9636,6 +9583,24 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"dgG" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible/layer2, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/engineering/hallway) "dgP" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/machinery/atmospherics/pipe/bridge_pipe/green/visible{ @@ -9688,6 +9653,12 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"dhG" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/holopad, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron, +/area/station/cargo/sorting) "dhL" = ( /obj/docking_port/stationary{ dir = 4; @@ -9714,19 +9685,6 @@ }, /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) -"die" = ( -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/effect/turf_decal/delivery, -/obj/structure/table, -/obj/item/storage/toolbox/electrical{ - pixel_y = 3 - }, -/obj/item/multitool{ - pixel_x = -4; - pixel_y = 2 - }, -/turf/open/floor/iron, -/area/station/engineering/lobby) "dif" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -9771,6 +9729,19 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/engineering/circuit_workshop) +"diP" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/defibrillator_mount/directional/west, +/obj/machinery/stasis{ + dir = 4 + }, +/obj/effect/turf_decal/box, +/obj/machinery/vitals_reader/directional/north, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/medical/treatment_center) "diQ" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/stripes/line{ @@ -9835,16 +9806,6 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"djQ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "djW" = ( /obj/machinery/airalarm/directional/south, /obj/machinery/camera/directional/south{ @@ -9865,6 +9826,13 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"dka" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "dke" = ( /obj/machinery/firealarm/directional/west, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -9900,6 +9868,14 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos) +"dkE" = ( +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/security/glass{ + name = "Security Checkpoint" + }, +/obj/effect/mapping_helpers/airlock/access/all/security/general, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/aft) "dkH" = ( /obj/structure/cable, /obj/machinery/duct, @@ -9972,6 +9948,18 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) +"dlO" = ( +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 4 + }, +/obj/structure/table/glass, +/obj/structure/cable, +/obj/structure/fireaxecabinet/jawsofrecovery/directional/east, +/obj/item/surgicaldrill, +/obj/item/toy/figure/paramedic, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/paramedic) "dlS" = ( /obj/machinery/light/floor, /turf/open/floor/engine/vacuum, @@ -10046,35 +10034,6 @@ /obj/effect/spawner/random/maintenance/no_decals, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"dna" = ( -/obj/structure/table, -/obj/item/folder{ - pixel_x = -4 - }, -/obj/item/pai_card, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) -"dnl" = ( -/obj/machinery/light/directional/north, -/obj/machinery/firealarm/directional/north, -/obj/machinery/camera/directional/north{ - c_tag = "Arrivals Dock"; - name = "shuttle camera" - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/structure/closet/firecloset, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "dnt" = ( /obj/effect/landmark/carpspawn, /obj/structure/cable, @@ -10101,10 +10060,6 @@ /obj/item/wallframe/apc, /turf/open/floor/iron/dark, /area/station/maintenance/port/fore) -"dnF" = ( -/obj/effect/landmark/event_spawn, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) "dnL" = ( /obj/structure/table, /obj/effect/turf_decal/stripes/line{ @@ -10190,18 +10145,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/science/xenobiology) -"doB" = ( -/obj/machinery/atmospherics/components/trinary/filter{ - dir = 4 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/delivery, -/obj/machinery/light_switch/directional/north, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) "doH" = ( /obj/effect/turf_decal/stripes/corner, /obj/effect/turf_decal/stripes/corner{ @@ -10214,14 +10157,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/fore) -"doU" = ( -/obj/machinery/shower/directional/west, -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/obj/effect/turf_decal/box, -/turf/open/floor/iron/dark, -/area/station/engineering/main) "dpc" = ( /obj/effect/turf_decal/siding/thinplating/dark/end{ dir = 4 @@ -10268,38 +10203,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"dpR" = ( -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/main) -"dpT" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/status_display/evac/directional/north, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) -"dqa" = ( -/obj/item/kirbyplants/organic/plant21, -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/turf/open/floor/iron/dark/corner, -/area/station/hallway/primary/central/fore) "dqg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -10310,6 +10213,36 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"dqh" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/closet/firecloset, +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/turf/open/floor/plating, +/area/station/hallway/secondary/exit/departure_lounge) +"dqk" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/end{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) +"dqq" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron/dark, +/area/station/command/bridge) +"dqv" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "dqw" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -10327,6 +10260,16 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"dqJ" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/computer/security/telescreen/monastery/directional/south, +/turf/open/floor/iron/grimy, +/area/station/hallway/primary/fore) "dqL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -10418,6 +10361,14 @@ /obj/machinery/griddle, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"drX" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "dsc" = ( /obj/machinery/door/airlock/command/glass{ name = "Control Room" @@ -10429,10 +10380,6 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/tcoms, /turf/open/floor/iron/dark, /area/station/tcommsat/computer) -"dsh" = ( -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "dsk" = ( /turf/open/floor/iron/dark, /area/station/command/bridge) @@ -10468,14 +10415,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"dsC" = ( -/obj/machinery/light/cold/directional/east, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "dsD" = ( /obj/structure/sign/departments/cargo, /turf/closed/wall, @@ -10511,10 +10450,6 @@ /obj/effect/turf_decal/tile/green/fourcorners, /turf/open/floor/iron/checker, /area/station/service/hydroponics) -"dtb" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/closed/wall/r_wall, -/area/station/maintenance/disposal/incinerator) "dtg" = ( /obj/machinery/atmospherics/components/unary/outlet_injector/monitored/oxygen_input{ dir = 4 @@ -10541,18 +10476,6 @@ /obj/effect/mapping_helpers/airlock/unres/delayed, /turf/open/floor/iron/dark, /area/station/maintenance/port/fore) -"dtD" = ( -/obj/machinery/light/small/directional/west, -/obj/machinery/newscaster/directional/south, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/table/wood/fancy/red, -/obj/structure/sign/painting/large/library{ - dir = 8; - pixel_x = -29 - }, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) "dtP" = ( /obj/effect/turf_decal/siding/wood{ dir = 9 @@ -10567,15 +10490,6 @@ }, /turf/open/floor/iron/dark, /area/station/commons/storage/primary) -"dup" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/warden) -"duq" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/turf/open/floor/plating, -/area/station/maintenance/fore) "duA" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/masks, @@ -10598,17 +10512,6 @@ /obj/structure/cable, /turf/open/floor/wood, /area/station/security/detectives_office) -"duU" = ( -/obj/effect/decal/cleanable/cobweb, -/obj/effect/spawner/random/structure/crate, -/turf/open/floor/iron/dark, -/area/station/maintenance/port/lesser) -"duV" = ( -/obj/structure/transit_tube/crossing/horizontal, -/obj/structure/lattice, -/obj/structure/sign/warning/secure_area/directional/south, -/turf/open/space/basic, -/area/space/nearstation) "duW" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line{ @@ -10681,49 +10584,6 @@ /obj/machinery/computer/security/telescreen/minisat/directional/south, /turf/open/floor/plating, /area/station/hallway/primary/central/fore) -"dwh" = ( -/obj/structure/extinguisher_cabinet/directional/south, -/obj/structure/table, -/obj/item/stock_parts/micro_laser{ - pixel_x = -7; - pixel_y = -3 - }, -/obj/item/stock_parts/micro_laser{ - pixel_x = -1; - pixel_y = 3 - }, -/obj/item/stock_parts/micro_laser{ - pixel_x = -5; - pixel_y = 4 - }, -/obj/item/stock_parts/micro_laser{ - pixel_x = -3; - pixel_y = -2 - }, -/obj/item/stock_parts/scanning_module{ - pixel_x = 12; - pixel_y = -1 - }, -/obj/item/stock_parts/scanning_module{ - pixel_x = 10; - pixel_y = 1 - }, -/obj/item/stock_parts/scanning_module{ - pixel_x = 13; - pixel_y = 4 - }, -/obj/item/stock_parts/scanning_module{ - pixel_x = 8; - pixel_y = -1 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/drone_bay) "dwx" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -10792,23 +10652,6 @@ /obj/machinery/light/small/directional/west, /turf/open/floor/engine, /area/station/science/xenobiology) -"dxB" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) -"dxU" = ( -/obj/effect/turf_decal/siding/yellow{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) "dya" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -10844,26 +10687,6 @@ /obj/machinery/power/port_gen/pacman/pre_loaded, /turf/open/floor/plating, /area/station/engineering/supermatter/room) -"dyC" = ( -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/obj/structure/sink/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/obj/machinery/atmospherics/components/binary/pump/on/layer4{ - dir = 4; - name = "Airlock Pump"; - target_pressure = 300; - hide = 1 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "dyD" = ( /obj/machinery/air_sensor/nitrogen_tank, /turf/open/floor/engine/n2, @@ -10905,15 +10728,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"dzy" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/effect/landmark/start/warden, -/obj/structure/chair/office, -/obj/effect/turf_decal/tile/red/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/warden) "dzL" = ( /obj/machinery/modular_computer/preset/engineering{ dir = 8 @@ -10923,21 +10737,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) -"dzQ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/siding/blue{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "dzS" = ( /turf/closed/wall, /area/station/security/lockers) @@ -11048,6 +10847,21 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/iron/showroomfloor, /area/station/science) +"dBX" = ( +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/machinery/disposal/bin/tagger, +/obj/effect/turf_decal/bot, +/obj/machinery/firealarm/directional/west, +/obj/machinery/status_display/supply{ + pixel_y = -32 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/storage) "dCd" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/table/wood/fancy/blue, @@ -11072,18 +10886,6 @@ /obj/machinery/light/cold/directional/west, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) -"dCX" = ( -/obj/machinery/atmospherics/pipe/smart/manifold/cyan/visible{ - dir = 1 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "dDp" = ( /obj/structure/mirror/directional/north, /obj/structure/sink/directional/south, @@ -11163,6 +10965,19 @@ "dEX" = ( /turf/open/floor/iron/dark, /area/station/service/chapel/dock) +"dFa" = ( +/obj/structure/closet/crate/trashcart/laundry, +/obj/effect/spawner/random/contraband/prison, +/obj/item/clothing/under/rank/prisoner, +/obj/item/clothing/under/rank/prisoner, +/obj/item/clothing/under/rank/prisoner/skirt, +/obj/item/clothing/under/rank/prisoner/skirt, +/obj/effect/turf_decal/tile/blue/opposingcorners{ + dir = 1 + }, +/obj/structure/cable, +/turf/open/floor/iron/cafeteria, +/area/station/security/prison/garden) "dFj" = ( /obj/effect/turf_decal/delivery, /obj/effect/decal/cleanable/dirt, @@ -11178,11 +10993,15 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/miningdock) -"dFA" = ( -/obj/machinery/pdapainter, -/obj/structure/sign/poster/official/ian/directional/north, -/turf/open/floor/wood, -/area/station/command/heads_quarters/hop) +"dFC" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/door/airlock{ + name = "Prison Kitchen" + }, +/obj/machinery/duct, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "dFM" = ( /obj/machinery/door/airlock/maintenance, /obj/structure/cable, @@ -11271,21 +11090,6 @@ /obj/structure/sign/warning/secure_area, /turf/closed/wall/r_wall, /area/station/engineering/storage/tech) -"dHw" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/delivery, -/obj/machinery/portable_atmospherics/canister/air, -/obj/machinery/status_display/evac/directional/west, -/obj/effect/turf_decal/stripes/end{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/atmos) "dHL" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/old, @@ -11322,14 +11126,6 @@ }, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) -"dIv" = ( -/obj/machinery/module_duplicator, -/obj/machinery/newscaster/directional/north, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance/office) "dIy" = ( /obj/machinery/door/airlock/grunge, /obj/structure/cable, @@ -11338,6 +11134,12 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/dark, /area/station/service/chapel/dock) +"dIB" = ( +/obj/effect/turf_decal/tile/red/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "dIN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible, /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible/layer2, @@ -11446,6 +11248,17 @@ }, /turf/closed/wall/r_wall, /area/station/science/ordnance/bomb) +"dJt" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ + dir = 9 + }, +/obj/machinery/computer/turbine_computer{ + dir = 1; + mapping_id = "main_turbine" + }, +/turf/open/floor/engine, +/area/station/maintenance/disposal/incinerator) "dJw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -11485,19 +11298,11 @@ }, /turf/open/floor/iron/dark, /area/station/medical/morgue) -"dKa" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) -"dKm" = ( +"dKg" = ( +/obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/spawner/xmastree, -/obj/effect/landmark/event_spawn, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/floor, /turf/open/floor/iron/dark, /area/station/service/chapel) "dKo" = ( @@ -11547,6 +11352,18 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/storage_shared) +"dKB" = ( +/obj/machinery/computer/order_console/cook{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/east, +/obj/machinery/camera/directional/east{ + c_tag = "Kitchen Coldroom"; + name = "kitchen camera" + }, +/turf/open/floor/iron/kitchen_coldroom/freezerfloor, +/area/station/service/kitchen/coldroom) "dKJ" = ( /turf/closed/wall/r_wall/rust, /area/station/command/heads_quarters/captain/private) @@ -11581,6 +11398,14 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating/airless, /area/space/nearstation) +"dLg" = ( +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/purple/opposingcorners, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lab) "dLh" = ( /obj/machinery/door/airlock/command/glass{ name = "Bridge Access" @@ -11602,26 +11427,6 @@ dir = 8 }, /area/station/hallway/primary/port) -"dLr" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/light/small/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 4 - }, -/turf/open/floor/plating, -/area/station/maintenance/solars/port/fore) -"dLy" = ( -/obj/machinery/light/directional/west, -/obj/structure/flora/bush/ferny/style_random, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/grass, -/area/station/science/auxlab) "dLA" = ( /obj/item/kirbyplants/organic/plant10, /obj/machinery/light_switch/directional/west{ @@ -11642,6 +11447,10 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lobby) +"dLD" = ( +/obj/machinery/light/warm/dim/directional/west, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "dLL" = ( /obj/structure/flora/bush/ferny, /turf/open/misc/asteroid, @@ -11683,31 +11492,12 @@ "dME" = ( /turf/closed/wall, /area/station/cargo/warehouse) -"dMH" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/camera/directional/north{ - c_tag = "Head of Security's Office" - }, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/machinery/photocopier, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/hos) "dMR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/table_frame, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"dNx" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "dNL" = ( /obj/machinery/button/flasher{ id = "IsolationFlash"; @@ -11724,6 +11514,18 @@ /obj/machinery/computer/security/telescreen/interrogation/directional/north, /turf/open/floor/iron, /area/station/security/prison) +"dNT" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/obj/machinery/gizmo/toggle/tucker_tubes, +/turf/open/floor/engine, +/area/station/science/auxlab) +"dOa" = ( +/obj/machinery/portable_atmospherics/canister/plasma, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/iron/dark, +/area/station/science/ordnance/storage) "dOg" = ( /obj/item/kirbyplants/organic/plant22, /turf/open/floor/iron/dark, @@ -11737,31 +11539,18 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"dOu" = ( -/obj/machinery/computer/shuttle/mining{ - dir = 8; - req_access = null - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/obj/structure/extinguisher_cabinet/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/cargo/miningdock) "dOz" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"dOF" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/brown/half/contrasted, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +"dOA" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible, +/obj/effect/turf_decal/stripes/line, +/obj/structure/cable, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "dOL" = ( /obj/structure/table, /obj/machinery/microwave{ @@ -11929,6 +11718,25 @@ /obj/structure/cable, /turf/open/floor/engine/telecomms, /area/station/tcommsat/server) +"dRG" = ( +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/table, +/obj/effect/turf_decal/bot, +/obj/machinery/camera/directional/east{ + c_tag = "Cargo Office"; + name = "cargo camera"; + network = list("ss13","qm") + }, +/obj/machinery/fax{ + fax_name = "Cargo Office"; + name = "Cargo Office Fax Machine" + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/office) "dRR" = ( /obj/effect/turf_decal/bot, /obj/structure/bodycontainer/morgue, @@ -11970,6 +11778,15 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"dSo" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/machinery/status_display/evac/directional/north, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "dSt" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -11996,6 +11813,13 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) +"dSz" = ( +/obj/machinery/rnd/production/techfab/department/medical, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/stripes/box, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/dark, +/area/station/medical/storage) "dSB" = ( /obj/machinery/door/airlock/public/glass{ name = "Garden" @@ -12046,6 +11870,10 @@ /obj/item/integrated_circuit/loaded/hello_world, /turf/open/floor/iron/showroomfloor, /area/station/engineering/circuit_workshop) +"dTG" = ( +/obj/structure/transit_tube/crossing, +/turf/open/floor/plating, +/area/station/maintenance/fore) "dTQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/yellow/half/contrasted, @@ -12083,6 +11911,25 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/service/bar) +"dUg" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/structure/cable, +/turf/open/floor/iron, +/area/station/security/processing) +"dUm" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "dUn" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -12146,28 +11993,6 @@ dir = 1 }, /area/station/hallway/primary/fore) -"dUY" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/siding/yellow{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) "dVb" = ( /obj/item/book/manual/wiki/security_space_law, /obj/item/taperecorder{ @@ -12193,6 +12018,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/service/chapel/storage) +"dVn" = ( +/obj/machinery/computer/bank_machine, +/obj/structure/sign/warning/secure_area/directional/north, +/turf/open/floor/circuit/green{ + luminosity = 2 + }, +/area/station/command/vault) "dVB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -12202,6 +12034,16 @@ }, /turf/open/floor/iron, /area/station/cargo/miningoffice) +"dVE" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/engine, +/area/station/tcommsat/computer) "dVN" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/item/clothing/head/fedora, @@ -12257,6 +12099,24 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/cargo/storage) +"dXh" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "dXz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -12312,23 +12172,19 @@ /obj/item/wirecutters, /turf/open/floor/iron/dark, /area/station/security/office) +"dYH" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/north, +/obj/machinery/computer/atmos_control/nocontrol/master, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos/storage/gas) "dYQ" = ( /obj/effect/turf_decal/bot, /obj/machinery/holopad, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/courtroom) -"dZe" = ( -/obj/structure/closet/secure_closet/bar, -/obj/item/flashlight/lantern, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/west, -/obj/machinery/airalarm/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/bar/backroom) "dZk" = ( /obj/machinery/door/airlock/grunge{ id_tag = "Cabin_2"; @@ -12356,13 +12212,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/command/bridge) -"dZR" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "dZS" = ( /obj/machinery/door/airlock/atmos/glass, /obj/effect/mapping_helpers/airlock/cyclelink_helper, @@ -12610,6 +12459,27 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"eeJ" = ( +/obj/structure/table, +/obj/item/hand_labeler, +/obj/item/crowbar/red, +/obj/item/gps/mining, +/obj/effect/decal/cleanable/cobweb, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/light_switch/directional/north, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "eeQ" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line{ @@ -12647,6 +12517,11 @@ "efG" = ( /turf/closed/wall, /area/station/maintenance/fore) +"efO" = ( +/obj/machinery/light/small/directional/west, +/obj/machinery/portable_atmospherics/canister/air, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "efP" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -12714,6 +12589,13 @@ /obj/item/radio/intercom/directional/north, /turf/open/floor/engine/xenobio, /area/station/science/xenobiology) +"egH" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "egU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/landmark/event_spawn, @@ -12724,28 +12606,6 @@ /obj/structure/cable, /turf/open/floor/iron/showroomfloor, /area/station/medical/virology) -"egV" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/obj/structure/cable, -/obj/machinery/status_display/ai/directional/south, -/obj/machinery/firealarm/directional/east, -/turf/open/floor/engine, -/area/station/ai/satellite/chamber) -"ehc" = ( -/obj/machinery/door/airlock/maintenance, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 8 - }, -/obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental, -/obj/effect/mapping_helpers/airlock/access/any/service/janitor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/maintenance/starboard/aft) "ehp" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/carpet/red, @@ -12935,12 +12795,6 @@ "ekm" = ( /turf/closed/wall/r_wall, /area/station/engineering/supermatter/room) -"ekp" = ( -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 1 - }, -/turf/open/floor/plating, -/area/station/maintenance/disposal/incinerator) "ekB" = ( /obj/effect/turf_decal/tile/blue, /obj/effect/turf_decal/tile/blue/half/contrasted, @@ -13028,16 +12882,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/engineering/circuit_workshop) -"elD" = ( -/obj/machinery/vending/dinnerware, -/obj/effect/turf_decal/bot, -/obj/machinery/newscaster/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/kitchen) "elS" = ( /obj/effect/turf_decal/box/corners{ dir = 8 @@ -13182,6 +13026,21 @@ }, /turf/open/floor/iron, /area/station/engineering/storage_shared) +"enU" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 4; + name = "Gas to Chamber" + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/xenobiology) "eob" = ( /obj/structure/sign/warning, /turf/closed/wall, @@ -13225,37 +13084,24 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/medical/virology) -"eoM" = ( -/obj/machinery/door/poddoor/preopen{ - id = "brigfrontdoor"; - name = "Front Security Blast Door" - }, -/obj/structure/disposalpipe/segment{ - dir = 4 +"epi" = ( +/obj/machinery/button/door/directional/north{ + id = "Biohazard"; + name = "Emergency Research Lockdown"; + pixel_x = -8; + req_access = list("research") }, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/firedoor, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/security/brig) -"eoT" = ( /obj/structure/table, -/obj/item/clothing/head/utility/welding{ - pixel_y = 4 +/obj/machinery/recharger, +/obj/effect/turf_decal/tile/red/anticorner/contrasted{ + dir = 4 }, -/obj/machinery/camera/directional/north{ - c_tag = "Tool Storage"; - name = "engineering camera"; - network = list("ss13","engine") +/obj/effect/turf_decal/tile/red{ + dir = 4 }, -/obj/structure/cable, -/obj/item/clothing/gloves/color/fyellow, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/storage/primary) +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/security/checkpoint/science) "ept" = ( /obj/structure/cable, /obj/effect/landmark/event_spawn, @@ -13308,6 +13154,10 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"eqi" = ( +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "eqx" = ( /obj/machinery/conveyor{ dir = 5; @@ -13328,13 +13178,6 @@ /obj/effect/turf_decal/tile/red, /turf/open/floor/iron, /area/station/security/office) -"eqD" = ( -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/dock) "eqE" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ @@ -13355,24 +13198,6 @@ }, /turf/open/floor/iron/dark, /area/station/security/warden) -"eqV" = ( -/obj/structure/table, -/obj/item/tank/jetpack/carbondioxide{ - pixel_x = -4; - pixel_y = 6 - }, -/obj/item/tank/jetpack/carbondioxide{ - pixel_y = 2 - }, -/obj/item/wrench, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/command/eva) "era" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -13394,6 +13219,15 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/department/crew_quarters/bar) +"erk" = ( +/obj/effect/decal/cleanable/blood/footprints{ + dir = 4; + pixel_x = 24; + pixel_y = 8 + }, +/obj/machinery/light/small/broken/directional/north, +/turf/open/floor/plating/rust, +/area/station/security/prison/shower) "erl" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/airalarm/directional/north, @@ -13410,6 +13244,26 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/engineering/atmos/pumproom) +"erp" = ( +/obj/structure/closet/wardrobe/miner, +/obj/effect/turf_decal/delivery, +/obj/item/clothing/suit/hooded/wintercoat/miner, +/obj/item/clothing/suit/hooded/wintercoat/miner, +/obj/item/clothing/suit/hooded/wintercoat/miner, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow/opposingcorners{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "erH" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, @@ -13501,35 +13355,21 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/starboard) -"eto" = ( -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/main) -"etB" = ( -/obj/structure/table/wood, -/obj/item/folder/red, -/obj/item/lighter{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/cigarette/cigar/cohiba{ - pixel_x = 6 - }, -/obj/item/cigarette/cigar, -/obj/machinery/light/directional/west, -/obj/machinery/airalarm/directional/west, -/obj/item/paper_bin, -/turf/open/floor/wood, -/area/station/command/heads_quarters/hos) "etD" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/service/kitchen) +"etH" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/assembly/flash/handheld, +/obj/item/assembly/flash/handheld, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "etO" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -13546,16 +13386,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/pharmacy) -"euc" = ( -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/airlock/public/glass{ - name = "Chapel Hallway" - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/fore) "euX" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -13568,6 +13398,16 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/storage) +"eva" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/disposal/bin/tagger, +/obj/structure/extinguisher_cabinet/directional/west, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/structure/disposalpipe/trunk, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos/storage/gas) "evb" = ( /obj/structure/tank_dispenser, /obj/effect/turf_decal/delivery, @@ -13594,16 +13434,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"evu" = ( -/obj/effect/turf_decal/box/corners{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "evD" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -13681,6 +13511,15 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/port) +"ewZ" = ( +/obj/machinery/recharge_station, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/obj/structure/extinguisher_cabinet/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/paramedic) "exg" = ( /obj/structure/sign/warning/vacuum/external, /turf/closed/wall, @@ -13728,25 +13567,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/maintenance/starboard/fore) -"exV" = ( -/obj/structure/table, -/obj/item/tank/internals/emergency_oxygen{ - pixel_x = -4 - }, -/obj/machinery/portable_atmospherics/canister/air{ - pixel_x = 4 - }, -/obj/item/clothing/mask/breath{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/clothing/mask/breath, -/obj/structure/sign/poster/contraband/random/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) "eyc" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -13776,19 +13596,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"eyx" = ( -/obj/structure/reagent_dispensers/fueltank, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "eyA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -13798,6 +13605,23 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) +"eyJ" = ( +/obj/machinery/airalarm/directional/west, +/obj/machinery/power/apc/auto_name/directional/south, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 8 + }, +/obj/machinery/vending/coffee, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/break_room) "eyU" = ( /obj/structure/cable, /obj/effect/spawner/structure/window/reinforced, @@ -13828,20 +13652,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"ezY" = ( -/obj/structure/chair{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/execution/education) "eAa" = ( /obj/structure/rack, /obj/effect/turf_decal/bot, @@ -13905,21 +13715,24 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) -"eBi" = ( -/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/light/directional/west, +"eAQ" = ( +/obj/effect/spawner/random/engineering/tracking_beacon, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) +"eBm" = ( /obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/table, +/obj/machinery/reagentgrinder{ + pixel_y = 5 + }, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 }, /turf/open/floor/iron/dark, -/area/station/engineering/atmos) +/area/station/service/hydroponics) "eBu" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -13950,30 +13763,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) -"eBZ" = ( -/obj/effect/turf_decal/delivery, -/obj/structure/closet/crate/silvercrate, -/obj/machinery/light/small/directional/east, -/obj/item/screwdriver/power, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/command/vault) "eCd" = ( /turf/closed/wall, /area/station/hallway/secondary/service) -"eCf" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "eCh" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -13997,6 +13789,17 @@ /obj/structure/cable, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) +"eCH" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/structure/extinguisher_cabinet/directional/west, +/obj/machinery/status_display/ai/directional/south, +/obj/structure/cable/layer3, +/turf/open/floor/engine, +/area/station/ai/satellite/chamber) "eCU" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -14043,15 +13846,30 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/storage) -"eDx" = ( -/obj/machinery/firealarm/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/red/half/contrasted{ +"eDw" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/turf_decal/siding/purple/corner{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/brig) +/obj/effect/turf_decal/tile/purple{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lobby) +"eDR" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/light_switch/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted, +/obj/effect/turf_decal/tile/purple, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "eDT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14094,18 +13912,6 @@ }, /turf/open/floor/iron/grimy, /area/station/security/prison) -"eEz" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/yellow{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "eEB" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -14127,6 +13933,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/aft) +"eEU" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "eEX" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/cleanable/dirt, @@ -14269,6 +14086,17 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) +"eHw" = ( +/obj/effect/turf_decal/tile/blue/anticorner/contrasted, +/obj/structure/closet/secure_closet/paramedic, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/camera/autoname/directional/east, +/obj/machinery/airalarm/directional/east, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/paramedic) "eHI" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 @@ -14307,34 +14135,6 @@ /obj/structure/sign/departments/evac, /turf/closed/wall, /area/station/security/checkpoint/customs) -"eIb" = ( -/obj/machinery/light/directional/east, -/obj/structure/chair/office/light{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/obj/machinery/computer/security/telescreen/ordnance/directional/east, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) -"eIi" = ( -/obj/structure/table/wood/fancy, -/obj/effect/turf_decal/siding/wideplating/dark/corner{ - dir = 4 - }, -/obj/machinery/light/directional/east, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "eIl" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/security{ @@ -14360,19 +14160,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science) -"eIP" = ( -/obj/effect/landmark/start/cyborg, -/obj/machinery/airalarm/directional/north, -/obj/machinery/camera/motion/directional/west{ - c_tag = "ai_upload Foyer"; - name = "upload camera"; - network = list("aiupload") - }, -/obj/machinery/computer/security/telescreen/aiupload/directional/west, -/turf/open/floor/circuit/green{ - luminosity = 2 - }, -/area/station/ai/upload/chamber) "eIT" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -14402,22 +14189,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/wood/large, /area/station/service/bar/atrium) -"eJm" = ( -/obj/structure/table, -/obj/item/clipboard, -/obj/item/folder/red, -/obj/item/restraints/handcuffs, -/obj/item/melee/baseball_bat{ - desc = "A staple of security force interrogations."; - icon_state = "baseball_bat_metal"; - name = "kneecapper" - }, -/obj/machinery/status_display/ai/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/execution/education) "eJq" = ( /turf/closed/wall/r_wall/rust, /area/station/science/robotics/lab) @@ -14453,6 +14224,19 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"eKa" = ( +/obj/effect/turf_decal/stripes/line, +/obj/machinery/camera/directional/east{ + c_tag = "Starboard Hallway Cargo Counter"; + name = "starboard camera" + }, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow, +/obj/machinery/light/directional/east, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "eKk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14497,6 +14281,21 @@ }, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"eLf" = ( +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/machinery/disposal/bin/tagger, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red/opposingcorners{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "eLm" = ( /turf/closed/wall, /area/station/security/checkpoint/customs) @@ -14557,6 +14356,14 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"eMg" = ( +/obj/structure/table, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark, +/area/station/security/courtroom) "eMo" = ( /obj/structure/sign/departments/evac, /turf/closed/wall, @@ -14599,20 +14406,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/medical/virology) -"eMX" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "eNb" = ( /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) @@ -14644,6 +14437,14 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) +"eOe" = ( +/obj/effect/turf_decal/stripes/line, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/caution/stand_clear, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/security/processing) "eOh" = ( /obj/structure/table/reinforced, /obj/structure/reagent_dispensers/servingdish, @@ -14658,7 +14459,6 @@ }, /obj/effect/turf_decal/tile/blue/opposingcorners, /obj/machinery/duct, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) "eOl" = ( @@ -14678,6 +14478,33 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"eOA" = ( +/obj/effect/mapping_helpers/broken_floor, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/bot, +/obj/structure/rack, +/obj/item/wrench, +/obj/item/storage/toolbox/emergency{ + pixel_y = 4 + }, +/obj/item/hand_labeler, +/obj/item/stack/package_wrap, +/obj/machinery/button/door/directional/east{ + id = "commissaryshutter"; + name = "Commissary Shutter Toggle"; + pixel_x = 26; + pixel_y = 7 + }, +/obj/machinery/button/door/directional/east{ + id = "commissarydoor"; + name = "Commissary Door Lock"; + normaldoorcontrol = 1; + pixel_x = 26; + pixel_y = -2; + specialfunctions = 4 + }, +/turf/open/floor/wood, +/area/station/commons/vacant_room/commissary) "eOG" = ( /obj/effect/turf_decal/delivery, /obj/effect/decal/cleanable/cobweb/cobweb2, @@ -14697,6 +14524,13 @@ }, /turf/open/floor/circuit/red, /area/station/ai/upload/chamber) +"eOV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/tile/brown/anticorner/contrasted, +/obj/effect/turf_decal/tile/yellow, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "eOW" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/decal/cleanable/dirt, @@ -14717,14 +14551,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"ePe" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/hallway/primary/aft) "ePf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14735,13 +14561,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar/backroom) -"ePm" = ( -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 8 - }, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "ePU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14793,6 +14612,21 @@ /obj/structure/flora/bush/sunny/style_random, /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) +"eQh" = ( +/obj/structure/table, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/glass/fifty, +/obj/item/storage/belt/utility, +/obj/item/clothing/head/utility/welding, +/obj/item/clothing/glasses/welding, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/airalarm/directional/west, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron/dark, +/area/station/science/robotics/lab) "eQj" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -14870,6 +14704,30 @@ /obj/effect/mapping_helpers/airlock/access/all/command/gateway, /turf/open/floor/iron/dark, /area/station/command/gateway) +"eQW" = ( +/obj/machinery/atmospherics/pipe/smart/manifold/cyan/visible{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/directional/west, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) +"eRl" = ( +/obj/effect/turf_decal/tile/brown{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "eRp" = ( /obj/structure/disposalpipe/segment, /obj/effect/decal/cleanable/dirt, @@ -14912,19 +14770,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/construction/mining/aux_base) -"eRD" = ( -/obj/structure/table/wood, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/obj/machinery/newscaster/directional/west, -/obj/item/camera{ - pixel_y = 4 - }, -/obj/item/hand_tele, -/turf/open/floor/wood/large, -/area/station/command/heads_quarters/captain) "eRK" = ( /obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ dir = 4 @@ -15001,12 +14846,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/checker, /area/station/maintenance/port/aft) -"eTS" = ( -/obj/machinery/light/directional{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "eUd" = ( /obj/structure/table, /obj/item/reagent_containers/condiment/peppermill{ @@ -15052,6 +14891,19 @@ }, /turf/open/floor/iron/dark, /area/station/medical/storage) +"eUy" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/status_display/ai/directional/west, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/light/directional/west, +/turf/open/floor/iron, +/area/station/hallway/primary/aft) "eUM" = ( /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, /turf/closed/wall/r_wall, @@ -15069,6 +14921,16 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"eUU" = ( +/obj/machinery/vending/cola/red, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "eVL" = ( /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, @@ -15096,21 +14958,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark, /area/station/command/bridge) -"eWd" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/wall_healer/directional/north, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "eWu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -15121,12 +14968,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/security/brig) -"eWH" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/shower/directional/west, -/obj/effect/turf_decal/box, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/cryo) "eWP" = ( /turf/closed/wall/r_wall, /area/station/maintenance/port/fore) @@ -15250,19 +15091,6 @@ /obj/item/fuel_pellet, /turf/open/floor/plating, /area/station/cargo/drone_bay) -"eYw" = ( -/obj/machinery/recharge_station, -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/science/robotics/mechbay) "eYB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -15275,21 +15103,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"eYE" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/landmark/start/chief_engineer, -/obj/structure/cable, -/obj/effect/turf_decal/siding/yellow{ - dir = 5 - }, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) "eYH" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/blue/opposingcorners, @@ -15320,6 +15133,15 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) +"eZi" = ( +/obj/effect/turf_decal/siding/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "eZm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -15347,11 +15169,6 @@ /obj/structure/flora/grass/jungle/b/style_5, /turf/open/misc/asteroid, /area/space/nearstation) -"eZL" = ( -/obj/effect/landmark/event_spawn, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "fac" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -15365,18 +15182,13 @@ /obj/structure/sign/warning/docking, /turf/closed/wall, /area/station/cargo/miningoffice) -"fak" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/camera/directional/east{ - c_tag = "Starboard Hallway Cargo Counter"; - name = "starboard camera" - }, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) +"faC" = ( +/obj/structure/table, +/obj/machinery/firealarm/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron/dark, +/area/station/commons/locker) "faN" = ( /obj/structure/cable, /obj/structure/chair/office{ @@ -15385,6 +15197,17 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark, /area/station/command/bridge) +"faY" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/security/processing) "faZ" = ( /obj/structure/chair/sofa/left/maroon{ dir = 4 @@ -15406,11 +15229,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/stairs/old, /area/station/hallway/primary/fore) -"fbz" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "fbB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -15450,12 +15268,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/medical/cryo) -"fcM" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "fcX" = ( /obj/structure/table/wood, /obj/machinery/camera/directional/west{ @@ -15559,16 +15371,16 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"feZ" = ( -/obj/structure/displaycase/labcage, -/obj/effect/turf_decal/box, -/obj/machinery/light/directional/south, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 +"feW" = ( +/obj/machinery/computer/mech_bay_power_console{ + dir = 4 }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/rd) +/obj/item/radio/intercom/directional/north, +/obj/structure/cable, +/turf/open/floor/circuit/green{ + luminosity = 2 + }, +/area/station/science/robotics/mechbay) "ffa" = ( /turf/open/floor/plating/airless, /area/station/hallway/secondary/exit/departure_lounge) @@ -15645,6 +15457,13 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"ffT" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/machinery/atmospherics/components/tank/air/layer4{ + dir = 8 + }, +/turf/open/floor/plating, +/area/station/maintenance/aft) "fgb" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -15664,17 +15483,13 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/commons/storage/art) -"fgu" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ +"fgx" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/blue{ dir = 4 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "fgy" = ( /obj/structure/disposalpipe/segment{ dir = 10 @@ -15721,6 +15536,14 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/central) +"fhc" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/security/brig) "fhe" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, @@ -15733,6 +15556,15 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard) +"fhq" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "fhs" = ( /obj/structure/closet/emcloset, /obj/effect/turf_decal/delivery, @@ -15755,6 +15587,19 @@ }, /turf/open/floor/plating, /area/station/maintenance/aft) +"fhC" = ( +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "fhH" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -15810,6 +15655,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/department/bridge) +"fiC" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/maintenance/aft) "fiH" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -15830,15 +15681,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/captain) -"fiR" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/security/processing) "fiW" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -15851,47 +15693,6 @@ }, /turf/open/floor/wood/large, /area/station/service/bar/atrium) -"fiZ" = ( -/obj/item/kirbyplants/organic/applebush, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/airalarm/directional/west, -/obj/machinery/status_display/ai/directional/north, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) -"fjk" = ( -/obj/structure/table, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/effect/spawner/random/aimodule/harmful, -/obj/machinery/door/window/brigdoor/left/directional/south{ - name = "High-Risk Modules"; - req_access = list("captain") - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) -"fjn" = ( -/obj/structure/sink/directional/south, -/obj/effect/turf_decal/tile/green/anticorner/contrasted{ - dir = 4 - }, -/obj/machinery/light/directional/north, -/obj/structure/closet/l3closet/virology, -/obj/item/tank/internals/emergency_oxygen, -/obj/item/tank/internals/emergency_oxygen, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "fjw" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -15917,6 +15718,19 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/lockers) +"fjG" = ( +/obj/effect/turf_decal/stripes/white/line{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/structure/mannequin/skeleton{ + dir = 8 + }, +/obj/item/radio/intercom/directional/east, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "fjI" = ( /obj/structure/chair/office{ dir = 4 @@ -15929,15 +15743,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/fore) -"fjX" = ( -/obj/machinery/door/firedoor, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/central/fore) "fkw" = ( /obj/effect/landmark/start/hangover, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -15945,30 +15750,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"fkB" = ( -/obj/structure/table, -/obj/item/stack/sheet/plasteel/fifty{ - amount = 10; - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/stack/sheet/rglass{ - amount = 50 - }, -/obj/item/stack/sheet/rglass{ - amount = 50 - }, -/obj/item/clothing/shoes/magboots{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/clothing/shoes/magboots, -/obj/machinery/airalarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/eva) "fkI" = ( /obj/structure/table, /obj/item/wallframe/airalarm, @@ -15998,18 +15779,6 @@ }, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"flt" = ( -/obj/structure/bed, -/obj/machinery/iv_drip, -/obj/item/bedsheet/medical, -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/medical) "flx" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, /obj/effect/turf_decal/stripes/line{ @@ -16077,6 +15846,18 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/central) +"fmv" = ( +/obj/structure/table/wood, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/obj/machinery/newscaster/directional/west, +/obj/item/camera{ + pixel_y = 4 + }, +/obj/item/hand_tele, +/turf/open/floor/wood/large, +/area/station/command/heads_quarters/captain) "fmx" = ( /obj/effect/turf_decal/loading_area{ dir = 1 @@ -16109,14 +15890,17 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) -"fmQ" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/airlock/public/glass{ - name = "Chapel Hallway" +"fmK" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 8 }, /turf/open/floor/iron/dark, -/area/station/hallway/primary/fore) +/area/station/hallway/primary/aft) "fmV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16128,6 +15912,20 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"fmY" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 5 + }, +/obj/structure/cable, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/storage) "fne" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16146,6 +15944,34 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/storage) +"fnf" = ( +/obj/item/storage/medkit/regular{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/storage/medkit/brute{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/storage/medkit/brute, +/obj/item/storage/medkit/brute{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/structure/table/glass, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/door/window/left/directional/east{ + name = "First-Aid Supplies"; + req_access = list("medical") + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/medical/storage) "fnu" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -16198,11 +16024,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"fol" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/wood, -/area/station/commons/vacant_room/commissary) "fov" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 @@ -16210,19 +16031,6 @@ /obj/machinery/status_display/ai/directional/east, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"foB" = ( -/obj/structure/table/wood/fancy/black, -/obj/item/food/grown/poppy/geranium{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/geranium{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/geranium, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "foP" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -16259,10 +16067,6 @@ /obj/item/clothing/glasses/sunglasses/big, /turf/open/floor/carpet/green, /area/station/service/lawoffice) -"fph" = ( -/obj/effect/spawner/random/engineering/tracking_beacon, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "fpk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16271,6 +16075,16 @@ }, /turf/open/floor/engine, /area/station/ai/satellite/atmos) +"fpn" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/commons/fitness/recreation) "fpx" = ( /obj/structure/chair/office/light{ dir = 4 @@ -16360,6 +16174,17 @@ }, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/starboard) +"fqu" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/command/heads_quarters/rd) "fqG" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16531,18 +16356,6 @@ /obj/effect/turf_decal/tile/red/fourcorners, /turf/open/floor/iron/white, /area/station/security/prison/safe) -"fth" = ( -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/structure/table/glass, -/obj/machinery/microwave{ - pixel_y = 5 - }, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/break_room) "fti" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -16564,14 +16377,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) -"fty" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/airalarm/directional/north, -/turf/open/floor/iron/dark, -/area/station/service/chapel/dock) "ftz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16587,6 +16392,16 @@ }, /turf/open/floor/iron/dark, /area/station/service/kitchen) +"ftJ" = ( +/obj/effect/decal/cleanable/cobweb, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/firealarm/directional/west, +/obj/item/radio/intercom/directional/north, +/obj/machinery/pdapainter/supply, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/qm) "ftS" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -16793,6 +16608,19 @@ }, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/port) +"fwd" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/siding/yellow{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "fwf" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -16810,32 +16638,6 @@ }, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) -"fwM" = ( -/obj/structure/table, -/obj/machinery/camera/directional/east{ - c_tag = "Garden" - }, -/obj/effect/turf_decal/siding/green{ - dir = 8 - }, -/obj/item/shovel/spade, -/obj/item/hatchet, -/obj/item/cultivator, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/item/shovel/spade, -/obj/item/cultivator, -/obj/item/hatchet, -/obj/item/plant_analyzer, -/obj/item/reagent_containers/cup/bottle/nutrient/rh{ - pixel_x = 2; - pixel_y = 1 - }, -/obj/item/reagent_containers/cup/bottle/nutrient/ez, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "fxd" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/north, @@ -16854,20 +16656,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/security/mechbay) -"fxx" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light_switch/directional/west, -/obj/machinery/light/directional/west, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/disposal/bin/tagger, -/turf/open/floor/iron/dark, -/area/station/medical/office) "fxB" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/bridge_pipe/purple/visible, @@ -16941,6 +16729,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/courtroom) +"fyh" = ( +/obj/structure/table, +/obj/machinery/processor{ + pixel_y = 6 + }, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/service/kitchen) "fyj" = ( /obj/structure/cable, /turf/closed/wall/r_wall, @@ -16996,30 +16795,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron/dark, /area/station/service/chapel) -"fyM" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) -"fyS" = ( -/obj/structure/closet/wardrobe/miner, -/obj/effect/turf_decal/delivery, -/obj/item/clothing/suit/hooded/wintercoat/miner, -/obj/item/clothing/suit/hooded/wintercoat/miner, -/obj/item/clothing/suit/hooded/wintercoat/miner, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "fyZ" = ( /obj/structure/chair/office/light{ dir = 4 @@ -17090,6 +16865,14 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"fAk" = ( +/obj/machinery/status_display/evac/directional/north, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron, +/area/station/security/courtroom) "fAI" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/purple/filled/line{ @@ -17106,6 +16889,18 @@ dir = 8 }, /area/station/service/chapel) +"fAW" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/machinery/status_display/ai/directional/north, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/security/execution/transfer) "fBh" = ( /obj/machinery/door/airlock/maintenance, /obj/structure/disposalpipe/segment{ @@ -17147,6 +16942,13 @@ /obj/effect/mapping_helpers/airlock/access/all/service/chapel_office, /turf/open/floor/iron/dark, /area/station/service/chapel/office) +"fBw" = ( +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "fBF" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -17222,6 +17024,11 @@ }, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) +"fCf" = ( +/obj/structure/flora/bush/flowers_yw/style_random, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/grass, +/area/station/service/chapel) "fCs" = ( /obj/structure/chair/wood{ dir = 8 @@ -17257,21 +17064,6 @@ }, /turf/open/floor/iron, /area/station/service/theater) -"fCN" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/light/small/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/plating, -/area/station/maintenance/solars/port/aft) "fCX" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -17446,16 +17238,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/janitor) -"fFs" = ( -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/machinery/atmospherics/components/tank/air/layer5{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) "fFE" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -17603,14 +17385,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"fIl" = ( -/obj/effect/mapping_helpers/broken_floor, -/obj/effect/turf_decal/bot, -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 8 - }, -/turf/open/floor/plating, -/area/station/maintenance/starboard) "fIn" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/flora/grass/jungle/a/style_random, @@ -17751,6 +17525,15 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/captain) +"fJy" = ( +/obj/machinery/flasher/portable, +/obj/effect/turf_decal/box, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/light/small/red/directional/north, +/turf/open/floor/iron/dark, +/area/station/security/armory) "fJz" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -17877,6 +17660,19 @@ /obj/machinery/power/port_gen/pacman/pre_loaded, /turf/open/floor/plating, /area/station/ai/satellite/maintenance/storage) +"fLi" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/landmark/event_spawn, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/command/bridge) "fLo" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -17884,10 +17680,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/cargo/storage) -"fLu" = ( -/obj/effect/decal/cleanable/ash, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "fLv" = ( /turf/open/floor/glass, /area/station/service/hydroponics) @@ -17935,6 +17727,26 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/fore) +"fLE" = ( +/obj/item/clothing/mask/gas/sechailer{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/clothing/mask/gas/sechailer, +/obj/item/clothing/mask/gas/sechailer{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/flashlight/seclite, +/obj/item/flashlight/seclite, +/obj/item/flashlight/seclite, +/obj/effect/decal/cleanable/cobweb, +/obj/machinery/airalarm/directional/west, +/obj/structure/table, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/light/small/red/directional/north, +/turf/open/floor/iron/dark, +/area/station/security/armory) "fLH" = ( /obj/effect/decal/cleanable/blood/old, /obj/machinery/air_sensor/ordnance_burn_chamber, @@ -17965,6 +17777,19 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/command/bridge) +"fMA" = ( +/obj/structure/table/wood/fancy/black, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/geranium, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "fMF" = ( /obj/machinery/camera/directional/west{ c_tag = "Atmospherics Tank - Plasma"; @@ -18002,25 +17827,6 @@ /obj/structure/flora/grass/jungle/b/style_random, /turf/open/misc/asteroid, /area/station/maintenance/port/lesser) -"fNG" = ( -/obj/machinery/light/directional/north, -/obj/machinery/status_display/evac/directional/north, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron, -/area/station/security/courtroom) -"fNT" = ( -/obj/item/kirbyplants/organic/plant21, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark/corner{ - dir = 8 - }, -/area/station/hallway/primary/central/fore) "fOd" = ( /obj/machinery/chem_master/condimaster{ desc = "Used to separate out liquids - useful for purifying botanical extracts. Also dispenses condiments."; @@ -18113,6 +17919,21 @@ dir = 4 }, /area/station/hallway/primary/fore) +"fPk" = ( +/obj/structure/table/wood, +/obj/item/folder/red, +/obj/item/lighter{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/cigarette/cigar/cohiba{ + pixel_x = 6 + }, +/obj/item/cigarette/cigar, +/obj/machinery/airalarm/directional/west, +/obj/item/paper_bin, +/turf/open/floor/wood, +/area/station/command/heads_quarters/hos) "fPp" = ( /obj/effect/landmark/start/hangover, /obj/effect/turf_decal/stripes/line{ @@ -18236,6 +18057,22 @@ /obj/machinery/duct, /turf/open/floor/iron/grimy, /area/station/security/prison/safe) +"fQY" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/airlock/highsecurity{ + name = "AI Upload" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/effect/mapping_helpers/airlock/access/all/command/ai_upload, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "fRf" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/brown/half/contrasted{ @@ -18246,6 +18083,14 @@ }, /turf/open/floor/iron, /area/station/cargo/warehouse) +"fRm" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "fRp" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -18282,6 +18127,23 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"fRC" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ + dir = 5 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/machinery/duct, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "fRP" = ( /obj/structure/chair{ dir = 8 @@ -18290,22 +18152,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/security/interrogation) -"fRS" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 8 - }, -/obj/machinery/camera/autoname/directional/west, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "fSa" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/purple/half/contrasted{ @@ -18316,16 +18162,16 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"fSm" = ( +/obj/machinery/gulag_teleporter, +/obj/effect/turf_decal/box, +/obj/effect/turf_decal/stripes/corner, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/security/processing) "fSt" = ( /turf/closed/wall/r_wall/rust, /area/station/engineering/storage_shared) -"fSC" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/public/glass{ - name = "Arrivals Dock" - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) "fSD" = ( /obj/effect/turf_decal/bot, /obj/structure/closet/crate, @@ -18417,6 +18263,17 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/maintenance/storage) +"fTa" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "fTr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -18461,6 +18318,14 @@ /obj/effect/turf_decal/tile/red/anticorner/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/security/brig) +"fUj" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/central/fore) "fUp" = ( /obj/machinery/chem_heater/withbuffer, /obj/effect/decal/cleanable/dirt, @@ -18469,18 +18334,6 @@ }, /turf/open/floor/iron/dark, /area/station/medical/chemistry) -"fUP" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/duct, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/service/bar/atrium) "fUQ" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/sign/warning/vacuum/external/directional/west, @@ -18570,6 +18423,23 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"fWg" = ( +/obj/structure/table, +/obj/item/clipboard, +/obj/item/folder/red, +/obj/item/restraints/handcuffs, +/obj/item/melee/baseball_bat{ + desc = "A staple of security force interrogations."; + icon_state = "baseball_bat_metal"; + name = "kneecapper" + }, +/obj/machinery/status_display/ai/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/dim/directional/east, +/turf/open/floor/iron/dark, +/area/station/security/execution/education) "fWk" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 8 @@ -18583,18 +18453,6 @@ }, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/bomb) -"fWB" = ( -/obj/machinery/airalarm/directional/west, -/obj/machinery/rnd/production/techfab/department/service, -/obj/effect/turf_decal/delivery, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/directional/west, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/service) "fWK" = ( /obj/machinery/cryo_cell, /obj/effect/turf_decal/bot, @@ -18635,9 +18493,28 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"fXK" = ( +/obj/structure/chair/sofa/bench/right{ + dir = 4 + }, +/obj/effect/landmark/start/assistant, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/obj/machinery/light/dim/directional/west, +/turf/open/floor/wood, +/area/station/service/chapel/monastery) "fXL" = ( /turf/closed/wall/rust, /area/station/service/chapel/office) +"fXP" = ( +/obj/machinery/newscaster/directional/east, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/machinery/light/warm/dim/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "fXQ" = ( /obj/effect/turf_decal/caution/stand_clear, /obj/machinery/door/poddoor/shutters/radiation/preopen{ @@ -18657,17 +18534,6 @@ /obj/effect/landmark/navigate_destination/eva, /turf/open/floor/iron/dark, /area/station/command/eva) -"fXZ" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/structure/table, -/obj/item/clothing/gloves/latex, -/obj/item/paper/guides/jobs/medical/morgue, -/obj/item/pen, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) "fYh" = ( /turf/closed/wall/r_wall, /area/station/command/bridge) @@ -18706,6 +18572,13 @@ /obj/machinery/computer/security/telescreen/med_sec/directional/south, /turf/open/floor/iron/dark, /area/station/security/checkpoint/medical) +"fZj" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "fZk" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -18844,6 +18717,11 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/department/bridge) +"gbx" = ( +/obj/structure/cable, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/xenobiology) "gbB" = ( /obj/item/disk/computer/medical, /obj/item/disk/computer/medical, @@ -18932,6 +18810,28 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"gcV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/structure/table, +/obj/item/clothing/gloves/latex, +/obj/item/paper/guides/jobs/medical/morgue, +/obj/item/pen, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) +"gdf" = ( +/obj/structure/table, +/obj/machinery/status_display/ai/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/foyer) "gdA" = ( /obj/machinery/door/airlock/maintenance, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -19020,29 +18920,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lab) -"geO" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/machinery/door_buttons/airlock_controller{ - idExterior = "virology_airlock_exterior"; - idInterior = "virology_airlock_interior"; - idSelf = "virology_airlock_control"; - name = "Virology Access Console"; - pixel_x = 24; - pixel_y = -24; - req_access = list("virology") - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/firealarm/directional/west, -/obj/effect/turf_decal/tile/green/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "geV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -19082,34 +18959,17 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) +"gfy" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/holopad, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/showroomfloor, +/area/station/security/warden) "gfz" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/structure/flora/bush/stalky/style_random, /turf/open/floor/grass, /area/station/service/hydroponics/garden) -"gfD" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science) -"gfT" = ( -/obj/machinery/firealarm/directional/west, -/obj/machinery/light/directional/west, -/obj/machinery/computer/atmos_control/ordnancemix{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) "ggb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, @@ -19123,6 +18983,22 @@ }, /turf/open/floor/iron/chapel, /area/station/service/chapel) +"ggD" = ( +/obj/structure/table, +/obj/item/storage/crayons, +/obj/item/clothing/under/color/grey{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clothing/under/color/grey, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "ggF" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -19132,22 +19008,6 @@ /obj/machinery/computer/station_alert, /turf/open/floor/iron/dark, /area/station/engineering/lobby) -"ggI" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/door/airlock/maintenance, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "sci-maint-passthrough" - }, -/obj/effect/mapping_helpers/airlock/access/any/science/maintenance, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 1 - }, -/turf/open/floor/plating, -/area/station/science) "ggS" = ( /turf/closed/wall/r_wall, /area/station/science/ordnance) @@ -19267,14 +19127,6 @@ }, /turf/open/floor/engine, /area/station/engineering/storage/tech) -"giG" = ( -/obj/machinery/light/directional/east, -/obj/structure/closet/bombcloset, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/science/lobby) "giH" = ( /obj/structure/sign/poster/official/help_others, /turf/closed/wall/r_wall/rust, @@ -19314,20 +19166,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) -"gjc" = ( -/obj/machinery/airalarm/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"gjg" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/newscaster/directional/south, -/turf/open/floor/iron/dark, -/area/station/security/brig) "gjv" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -19406,11 +19244,6 @@ /obj/structure/sign/poster/random/directional/east, /turf/open/floor/plating, /area/station/maintenance/department/bridge) -"gkb" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/engine, -/area/station/command/vault) "gkf" = ( /obj/structure/sign/warning/docking, /turf/closed/wall/rust, @@ -19482,16 +19315,6 @@ /obj/machinery/holopad, /turf/open/floor/carpet/green, /area/station/security/detectives_office) -"glY" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/airalarm/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/floor/iron/grimy, -/area/station/hallway/primary/fore) "gmb" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -19520,13 +19343,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) -"gmn" = ( -/obj/structure/grille/broken, -/obj/effect/decal/cleanable/blood/old, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/plating, -/area/station/maintenance/aft) "gmo" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/structure/window/reinforced, @@ -19576,6 +19392,22 @@ /obj/item/flashlight/lantern, /turf/open/floor/carpet/red, /area/station/service/chapel) +"gns" = ( +/obj/machinery/status_display/ai/directional/south, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/structure/frame/computer{ + anchored = 1; + dir = 1 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/cargo/drone_bay) "gny" = ( /obj/structure/cable, /obj/effect/turf_decal/sand/plating, @@ -19650,6 +19482,13 @@ /obj/machinery/light/floor, /turf/open/floor/engine/n2, /area/station/engineering/atmos) +"got" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/firealarm/directional/south, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "gou" = ( /obj/effect/turf_decal/box/corners{ dir = 8 @@ -19659,6 +19498,17 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/gravity_generator) +"goz" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/hallway/primary/aft) "goG" = ( /obj/effect/turf_decal/siding/yellow{ dir = 4 @@ -19768,6 +19618,18 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) +"gpX" = ( +/obj/structure/table, +/obj/item/folder{ + pixel_x = -4 + }, +/obj/item/pai_card, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "gqk" = ( /obj/machinery/chem_heater/withbuffer, /obj/effect/turf_decal/bot, @@ -19880,6 +19742,17 @@ /obj/structure/lattice/catwalk, /turf/open/space/basic, /area/station/solars/port/fore) +"grZ" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "gse" = ( /obj/structure/chair/pew/left{ dir = 8 @@ -19891,17 +19764,15 @@ dir = 1 }, /area/station/service/chapel) -"gsl" = ( -/obj/effect/turf_decal/siding/red{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +"gso" = ( +/obj/structure/closet/secure_closet/engineering_personal, +/obj/item/clothing/suit/hooded/wintercoat/engineering, +/obj/effect/turf_decal/delivery, +/obj/machinery/light_switch/directional/north, +/obj/item/pickaxe/mini, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) +/area/station/engineering/storage_shared) "gsr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -19953,6 +19824,34 @@ }, /turf/open/floor/iron, /area/station/cargo/office) +"gsR" = ( +/obj/structure/table/wood, +/obj/item/storage/photo_album/library{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/item/taperecorder{ + pixel_x = 5; + pixel_y = 4 + }, +/obj/item/camera, +/obj/item/pen{ + pixel_x = -7; + pixel_y = -5 + }, +/obj/machinery/light/small/directional/west, +/obj/machinery/camera/directional/west{ + c_tag = "Library Backroom"; + name = "library camera" + }, +/obj/structure/sign/painting/large/library_private{ + dir = 8; + pixel_x = -29 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/station/service/library) "gtj" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, @@ -19992,26 +19891,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/janitor) -"gtK" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/button/door/directional/south{ - id = "morgueprivacy"; - name = "Morgue Privacy Shutter"; - pixel_x = -8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) -"gtR" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/disposal/bin/tagger, -/obj/structure/extinguisher_cabinet/directional/west, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/disposalpipe/trunk, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos/storage/gas) "gtT" = ( /obj/structure/railing{ dir = 8 @@ -20021,13 +19900,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/funeral) -"gtU" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Security Maintenance" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/dark, -/area/station/maintenance/aft) "gtW" = ( /turf/closed/wall/r_wall, /area/station/command/heads_quarters/rd) @@ -20037,6 +19909,12 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/showroomfloor, /area/station/security/medical) +"guq" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "guu" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -20083,6 +19961,13 @@ /obj/structure/grille, /turf/closed/wall/r_wall/rust, /area/station/engineering/atmos) +"gve" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/iron/smooth_large, +/area/station/maintenance/department/chapel/monastery) "gvu" = ( /obj/machinery/portable_atmospherics/canister, /obj/machinery/atmospherics/components/unary/portables_connector/visible, @@ -20143,6 +20028,13 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/commons/toilet/restrooms) +"gwr" = ( +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/structure/closet/radiation, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/maintenance/disposal/incinerator) "gwI" = ( /obj/structure/chair{ dir = 8 @@ -20152,18 +20044,6 @@ }, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/bomb) -"gwO" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/obj/item/flatpack/flatpacker, -/obj/effect/turf_decal/delivery, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/science/lab) "gwV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/item/radio/intercom/directional/south, @@ -20184,13 +20064,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/engineering/circuit_workshop) -"gxe" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/small/directional/east, -/obj/structure/noticeboard/directional/east, -/obj/machinery/computer/gateway_control, -/turf/open/floor/iron/dark, -/area/station/command/gateway) "gxi" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ @@ -20218,6 +20091,16 @@ /obj/effect/turf_decal/tile/blue/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) +"gxS" = ( +/obj/machinery/vending/wardrobe/bar_wardrobe, +/obj/effect/turf_decal/delivery, +/obj/effect/decal/cleanable/cobweb, +/obj/item/radio/intercom/directional/west, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/dark, +/area/station/service/bar/backroom) "gxW" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall, @@ -20274,15 +20157,21 @@ }, /turf/open/floor/iron/dark, /area/station/command/gateway) -"gyM" = ( -/obj/machinery/light/directional/east, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/south, -/obj/effect/mapping_helpers/apc/cell_10k, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/engineering/supermatter/room) +"gyR" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/camera/directional/east{ + c_tag = "Brig Warden's Office" + }, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/light/dim/directional/east, +/turf/open/floor/iron, +/area/station/security/brig) "gyV" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, @@ -20328,6 +20217,20 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"gzZ" = ( +/obj/effect/spawner/random/trash/hobo_squat, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) +"gAd" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/kirbyplants/organic/plant21{ + desc = "After his promotion, he was transferred to Kilo Station to serve as the gateway's protector."; + name = "rodger" + }, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/command/gateway) "gAF" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -20355,12 +20258,6 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/dark, /area/station/medical/medbay/central) -"gBd" = ( -/obj/machinery/portable_atmospherics/canister/plasma, -/obj/machinery/light/directional/west, -/obj/item/radio/intercom/directional/west, -/turf/open/floor/iron/dark, -/area/station/science/ordnance/storage) "gBf" = ( /obj/machinery/door/airlock/maintenance, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -20369,27 +20266,15 @@ /obj/effect/mapping_helpers/airlock/access/any/security/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/port/lesser) -"gBi" = ( -/obj/structure/table, -/obj/item/clipboard, -/obj/item/flashlight/seclite, -/obj/item/flashlight/seclite, -/obj/item/flashlight/seclite, -/obj/effect/decal/cleanable/dirt, -/obj/item/toy/figure/secofficer, -/obj/structure/extinguisher_cabinet/directional/east, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 +"gBk" = ( +/obj/structure/reagent_dispensers/water_cooler, +/obj/machinery/button/door/directional/east{ + name = "Psychology Privacy Shutters"; + id = "psychprivacy" }, -/turf/open/floor/iron/dark, -/area/station/security/lockers) -"gBp" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/psychology) "gBx" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -20451,6 +20336,11 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"gCq" = ( +/obj/structure/chair/stool/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/commons/locker) "gCu" = ( /obj/structure/chair{ dir = 8 @@ -20588,16 +20478,6 @@ }, /turf/open/floor/iron, /area/station/engineering/hallway) -"gEs" = ( -/obj/structure/closet/secure_closet/miner, -/obj/effect/turf_decal/delivery, -/obj/item/tank/internals/oxygen/yellow, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/miningdock) "gEB" = ( /obj/machinery/disposal/bin/tagger, /obj/effect/turf_decal/bot, @@ -20760,6 +20640,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/aft) +"gGD" = ( +/obj/machinery/flasher/directional/east{ + id = "AI"; + name = "Meatbag Pacifier" + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/end{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "gGF" = ( /obj/structure/grille/broken, /obj/structure/lattice, @@ -20805,14 +20696,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"gHi" = ( -/obj/machinery/camera/directional/north{ - c_tag = "Atrium Starboard"; - name = "service camera" +"gHg" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 9 }, -/obj/effect/turf_decal/tile/red/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/service/bar/atrium) +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "gHA" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/delivery, @@ -20870,6 +20763,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"gIe" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/item/kirbyplants/organic/plant16, +/obj/effect/turf_decal/box/corners{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/central/fore) "gIo" = ( /obj/effect/decal/cleanable/dirt, /obj/item/wrench, @@ -20888,6 +20797,10 @@ /obj/effect/mapping_helpers/airlock/access/any/security/maintenance, /turf/open/floor/plating, /area/station/maintenance/department/security) +"gIv" = ( +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "gIw" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/old, @@ -20899,6 +20812,24 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"gIC" = ( +/obj/effect/turf_decal/tile/red, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) +"gID" = ( +/obj/machinery/vending/wardrobe/law_wardrobe, +/obj/structure/extinguisher_cabinet/directional/east, +/obj/effect/turf_decal/delivery, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/lawoffice) "gIE" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -20914,14 +20845,32 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"gII" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ +"gIJ" = ( +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "departures-entrance" + }, +/obj/machinery/door/airlock/public/glass{ + name = "Departure Lounge" + }, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) +"gIW" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ dir = 1 }, -/obj/effect/landmark/start/station_engineer, -/obj/effect/turf_decal/box, /turf/open/floor/iron, -/area/station/engineering/main) +/area/station/hallway/secondary/exit/departure_lounge) "gIX" = ( /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ dir = 6 @@ -20973,6 +20922,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/engine, /area/station/engineering/gravity_generator) +"gJw" = ( +/obj/structure/rack, +/obj/effect/turf_decal/bot, +/obj/item/wrench, +/obj/effect/decal/cleanable/cobweb, +/obj/effect/decal/cleanable/dirt, +/obj/item/tank/internals/oxygen, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/atmos) "gJz" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/corner{ @@ -21011,17 +20970,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, /turf/closed/wall/rust, /area/station/maintenance/disposal/incinerator) -"gJL" = ( -/obj/machinery/door/poddoor/preopen{ - id = "brigfrontdoor"; - name = "Front Security Blast Door" - }, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/firedoor, -/obj/effect/landmark/firealarm_sanity, -/turf/open/floor/iron/dark, -/area/station/security/brig) "gKd" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -21065,6 +21013,19 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/main) +"gKS" = ( +/obj/machinery/disposal/bin/tagger, +/obj/effect/turf_decal/bot, +/obj/item/radio/intercom/directional/east, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/hop) "gLm" = ( /obj/machinery/status_display/ai/directional/south, /obj/effect/decal/cleanable/dirt, @@ -21074,27 +21035,20 @@ /obj/structure/sign/departments/medbay/alt, /turf/closed/wall, /area/station/security/medical) -"gLp" = ( -/obj/structure/window/reinforced/spawner/directional/south, -/obj/structure/disposalpipe/segment, +"gLG" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/misc/asteroid/airless, -/area/space/nearstation) +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "gLH" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"gLK" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "gLO" = ( /turf/closed/wall/r_wall/rust, /area/station/security/courtroom) @@ -21123,6 +21077,14 @@ /obj/effect/turf_decal/caution/stand_clear, /turf/open/floor/iron/dark, /area/station/command/bridge) +"gMh" = ( +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/turf/open/floor/iron/stairs/left{ + dir = 4 + }, +/area/station/service/chapel) "gMp" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -21151,6 +21113,13 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/department/cargo) +"gMB" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/turf/open/floor/carpet/royalblue, +/area/station/service/chapel/office) "gMC" = ( /obj/machinery/camera/directional/south{ c_tag = "Engineering Storage"; @@ -21174,6 +21143,18 @@ /obj/structure/sign/poster/contraband/random/directional/west, /turf/open/floor/plating, /area/station/maintenance/fore) +"gML" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/siding/purple/end{ + dir = 1 + }, +/obj/machinery/duct, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "gNq" = ( /obj/machinery/vending/wardrobe/medi_wardrobe, /obj/effect/turf_decal/bot, @@ -21269,6 +21250,22 @@ /obj/structure/closet/crate, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"gOP" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/door/airlock/maintenance, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "sci-maint-passthrough" + }, +/obj/effect/mapping_helpers/airlock/access/any/science/maintenance, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/science) "gPa" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -21299,14 +21296,6 @@ "gPA" = ( /turf/closed/wall/rust, /area/station/maintenance/port/aft) -"gPC" = ( -/obj/structure/railing/corner{ - dir = 8 - }, -/obj/machinery/light/directional/east, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) "gPH" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -21411,6 +21400,19 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/cargo/office) +"gQM" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/conveyor{ + dir = 4; + id = "QMLoad2"; + name = "on ramp" + }, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/storage) "gQV" = ( /obj/machinery/camera/directional/west{ c_tag = "Atmospherics Tank - CO2"; @@ -21428,16 +21430,14 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/science/xenobiology) -"gRq" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/line, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 +"gRw" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) +/obj/machinery/airalarm/directional/east, +/obj/machinery/light/dim/directional/east, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "gRP" = ( /obj/item/kirbyplants/random, /obj/machinery/status_display/ai/directional/south, @@ -21464,6 +21464,25 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) +"gSm" = ( +/obj/item/storage/box/chemimp{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/storage/box/trackimp{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/structure/table, +/obj/structure/reagent_dispensers/wall/peppertank/directional/south, +/obj/item/storage/box/firingpins, +/obj/item/storage/box/firingpins, +/obj/item/storage/lockbox/loyalty, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/security/armory) "gSp" = ( /obj/effect/turf_decal/tile/brown/half/contrasted{ dir = 4 @@ -21488,35 +21507,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"gSC" = ( -/obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/blood, -/obj/item/reagent_containers/blood{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/item/reagent_containers/blood/a_minus, -/obj/item/reagent_containers/blood/b_minus{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/reagent_containers/blood/b_plus{ - pixel_x = 1; - pixel_y = 2 - }, -/obj/item/reagent_containers/blood/o_minus, -/obj/item/reagent_containers/blood/o_plus{ - pixel_x = -2; - pixel_y = -1 - }, -/obj/item/reagent_containers/blood/random, -/obj/item/reagent_containers/blood/random, -/obj/item/reagent_containers/blood/random, -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/medical/virology) "gSF" = ( /turf/closed/wall/r_wall, /area/station/security/prison/mess) @@ -21560,6 +21550,10 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/storage/gas) +"gTg" = ( +/obj/structure/grille/broken, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "gTr" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -21612,6 +21606,17 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"gTT" = ( +/obj/machinery/status_display/ai/directional/east, +/obj/effect/decal/cleanable/dirt, +/obj/item/kirbyplants/photosynthetic, +/obj/machinery/camera/motion/directional/south{ + c_tag = "ai_upload Consoles"; + network = list("aiupload"); + name = "upload camera" + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "gUw" = ( /obj/machinery/power/solar{ id = "aftport"; @@ -21620,14 +21625,6 @@ /obj/structure/cable, /turf/open/floor/iron/solarpanel/airless, /area/station/solars/port/aft) -"gUK" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/east, -/obj/item/kirbyplants/random, -/obj/effect/turf_decal/tile/brown/anticorner/contrasted, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "gUS" = ( /obj/effect/turf_decal/bot, /obj/effect/landmark/event_spawn, @@ -21699,6 +21696,24 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"gVy" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/duct, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "gVC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -21800,6 +21815,27 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) +"gYq" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/obj/machinery/light_switch/directional/north, +/obj/item/kirbyplants/organic/plant5, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ + dir = 10 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/engineering/atmos) "gYx" = ( /obj/machinery/door/poddoor/shutters/preopen{ dir = 1; @@ -21829,10 +21865,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos) -"gYS" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) "gYV" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -21847,20 +21879,6 @@ "gZl" = ( /turf/closed/wall/r_wall/rust, /area/station/hallway/secondary/service) -"gZm" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 1; - name = "N2O to Pure" - }, -/obj/machinery/light/directional/south, -/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/visible{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "gZu" = ( /obj/machinery/door/firedoor, /turf/open/floor/iron/dark, @@ -21872,6 +21890,12 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/paramedic) +"gZO" = ( +/obj/structure/sign/warning/electric_shock/directional/north, +/obj/structure/cable, +/obj/machinery/power/smes/full, +/turf/open/floor/circuit/red/telecomms, +/area/station/tcommsat/server) "hah" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/corner, @@ -21946,6 +21970,38 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) +"hbI" = ( +/obj/structure/rack, +/obj/effect/turf_decal/bot, +/obj/item/storage/box/bodybags{ + pixel_y = 5 + }, +/obj/item/storage/medkit/regular{ + empty = 1; + name = "First-Aid (empty)" + }, +/obj/item/storage/medkit/regular{ + empty = 1; + name = "First-Aid (empty)" + }, +/obj/item/storage/medkit/regular{ + empty = 1; + name = "First-Aid (empty)" + }, +/obj/item/stack/package_wrap, +/obj/item/healthanalyzer, +/obj/item/healthanalyzer, +/obj/item/healthanalyzer, +/obj/machinery/camera/directional/north{ + c_tag = "Robotics Lab"; + name = "science camera"; + network = list("ss13","rd") + }, +/obj/item/radio/intercom/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/science/robotics/lab) "hbT" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -21957,6 +22013,16 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"hct" = ( +/obj/machinery/computer/pandemic, +/obj/effect/turf_decal/delivery, +/obj/item/radio/intercom/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/dark, +/area/station/medical/virology) "hcH" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/delivery, @@ -22099,13 +22165,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"heX" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "hfl" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -22188,6 +22247,16 @@ /obj/structure/sign/warning/secure_area, /turf/closed/wall/r_wall, /area/station/maintenance/port/greater) +"hgS" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "hgW" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -22219,6 +22288,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/security/prison) +"hhy" = ( +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/camera/autoname/directional/north, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "hhE" = ( /obj/item/kirbyplants/organic/plant18, /obj/machinery/light_switch/directional/west{ @@ -22238,6 +22316,18 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lab) +"hhJ" = ( +/obj/machinery/flasher/directional/west{ + id = "AI"; + name = "Meatbag Pacifier" + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/end{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "hhX" = ( /obj/item/kirbyplants/organic/plant21, /obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ @@ -22262,22 +22352,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) -"hih" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/pharmacy) "hiQ" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -22293,30 +22367,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/maintenance/starboard/aft) -"hja" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/radio/intercom/directional/south, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/wood, -/area/station/service/bar/atrium) -"hjb" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/light/small/directional/south, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 8 - }, -/turf/open/floor/plating, -/area/station/maintenance/solars/starboard/fore) "hjh" = ( /obj/machinery/camera/directional/south{ c_tag = "Medical Operating Theater A"; @@ -22362,25 +22412,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) -"hjt" = ( -/obj/machinery/light/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) -"hjD" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/small/directional/west, -/obj/machinery/status_display/ai/directional/west, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/command/vault) "hjE" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/stripes/corner{ @@ -22532,12 +22563,6 @@ /obj/effect/mapping_helpers/airlock/access/all/security/armory, /turf/open/floor/iron/dark, /area/station/security/armory) -"hkW" = ( -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 8 - }, -/turf/open/floor/plating, -/area/station/maintenance/disposal/incinerator) "hlb" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/holosign/barrier/atmos, @@ -22734,6 +22759,18 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) +"hob" = ( +/obj/machinery/airalarm/directional/west, +/obj/machinery/rnd/production/techfab/department/service, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/service) "hoz" = ( /obj/structure/table/wood, /obj/structure/mirror/directional/west, @@ -22817,27 +22854,9 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/brig) -"hpu" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/siding/purple{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/lobby) "hpv" = ( /turf/closed/wall, /area/station/medical/surgery/theatre) -"hpz" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/engineering/hallway) "hpC" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -22848,6 +22867,15 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/science) +"hpD" = ( +/obj/machinery/camera/directional/north{ + c_tag = "Atrium Starboard"; + name = "service camera" + }, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/service/bar/atrium) "hpG" = ( /obj/effect/turf_decal/box, /obj/machinery/power/solar{ @@ -22984,6 +23012,19 @@ /obj/structure/flora/bush/grassy/style_random, /turf/open/floor/grass, /area/station/service/hydroponics) +"hrg" = ( +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id = "ceprivate"; + name = "Chief Engineer's Privacy Shutters" + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/door/firedoor, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "hri" = ( /obj/structure/dresser, /turf/open/floor/wood, @@ -23137,11 +23178,6 @@ dir = 4 }, /area/station/hallway/primary/fore) -"huj" = ( -/obj/effect/turf_decal/box, -/obj/effect/spawner/random/gizmo, -/turf/open/floor/engine, -/area/station/science/auxlab) "huo" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -23233,6 +23269,17 @@ /obj/structure/barricade/wooden/crude, /turf/open/floor/plating/rust, /area/station/security/prison) +"hvL" = ( +/obj/structure/table, +/obj/item/stack/medical/mesh, +/obj/item/stack/medical/suture, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/wall_healer/directional/north, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/medical/medbay/lobby) "hwr" = ( /obj/structure/disposaloutlet{ dir = 8 @@ -23259,16 +23306,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/security/office) -"hwN" = ( -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/effect/turf_decal/delivery, -/obj/structure/table, -/obj/item/storage/box, -/obj/item/radio/off{ - pixel_x = 6 - }, -/turf/open/floor/iron, -/area/station/engineering/lobby) "hwY" = ( /obj/machinery/air_sensor/air_tank, /turf/open/floor/engine/air, @@ -23309,21 +23346,6 @@ /obj/effect/landmark/navigate_destination/chapel, /turf/open/floor/iron/grimy, /area/station/hallway/primary/fore) -"hxi" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/machinery/door/airlock/atmos{ - name = "Incinerator" - }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ - dir = 6 - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/atmos, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/engineering/atmos) "hxt" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -23340,22 +23362,25 @@ }, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"hxx" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ +"hxF" = ( +/obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) -"hxL" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/mapping_helpers/broken_floor, /obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/structure/rack, -/obj/effect/decal/cleanable/cobweb, -/obj/item/storage/medkit/emergency, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 1 + }, /turf/open/floor/plating, -/area/station/maintenance/port/lesser) +/area/station/maintenance/disposal) "hxP" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -23367,6 +23392,40 @@ /obj/structure/extinguisher_cabinet/directional/west, /turf/open/floor/carpet/blue, /area/station/command/heads_quarters/hop) +"hxZ" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/machinery/camera/autoname/directional/east, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 8 + }, +/obj/machinery/light/small/directional/east, +/turf/open/floor/wood, +/area/station/commons/vacant_room/commissary) +"hyd" = ( +/obj/structure/chair{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/light/small/directional/west, +/turf/open/floor/plating, +/area/station/security/execution/education) +"hyk" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/obj/machinery/portable_atmospherics/canister/nitrogen, +/obj/effect/turf_decal/box, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron/dark, +/area/station/engineering/supermatter/room) "hyl" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -23375,6 +23434,20 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central) +"hyp" = ( +/obj/machinery/light/small/directional/west, +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "hyq" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -23406,6 +23479,11 @@ }, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"hzx" = ( +/obj/machinery/portable_atmospherics/canister/nitrogen, +/obj/machinery/light/cold/directional/south, +/turf/open/floor/iron/dark, +/area/station/science/ordnance/storage) "hzC" = ( /obj/structure/flora/rock/icy/style_2, /turf/open/misc/asteroid/airless, @@ -23441,19 +23519,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"hAF" = ( -/obj/structure/table/wood, -/obj/machinery/reagentgrinder{ - pixel_y = 5 - }, -/obj/item/toy/figure/bartender{ - pixel_x = -7 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/bar/backroom) "hAJ" = ( /turf/closed/wall, /area/station/engineering/storage/tech) @@ -23464,6 +23529,25 @@ "hAP" = ( /turf/closed/wall/r_wall, /area/station/security/evidence) +"hAT" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/structure/table, +/obj/item/experi_scanner{ + pixel_x = -4 + }, +/obj/item/experi_scanner{ + pixel_x = 4 + }, +/obj/machinery/requests_console/directional/north{ + department = "Research Lab"; + name = "Research Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/ore_update, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/mapping_helpers/requests_console/information, +/turf/open/floor/iron/dark, +/area/station/science/research) "hBa" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/corner{ @@ -23590,17 +23674,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/warden) -"hCl" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/obj/machinery/newscaster/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/lockers) "hCr" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -23707,16 +23780,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/engineering/atmos) -"hDX" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "hEa" = ( /obj/item/kirbyplants/organic/applebush, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -23726,6 +23789,17 @@ dir = 1 }, /area/station/hallway/primary/central/fore) +"hEh" = ( +/obj/structure/rack, +/obj/effect/spawner/random/techstorage/service_all, +/obj/effect/turf_decal/bot, +/obj/machinery/status_display/ai/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/effect/spawner/random/techstorage/arcade_boards, +/obj/effect/spawner/random/techstorage/arcade_boards, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "hEk" = ( /obj/structure/table, /obj/item/stack/package_wrap, @@ -23753,6 +23827,19 @@ /obj/item/papercutter, /turf/open/floor/iron/dark, /area/station/cargo/sorting) +"hEq" = ( +/obj/structure/sign/poster/official/anniversary_vintage_reprint/directional/north, +/obj/machinery/camera/directional/north{ + c_tag = "Chemistry North"; + network = list("ss13","medbay") + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "hEw" = ( /obj/structure/table/wood, /obj/item/paper_bin{ @@ -23777,19 +23864,6 @@ /obj/structure/flora/bush/pale/style_random, /turf/open/misc/asteroid, /area/station/maintenance/aft) -"hEH" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/disposal/bin/tagger, -/obj/machinery/light/directional/east, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "hEI" = ( /obj/machinery/pdapainter/medbay, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -23797,14 +23871,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/cmo) -"hEQ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "hEU" = ( /obj/structure/disposaloutlet, /obj/structure/disposalpipe/trunk{ @@ -23833,6 +23899,17 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/pumproom) +"hFf" = ( +/obj/structure/bed, +/obj/machinery/iv_drip, +/obj/item/bedsheet/medical, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/security/medical) "hFm" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -23902,6 +23979,20 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/storage) +"hGG" = ( +/obj/structure/table, +/obj/item/clipboard, +/obj/item/flashlight/seclite, +/obj/item/flashlight/seclite, +/obj/item/flashlight/seclite, +/obj/effect/decal/cleanable/dirt, +/obj/item/toy/figure/secofficer, +/obj/structure/extinguisher_cabinet/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/security/lockers) "hGX" = ( /obj/effect/turf_decal/bot, /obj/structure/closet/emcloset, @@ -23912,6 +24003,12 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) +"hHf" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/machinery/meter, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/cryo) "hHg" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 @@ -23941,23 +24038,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/execution/transfer) -"hHt" = ( -/obj/structure/table, -/obj/item/storage/crayons, -/obj/item/clothing/under/color/grey{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/clothing/under/color/grey, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) "hHL" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -23982,25 +24062,15 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/port) -"hHP" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/holopad/secure, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, +"hIp" = ( /obj/effect/turf_decal/stripes/line{ - dir = 1 + dir = 4 }, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"hIm" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +/turf/open/floor/plating, +/area/station/maintenance/starboard/aft) "hIq" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -24017,6 +24087,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood, /area/station/maintenance/starboard/fore) +"hIy" = ( +/obj/machinery/door/airlock/public/glass{ + name = "Auxiliary Dock" + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "hIS" = ( /obj/structure/rack, /obj/effect/spawner/random/techstorage/rnd_all, @@ -24037,17 +24116,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/security/checkpoint/supply) -"hJm" = ( -/obj/item/radio/intercom/directional/west, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "hJp" = ( /obj/machinery/airalarm/directional/south, /obj/effect/turf_decal/bot, @@ -24100,15 +24168,6 @@ }, /turf/open/floor/iron/smooth_large, /area/station/maintenance/department/chapel/monastery) -"hJD" = ( -/obj/structure/rack, -/obj/effect/spawner/random/techstorage/security_all, -/obj/effect/turf_decal/bot, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "hJQ" = ( /obj/structure/chair/sofa/right/maroon{ dir = 4 @@ -24189,14 +24248,12 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"hKP" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 6 +"hKF" = ( +/obj/structure/chair/office{ + dir = 1 }, -/obj/structure/sign/poster/official/enlist/directional/east, -/turf/open/floor/plating, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) "hKQ" = ( /obj/structure/table/wood, @@ -24220,6 +24277,9 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/security/checkpoint/supply) +"hKW" = ( +/turf/open/floor/plating, +/area/station/maintenance/fore) "hLj" = ( /obj/machinery/telecomms/server/presets/security, /turf/open/floor/circuit/green/telecomms/mainframe, @@ -24239,6 +24299,34 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/engineering/circuit_workshop) +"hLs" = ( +/obj/structure/closet/crate/freezer, +/obj/item/reagent_containers/blood, +/obj/item/reagent_containers/blood{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/item/reagent_containers/blood/a_minus, +/obj/item/reagent_containers/blood/b_minus{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/reagent_containers/blood/b_plus{ + pixel_x = 1; + pixel_y = 2 + }, +/obj/item/reagent_containers/blood/o_minus, +/obj/item/reagent_containers/blood/o_plus{ + pixel_x = -2; + pixel_y = -1 + }, +/obj/item/reagent_containers/blood/random, +/obj/item/reagent_containers/blood/random, +/obj/item/reagent_containers/blood/random, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/medical/virology) "hLz" = ( /obj/structure/disposalpipe/sorting/mail/flip{ dir = 8; @@ -24253,19 +24341,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/storage) -"hLH" = ( -/obj/machinery/computer/security/mining{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/status_display/evac/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/cargo/miningdock) "hLO" = ( /obj/structure/plasticflaps, /obj/machinery/conveyor{ @@ -24282,6 +24357,16 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"hLY" = ( +/obj/machinery/status_display/ai/directional/east, +/obj/machinery/camera/directional/north{ + c_tag = "ai_upload Turrets"; + name = "upload camera"; + network = list("aiupload") + }, +/obj/item/kirbyplants/photosynthetic, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "hMf" = ( /obj/structure/table/reinforced, /obj/structure/desk_bell{ @@ -24323,12 +24408,30 @@ "hMN" = ( /turf/closed/wall, /area/station/service/chapel/funeral) +"hMU" = ( +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "hMW" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) +"hMY" = ( +/obj/item/kirbyplants/organic/plant16, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "hMZ" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/poddoor/preopen{ @@ -24345,6 +24448,18 @@ /obj/effect/mapping_helpers/airlock/access/all/command/general, /turf/open/floor/iron/dark, /area/station/maintenance/department/bridge) +"hNh" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/port) "hNi" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -24356,13 +24471,6 @@ dir = 8 }, /area/station/hallway/primary/starboard) -"hNj" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible, -/obj/effect/turf_decal/stripes/line, -/obj/machinery/light/directional/south, -/obj/structure/cable, -/turf/open/floor/engine, -/area/station/engineering/supermatter/room) "hNy" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, @@ -24381,6 +24489,17 @@ /obj/structure/marker_beacon/burgundy, /turf/open/space/basic, /area/space/nearstation) +"hNG" = ( +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/fore) "hNI" = ( /turf/closed/wall/rust, /area/station/security/detectives_office) @@ -24456,21 +24575,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"hOH" = ( -/obj/machinery/firealarm/directional/south, -/obj/machinery/light/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/commons/toilet/restrooms) "hOJ" = ( /obj/machinery/door/airlock/maintenance, /obj/effect/mapping_helpers/airlock/abandoned, @@ -24488,23 +24592,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/dock) -"hON" = ( -/obj/machinery/airalarm/directional/north, -/obj/machinery/camera/directional/north{ - c_tag = "Engineering Desk"; - name = "engineering camera"; - network = list("ss13","engine") - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/siding/yellow{ - dir = 8 - }, -/obj/machinery/rnd/production/protolathe/department/engineering, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "hOU" = ( /obj/machinery/door/airlock/external{ name = "Security Escape Pod"; @@ -24606,18 +24693,22 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"hQa" = ( -/obj/item/radio/intercom/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/bot, -/obj/machinery/holopad, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) +"hQd" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/computer/mechpad{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/button/door/directional/south{ + id = "Skynet_launch"; + name = "Mech Bay Door Control" + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/science/robotics/mechbay) "hQg" = ( /obj/item/kirbyplants/organic/plant3, /obj/structure/disposalpipe/segment, @@ -24664,20 +24755,17 @@ /obj/machinery/power/apc/auto_name/directional/east, /turf/open/floor/engine/telecomms, /area/station/tcommsat/server) -"hRa" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/siding/yellow{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ +"hRe" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/disposal/bin/tagger, +/obj/structure/disposalpipe/trunk{ dir = 1 }, -/obj/effect/turf_decal/tile/red{ +/obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 1 }, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) +/turf/open/floor/iron/dark, +/area/station/commons/locker) "hRf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -24727,14 +24815,16 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/storage) -"hRO" = ( -/obj/effect/turf_decal/box/corners{ - dir = 1 +"hRJ" = ( +/obj/structure/chair/office/light{ + dir = 4 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 + }, +/obj/machinery/computer/security/telescreen/ordnance/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "hRU" = ( /obj/structure/table, /obj/machinery/cell_charger, @@ -24835,6 +24925,36 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"hTn" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/science) +"hTo" = ( +/obj/item/tank/internals/emergency_oxygen/engi{ + pixel_x = -5 + }, +/obj/item/tank/internals/emergency_oxygen/engi{ + pixel_x = 5 + }, +/obj/item/geiger_counter, +/obj/item/geiger_counter, +/obj/structure/table, +/obj/structure/sign/warning/xeno_mining/directional/west, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/dark, +/area/station/engineering/supermatter/room) "hTz" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 8 @@ -24870,6 +24990,19 @@ /obj/structure/plasticflaps/opaque, /turf/open/floor/plating, /area/station/security/prison/shower) +"hUn" = ( +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/obj/structure/table, +/obj/item/storage/box/disks{ + pixel_y = 5 + }, +/obj/item/clothing/gloves/latex, +/obj/item/clothing/gloves/latex, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/iron/dark, +/area/station/science/research) "hUw" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -24963,14 +25096,6 @@ /obj/item/toy/plush/nukeplushie, /turf/open/floor/plating/airless, /area/space/nearstation) -"hVF" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/event_spawn, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "hVH" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -24991,11 +25116,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"hVT" = ( -/obj/structure/flora/bush/flowers_br/style_random, -/obj/machinery/light/directional/east, -/turf/open/floor/grass, -/area/station/service/chapel) "hVU" = ( /obj/effect/spawner/random/engineering/tracking_beacon, /obj/effect/decal/cleanable/dirt, @@ -25032,6 +25152,20 @@ dir = 8 }, /area/station/hallway/primary/fore) +"hXa" = ( +/obj/structure/table, +/obj/item/storage/box/deputy, +/obj/item/stack/package_wrap, +/obj/item/hand_labeler, +/obj/machinery/camera/directional/west{ + c_tag = "Warden's Office" + }, +/obj/structure/reagent_dispensers/wall/peppertank/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/security/warden) "hXe" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -25040,6 +25174,24 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/hallway/primary/port) +"hXg" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, +/obj/machinery/space_heater, +/obj/machinery/airalarm/directional/east, +/obj/structure/sign/poster/contraband/fun_police/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/security/execution/education) "hXK" = ( /obj/structure/sign/directions/security{ pixel_y = -40 @@ -25073,18 +25225,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/storage/gas) -"hXY" = ( -/obj/effect/turf_decal/bot, -/obj/structure/closet/secure_closet/personal, -/obj/item/storage/backpack, -/obj/item/storage/backpack/satchel, -/obj/item/clothing/suit/hooded/wintercoat, -/obj/item/clothing/shoes/winterboots, -/obj/machinery/airalarm/directional/north, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/locker) "hYo" = ( /obj/structure/bed, /obj/item/bedsheet/red, @@ -25094,6 +25234,13 @@ /obj/effect/landmark/start/prisoner, /turf/open/floor/iron, /area/station/security/prison/safe) +"hYr" = ( +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "hYu" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -25160,16 +25307,6 @@ /obj/item/gun/grenadelauncher, /turf/open/floor/iron/dark, /area/station/security/armory) -"hZi" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/purple, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/command/heads_quarters/rd) "hZn" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall, @@ -25238,6 +25375,23 @@ }, /turf/open/floor/iron, /area/station/engineering/lobby) +"hZU" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/camera/directional/east{ + c_tag = "E.V.A. Storage"; + name = "command camera" + }, +/obj/machinery/suit_storage_unit/standard_unit{ + desc = "An industrial suit storage device carrying retro space suits. Neat!"; + helmet_type = /obj/item/clothing/head/helmet/space; + suit_type = /obj/item/clothing/suit/space + }, +/obj/machinery/status_display/ai/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/eva) "iai" = ( /obj/machinery/vending/hydronutrients, /obj/structure/railing{ @@ -25373,13 +25527,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/security/armory) -"ibL" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/structure/table, -/obj/item/surgery_tray/full, -/turf/open/floor/iron/dark, -/area/station/medical/surgery/theatre) "ibN" = ( /obj/effect/turf_decal/box/corners{ dir = 4 @@ -25401,6 +25548,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"icm" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/dark, +/area/station/science/auxlab) "icp" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -25498,6 +25651,18 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/security/execution/transfer) +"idf" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/commons/fitness/recreation) "idh" = ( /obj/structure/disposalpipe/sorting/mail/flip{ dir = 4; @@ -25630,57 +25795,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/research) -"ifu" = ( -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/cobweb, -/obj/item/book{ - desc = "An undeniably handy book."; - icon_state = "bookknock"; - name = "A Simpleton's Guide to Safe-cracking with Stethoscopes" - }, -/obj/item/stack/spacecash/c500{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/stack/spacecash/c1000, -/obj/item/gun/ballistic/automatic/pistol/deagle/gold, -/obj/machinery/status_display/ai/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/structure/safe/vault, -/turf/open/floor/iron/dark, -/area/station/command/vault) -"ifv" = ( -/obj/structure/table, -/obj/item/mmi, -/obj/item/bodypart/chest/robot{ - pixel_y = 4 - }, -/obj/item/bodypart/leg/right/robot{ - pixel_x = 6 - }, -/obj/item/bodypart/arm/right/robot{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/bodypart/leg/left/robot{ - pixel_x = -6 - }, -/obj/item/bodypart/arm/left/robot{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/bodypart/head/robot, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "ifw" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -25782,6 +25896,17 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"iiG" = ( +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/engineering/main) "iiQ" = ( /obj/effect/turf_decal/box, /obj/structure/toilet{ @@ -25859,19 +25984,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"ijC" = ( -/obj/structure/closet/secure_closet/chemical, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/light/directional/west, -/obj/structure/extinguisher_cabinet/directional/west, -/obj/item/storage/box/pillbottles, -/obj/item/storage/box/pillbottles, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/medical/pharmacy) "ijF" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -26040,6 +26152,15 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science) +"ikI" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/disposal/bin/tagger, +/obj/item/radio/intercom/directional/east, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/hos) "ikO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -26094,21 +26215,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/lobby) -"ilv" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/computer/records/security{ - dir = 4 - }, -/obj/machinery/newscaster/directional/west, -/obj/machinery/camera/directional/west{ - c_tag = "Security Office Computers" - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/machinery/light/small/directional/south, -/turf/open/floor/iron/dark, -/area/station/security/office) "ilI" = ( /obj/effect/decal/cleanable/blood/old, /obj/machinery/door/airlock/external{ @@ -26219,16 +26325,14 @@ /obj/machinery/airalarm/directional/south, /turf/open/floor/iron/checker, /area/station/security/mechbay) +"iol" = ( +/obj/structure/railing/corner, +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "ios" = ( /turf/closed/wall/rust, /area/station/service/kitchen) -"ioK" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/computer/upload/borg/no_lock{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "ioQ" = ( /turf/closed/wall, /area/station/engineering/circuit_workshop) @@ -26246,6 +26350,20 @@ /obj/structure/cable, /turf/open/floor/plating, /area/space/nearstation) +"ioW" = ( +/obj/structure/reagent_dispensers/fueltank, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "ipb" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/corner{ @@ -26281,6 +26399,14 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/hallway/primary/aft) +"ipn" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/machinery/newscaster/directional/south, +/obj/machinery/light/cold/dim/directional/south, +/turf/open/floor/iron/kitchen_coldroom/freezerfloor, +/area/station/medical/coldroom) "ipo" = ( /obj/machinery/chem_master/condimaster{ desc = "Used to separate out liquids - useful for purifying botanical extracts. Also dispenses condiments."; @@ -26292,20 +26418,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) -"ipA" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/highsecurity{ - name = "AI Upload" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/obj/effect/mapping_helpers/airlock/access/all/command/ai_upload, -/obj/effect/landmark/navigate_destination/aiupload, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "ipC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -26371,17 +26483,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"ipQ" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/south, -/obj/machinery/light_switch/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted, -/obj/effect/turf_decal/tile/purple, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) "iqd" = ( /obj/structure/rack, /obj/effect/spawner/random/techstorage/medical_all, @@ -26403,12 +26504,27 @@ }, /turf/open/misc/asteroid/airless, /area/space/nearstation) +"iqC" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 2; + id = "chemistry_shutters_2"; + name = "Chemistry Hall Shutters" + }, +/turf/open/floor/plating, +/area/station/medical/paramedic) "iqN" = ( /obj/effect/turf_decal/stripes/corner, /obj/effect/mapping_helpers/burnt_floor, /obj/effect/spawner/random/structure/crate, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"iqO" = ( +/mob/living/carbon/human/species/monkey, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/small/directional/west, +/turf/open/floor/grass, +/area/station/science/auxlab) "iqQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/airalarm/directional/south, @@ -26452,54 +26568,11 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/general, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/aft) -"irE" = ( -/obj/machinery/teleport/station, -/obj/machinery/status_display/evac/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/foyer) -"irG" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/obj/machinery/light_switch/directional/north, -/obj/machinery/light/directional/north, -/obj/item/kirbyplants/organic/plant5, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ - dir = 10 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/atmos) "irJ" = ( /obj/effect/spawner/random/structure/crate, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) -"irL" = ( -/obj/structure/table, -/obj/machinery/processor{ - pixel_y = 6 - }, -/obj/effect/turf_decal/delivery, -/obj/machinery/light/small/directional/south, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/kitchen) "irM" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/yellow/half/contrasted{ @@ -26557,6 +26630,16 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) +"ist" = ( +/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/machinery/light/warm/directional/east, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "isD" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -26575,13 +26658,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"isO" = ( -/obj/structure/closet/secure_closet/atmospherics, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/maintenance/disposal/incinerator) "isS" = ( /turf/open/floor/iron/grimy, /area/station/security/prison) @@ -26827,25 +26903,6 @@ }, /turf/open/floor/iron, /area/station/construction/mining/aux_base) -"ixj" = ( -/obj/machinery/light/directional/west, -/obj/machinery/requests_console/directional/west{ - department = "Engineering"; - name = "Engineering Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/information, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/bot, -/obj/machinery/microwave/engineering/cell_included, -/obj/structure/table, -/obj/item/storage/box/donkpockets{ - pixel_y = 5 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "ixn" = ( /obj/structure/railing/corner{ dir = 1 @@ -26884,16 +26941,13 @@ "ixB" = ( /turf/closed/wall/r_wall, /area/station/maintenance/department/chapel/monastery) -"ixN" = ( -/obj/structure/sign/painting/library{ - pixel_x = 32 - }, -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) +"ixE" = ( +/obj/structure/window/spawner/directional/west, +/obj/structure/window/spawner/directional/east, +/obj/structure/window/spawner/directional/north, +/obj/structure/flora/bush/fullgrass/style_random, +/turf/open/floor/grass, +/area/station/medical/medbay/central) "ixO" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/blood/old, @@ -26901,6 +26955,19 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) +"ixR" = ( +/obj/machinery/airalarm/directional/north, +/obj/machinery/navbeacon{ + codes_txt = "delivery;dir=8"; + location = "QM #1" + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/effect/turf_decal/delivery, +/obj/effect/decal/cleanable/blood/oil, +/mob/living/basic/bot/mulebot, +/turf/open/floor/iron/dark, +/area/station/cargo/warehouse) "iyd" = ( /obj/machinery/door/poddoor/incinerator_atmos_main, /turf/open/floor/engine, @@ -26917,13 +26984,6 @@ }, /turf/open/floor/iron/dark, /area/station/ai/satellite/atmos) -"iyk" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "iyr" = ( /obj/structure/lattice/catwalk, /obj/machinery/door/airlock/external{ @@ -26952,20 +27012,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/fore) -"iyI" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/camera/autoname/directional/east, -/turf/open/floor/engine, -/area/station/science/auxlab) -"iyM" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/red, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "iyT" = ( /obj/structure/table, /obj/item/folder/red, @@ -27043,19 +27089,24 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/fore) -"izM" = ( -/obj/effect/spawner/random/vending/colavend, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/port) "izU" = ( /obj/effect/turf_decal/delivery, /obj/machinery/vending/clothing, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) +"iAf" = ( +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/structure/closet/crate, +/obj/structure/grille/broken, +/obj/item/clothing/gloves/color/fyellow, +/obj/item/flashlight, +/obj/item/flashlight/flare, +/obj/item/relic, +/obj/structure/sign/poster/contraband/random/directional/west, +/obj/machinery/light/small/directional/south, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "iAo" = ( /obj/structure/lattice, /obj/structure/sign/warning/secure_area/directional/south, @@ -27203,6 +27254,15 @@ }, /turf/open/misc/asteroid, /area/station/maintenance/port/greater) +"iCf" = ( +/obj/structure/railing/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "iCK" = ( /obj/effect/turf_decal/bot, /obj/machinery/conveyor{ @@ -27452,16 +27512,6 @@ }, /turf/open/floor/bronze, /area/station/maintenance/department/chapel) -"iGD" = ( -/obj/structure/table, -/obj/machinery/light/directional/west, -/obj/machinery/status_display/ai/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/foyer) "iGG" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/structure/crate, @@ -27488,6 +27538,22 @@ }, /turf/open/floor/iron, /area/station/science/robotics/mechbay) +"iGQ" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer4{ + dir = 4 + }, +/obj/effect/turf_decal/delivery, +/obj/machinery/portable_atmospherics/canister/air, +/obj/machinery/status_display/evac/directional/west, +/obj/effect/turf_decal/stripes/end{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/atmos) "iGT" = ( /obj/structure/table, /obj/item/storage/box/lights/mixed{ @@ -27611,6 +27677,15 @@ /obj/machinery/wall_healer/directional/south, /turf/open/floor/iron, /area/station/cargo/storage) +"iID" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only, +/turf/open/floor/iron/dark, +/area/station/maintenance/fore) "iIF" = ( /obj/structure/table/glass, /obj/item/clothing/gloves/latex, @@ -27624,32 +27699,12 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) -"iIP" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) -"iIX" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/structure/filingcabinet/employment, -/obj/item/radio/intercom/directional/south, -/obj/machinery/button/door/directional/east{ - id = "lawyer_shutters"; - name = "Law Office Shutters Toggle"; - req_access = list("lawyer") - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/lawoffice) +"iIS" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/machinery/light/cold/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "iJf" = ( /obj/machinery/mech_bay_recharge_port, /obj/structure/sign/warning/no_smoking{ @@ -27716,6 +27771,18 @@ }, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"iJL" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/components/binary/pump/on/layer4{ + dir = 4; + name = "Airlock Pump"; + target_pressure = 300; + hide = 1 + }, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "iJN" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, @@ -27729,23 +27796,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room) -"iJQ" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "iJR" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -27802,6 +27852,11 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) +"iKV" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "iKX" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -27819,6 +27874,16 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"iLd" = ( +/obj/structure/table, +/obj/item/storage/box/bodybags{ + pixel_x = 6 + }, +/obj/item/storage/box/gloves{ + pixel_x = -5 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "iLj" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -27841,6 +27906,35 @@ /obj/item/storage/fancy/candle_box, /turf/open/floor/iron/dark, /area/station/service/chapel) +"iLF" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) +"iLI" = ( +/obj/machinery/firealarm/directional/east, +/obj/machinery/computer/dna_console{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/dark, +/area/station/science/genetics) +"iLN" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "iLQ" = ( /obj/machinery/portable_atmospherics/canister, /obj/machinery/atmospherics/components/unary/portables_connector/visible{ @@ -27874,16 +27968,25 @@ dir = 8 }, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/iron/showroomfloor, -/area/station/science/genetics) -"iMr" = ( -/obj/structure/reagent_dispensers/water_cooler, -/obj/machinery/button/door/directional/east{ - name = "Psychology Privacy Shutters"; - id = "psychprivacy" +/obj/structure/table, +/obj/item/storage/box/disks{ + pixel_x = 5; + pixel_y = 9 + }, +/obj/item/storage/box/bodybags{ + pixel_x = -8; + pixel_y = 9 + }, +/obj/item/storage/box/gloves{ + pixel_x = 6; + pixel_y = 1 + }, +/obj/item/flesh_shears{ + pixel_x = -10; + pixel_y = -5 }, /turf/open/floor/iron/showroomfloor, -/area/station/medical/psychology) +/area/station/science/genetics) "iMu" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 1 @@ -27943,6 +28046,19 @@ /obj/machinery/vending/boozeomat, /turf/closed/wall, /area/station/service/bar) +"iMS" = ( +/obj/machinery/computer/security{ + dir = 4 + }, +/obj/effect/turf_decal/bot, +/obj/item/radio/intercom/directional/west, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "iMX" = ( /turf/open/floor/engine/air, /area/station/engineering/atmos) @@ -27977,6 +28093,27 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/atmos) +"iNz" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) +"iNH" = ( +/obj/structure/chair/sofa/right/maroon{ + dir = 8 + }, +/obj/structure/railing{ + dir = 4 + }, +/obj/machinery/newscaster/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/bar/atrium) "iNK" = ( /obj/machinery/status_display/evac/directional/south, /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ @@ -28081,6 +28218,14 @@ /obj/machinery/power/apc/auto_name/directional/north, /turf/open/floor/iron/recharge_floor, /area/station/science/robotics/mechbay) +"iPG" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/structure/window/spawner/directional/east, +/obj/structure/table/optable, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "iPY" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -28107,24 +28252,29 @@ /obj/machinery/duct, /turf/open/floor/plating, /area/station/security/prison/garden) -"iQn" = ( -/obj/structure/table, -/obj/item/stack/sheet/iron/ten, -/obj/item/stack/cable_coil, -/obj/item/storage/backpack/duffelbag/sec/surgery{ - pixel_y = 5 +"iQh" = ( +/obj/machinery/computer/communications{ + dir = 1 }, -/obj/item/wirecutters, -/obj/machinery/firealarm/directional/east, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 +/obj/structure/plaque/static_plaque/golden/captain{ + pixel_y = -32 }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/warm/directional/south, /turf/open/floor/iron/dark, -/area/station/security/execution/education) +/area/station/command/heads_quarters/captain) "iQt" = ( /turf/closed/wall/rust, /area/station/commons/fitness/recreation) +"iQD" = ( +/obj/machinery/atmospherics/components/unary/thermomachine/freezer, +/obj/item/radio/intercom/directional/north, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/cryo) "iQK" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/cleanable/dirt, @@ -28245,18 +28395,6 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) -"iSU" = ( -/obj/machinery/computer/security{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/item/radio/intercom/directional/west, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "iTj" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters{ @@ -28311,6 +28449,12 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"iTF" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/light/small/red/directional/south, +/turf/open/floor/engine, +/area/station/command/vault) "iTL" = ( /obj/structure/chair{ dir = 4 @@ -28339,11 +28483,13 @@ /obj/effect/turf_decal/stripes/corner, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/bomb) -"iTZ" = ( -/obj/structure/transit_tube/crossing/horizontal, -/obj/structure/lattice, -/turf/open/space/basic, -/area/space/nearstation) +"iUc" = ( +/obj/effect/turf_decal/siding/wood, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/turf/open/floor/carpet/royalblue, +/area/station/service/chapel/office) "iUm" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -28420,14 +28566,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron/dark, /area/station/cargo/miningdock) -"iVy" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/beacon, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "iVF" = ( /obj/structure/table/wood, /obj/item/clipboard, @@ -28446,6 +28584,12 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"iVK" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/service/chapel/dock) "iVL" = ( /turf/closed/mineral/random/labormineral, /area/station/hallway/secondary/entry) @@ -28518,6 +28662,17 @@ }, /turf/open/floor/plating/airless, /area/station/hallway/secondary/exit/departure_lounge) +"iWV" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/disposal/bin/tagger, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/virology) "iXd" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -28528,6 +28683,38 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"iXh" = ( +/obj/structure/table/wood/fancy, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/machinery/firealarm/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/chapel) +"iXo" = ( +/obj/machinery/requests_console/directional/west{ + department = "Engineering"; + name = "Engineering Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/information, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/bot, +/obj/machinery/microwave/engineering/cell_included, +/obj/structure/table, +/obj/item/storage/box/donkpockets{ + pixel_y = 5 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "iXD" = ( /obj/machinery/camera/directional/east{ c_tag = "Xenobiology Cell 3"; @@ -28537,6 +28724,12 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/engine, /area/station/science/xenobiology) +"iXL" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/storage) "iXM" = ( /obj/structure/table, /obj/effect/turf_decal/stripes/line{ @@ -28650,6 +28843,13 @@ /obj/machinery/vending/drugs, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) +"iYD" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/storage) "iYI" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 @@ -28702,16 +28902,6 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/general, /turf/open/floor/iron/dark, /area/station/medical/storage) -"iZv" = ( -/obj/structure/extinguisher_cabinet/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/tile/red/anticorner/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/processing) "iZB" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -28725,6 +28915,17 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"iZN" = ( +/obj/structure/sign/painting/library{ + pixel_x = 32 + }, +/obj/machinery/photocopier, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/box/white, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/library) "iZV" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -28735,27 +28936,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"iZX" = ( -/obj/structure/chair, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/north, -/obj/machinery/firealarm/directional/north, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) -"jam" = ( -/obj/machinery/light/directional/north, -/obj/machinery/medical_kiosk{ - pixel_y = 4 +"jak" = ( +/obj/structure/lattice, +/obj/structure/disposalpipe/segment{ + dir = 4 }, -/obj/effect/turf_decal/box, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/medbay/lobby) +/obj/structure/transit_tube/diagonal/topleft, +/turf/open/space/basic, +/area/space/nearstation) "jas" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -28919,12 +29110,17 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/command/bridge) -"jcN" = ( -/obj/structure/bookcase/random/fiction, -/obj/item/radio/intercom/directional/north, -/obj/machinery/light/directional/north, -/turf/open/floor/wood, -/area/station/service/library) +"jdd" = ( +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/turf/open/floor/iron/showroomfloor, +/area/station/security/warden) +"jdi" = ( +/obj/structure/closet/secure_closet/atmospherics, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/maintenance/disposal/incinerator) "jdl" = ( /obj/structure/flora/bush/flowers_yw/style_random, /turf/open/floor/grass, @@ -28949,6 +29145,36 @@ "jds" = ( /turf/closed/wall, /area/station/service/kitchen) +"jdu" = ( +/obj/effect/landmark/start/scientist, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple/opposingcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/xenobiology) +"jdB" = ( +/obj/structure/table, +/obj/item/tank/jetpack/carbondioxide{ + pixel_x = -4; + pixel_y = 6 + }, +/obj/item/tank/jetpack/carbondioxide{ + pixel_y = 2 + }, +/obj/item/wrench, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/cold/dim/directional/south, +/turf/open/floor/iron/dark, +/area/station/command/eva) "jdE" = ( /obj/machinery/camera/directional/north{ c_tag = "Fore Hallway Diner"; @@ -28986,23 +29212,6 @@ /obj/structure/noticeboard/directional/south, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"jdY" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/item/kirbyplants/organic/plant16, -/obj/effect/turf_decal/box/corners{ - dir = 1 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/hallway/primary/central/fore) "jec" = ( /obj/effect/landmark/event_spawn, /obj/structure/cable, @@ -29140,15 +29349,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/port) -"jgt" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 1 - }, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/box, -/turf/open/floor/iron, -/area/station/engineering/main) "jgu" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 8 @@ -29234,11 +29434,6 @@ /obj/effect/landmark/atmospheric_sanity/mark_all_station_areas_as_goal, /turf/open/space/basic, /area/space) -"jhr" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/lobby) "jhv" = ( /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 1 @@ -29278,6 +29473,16 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/office) +"jhE" = ( +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/atmospherics/components/tank/air/layer4{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/entry) "jhH" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -29308,6 +29513,10 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/prison) +"jhY" = ( +/obj/structure/spirit_board, +/turf/open/floor/cult, +/area/station/service/chapel/office) "jia" = ( /obj/machinery/atmospherics/components/unary/thermomachine/heater{ dir = 1 @@ -29315,6 +29524,20 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"jig" = ( +/obj/machinery/requests_console/directional/east{ + department = "Garden"; + name = "Garden Requests Console" + }, +/obj/effect/turf_decal/siding/green{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "jij" = ( /obj/machinery/computer/records/security{ dir = 4 @@ -29388,13 +29611,6 @@ dir = 1 }, /area/station/hallway/primary/starboard) -"jiT" = ( -/obj/structure/filingcabinet/chestdrawer, -/obj/machinery/light/directional/north, -/obj/effect/decal/cleanable/cobweb/cobweb2, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/security/courtroom) "jiU" = ( /obj/machinery/door/airlock/medical{ id_tag = "Shower_2"; @@ -29417,26 +29633,16 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/department/electrical) -"jjm" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "jjs" = ( /turf/closed/wall/r_wall/rust, /area/station/maintenance/starboard/aft) -"jju" = ( -/obj/machinery/light/directional/north, -/obj/structure/chair/sofa/bench/left{ - dir = 4 - }, -/obj/effect/landmark/start/assistant, -/obj/effect/turf_decal/siding/wood{ +"jjF" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 }, -/turf/open/floor/wood, -/area/station/service/chapel/monastery) +/obj/structure/sink/directional/north, +/turf/open/floor/iron, +/area/station/security/prison/garden) "jjP" = ( /obj/structure/table, /obj/effect/decal/cleanable/cobweb/cobweb2, @@ -29529,21 +29735,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/security/brig) -"jmE" = ( -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen, -/obj/machinery/light/small/directional/south, -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/status_display/evac/directional/south, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/starboard) "jmK" = ( /obj/structure/railing/corner{ dir = 4 @@ -29586,6 +29777,20 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"jnl" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 9 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/fore) "jnp" = ( /turf/closed/wall/rust, /area/station/cargo/drone_bay) @@ -29599,6 +29804,17 @@ "joh" = ( /turf/closed/wall/rust, /area/station/command/heads_quarters/hos) +"jon" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 4 + }, +/obj/item/reagent_containers/cup/watering_can, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/security/prison/garden) "jop" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -29655,16 +29871,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"joS" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/east, -/obj/structure/reagent_dispensers/water_cooler, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) "joY" = ( /turf/closed/wall/rust, /area/station/maintenance/disposal) @@ -29717,14 +29923,6 @@ "jqb" = ( /turf/closed/wall, /area/station/security/detectives_office) -"jqt" = ( -/obj/machinery/computer/bank_machine, -/obj/structure/sign/warning/secure_area/directional/north, -/obj/machinery/light/small/directional/north, -/turf/open/floor/circuit/green{ - luminosity = 2 - }, -/area/station/command/vault) "jqI" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/burnt_floor, @@ -29887,6 +30085,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"jsR" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/starboard) "jsV" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters{ @@ -29995,16 +30201,6 @@ /obj/structure/reagent_dispensers/watertank, /turf/open/floor/iron/dark, /area/station/engineering/storage_shared) -"jvl" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/processing) "jvQ" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/burnt_floor, @@ -30115,16 +30311,6 @@ /obj/machinery/duct, /turf/open/floor/plastic, /area/station/hallway/secondary/service) -"jyS" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/corner, -/turf/open/floor/iron/dark, -/area/station/maintenance/disposal/incinerator) "jzf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -30178,6 +30364,23 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"jzu" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/machinery/status_display/evac/directional/north, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "jzw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -30190,6 +30393,15 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) +"jzx" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/airalarm/directional/north, +/turf/open/floor/iron/dark/corner{ + dir = 4 + }, +/area/station/hallway/primary/fore) "jzB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -30348,6 +30560,19 @@ "jCm" = ( /turf/closed/wall, /area/station/maintenance/department/crew_quarters/bar) +"jCF" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 4 + }, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "jCP" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ @@ -30378,6 +30603,17 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/commons/locker) +"jDv" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/conveyor{ + dir = 4; + id = "packagereturn"; + name = "crate return belt" + }, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/cargo/sorting) "jDy" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -30399,17 +30635,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) -"jDU" = ( -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ - dir = 4 - }, -/obj/structure/table/glass, -/obj/structure/cable, -/obj/structure/fireaxecabinet/jawsofrecovery/directional/east, -/obj/item/surgicaldrill, -/obj/item/toy/figure/paramedic, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/paramedic) "jEd" = ( /obj/structure/table, /obj/machinery/firealarm/directional/west, @@ -30469,6 +30694,21 @@ }, /turf/open/floor/engine/telecomms, /area/station/tcommsat/server) +"jEG" = ( +/obj/structure/table/wood/fancy/black, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/geranium, +/obj/machinery/firealarm/directional/east, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "jEK" = ( /obj/structure/sign/warning/docking, /turf/closed/wall, @@ -30496,6 +30736,17 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"jEW" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "jFq" = ( /obj/structure/barricade/wooden/crude, /obj/machinery/door/airlock/bronze, @@ -30552,15 +30803,6 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"jGg" = ( -/obj/effect/decal/cleanable/cobweb, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/firealarm/directional/west, -/obj/item/radio/intercom/directional/north, -/obj/machinery/pdapainter/supply, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/qm) "jGt" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -30620,6 +30862,18 @@ /obj/machinery/space_heater, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"jHP" = ( +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "jHR" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/stripes/line{ @@ -30650,19 +30904,33 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) -"jIa" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/start/chaplain, -/turf/open/floor/carpet/red, +"jHW" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/machinery/camera/directional/north{ + c_tag = "Morgue Entrance"; + name = "medical camera"; + network = list("ss13","medical") + }, +/obj/machinery/firealarm/directional/north, +/obj/effect/turf_decal/siding/white{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/fore) +"jIi" = ( +/obj/structure/railing/corner{ + dir = 8 + }, +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark, /area/station/service/chapel) -"jIg" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/meter, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/cryo) "jIt" = ( /obj/machinery/door/poddoor/shutters/radiation/preopen{ id = "engsm"; @@ -30807,26 +31075,6 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"jJB" = ( -/obj/structure/cable, -/obj/effect/landmark/start/hangover, -/obj/structure/disposalpipe/sorting/mail/flip{ - dir = 8; - name = "disposals sorting disposal pipe" - }, -/obj/effect/mapping_helpers/mail_sorting/supply/disposals, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) -"jJD" = ( -/obj/machinery/power/shieldwallgen/xenobiologyaccess, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/box, -/obj/structure/extinguisher_cabinet/directional/west, -/obj/structure/cable, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/science/xenobiology) "jJH" = ( /obj/structure/plasticflaps/opaque, /obj/machinery/navbeacon{ @@ -30880,17 +31128,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science) -"jKt" = ( -/obj/effect/decal/cleanable/dirt, -/obj/item/kirbyplants/organic/plant21{ - desc = "After his promotion, he was transferred to Kilo Station to serve as the gateway's protector."; - name = "rodger" - }, -/obj/machinery/light/small/directional/south, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/command/gateway) "jKv" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/blue{ @@ -30947,6 +31184,17 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/medical/morgue) +"jKU" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "jKZ" = ( /obj/machinery/vending/sustenance, /turf/open/floor/iron/white, @@ -30965,6 +31213,16 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) +"jLs" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/shower/directional/west, +/obj/effect/turf_decal/box, +/obj/structure/fluff/shower_drain, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/main) "jLy" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -31021,18 +31279,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/surgery/theatre) -"jMl" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "jMo" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -31069,22 +31315,21 @@ }, /turf/open/floor/iron/dark, /area/station/science/robotics/mechbay) -"jNe" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/landmark/event_spawn, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/command/bridge) "jNn" = ( /obj/structure/sign/warning/vacuum/external, /turf/closed/wall/rust, /area/station/maintenance/starboard) +"jNq" = ( +/obj/machinery/door/airlock/engineering{ + name = "Emergency Storage" + }, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/entry) "jNt" = ( /obj/machinery/navbeacon{ codes_txt = "patrol;next_patrol=Arrivals"; @@ -31196,19 +31441,17 @@ }, /turf/open/floor/wood, /area/station/commons/locker) +"jPA" = ( +/obj/machinery/conveyor/inverted{ + dir = 5; + id = "mining" + }, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/dark, +/area/station/cargo/miningoffice) "jPB" = ( /turf/closed/wall/rust, /area/station/security/courtroom) -"jPG" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/disposal/bin/tagger, -/obj/machinery/light/directional/east, -/obj/item/radio/intercom/directional/east, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/hos) "jQh" = ( /obj/structure/lattice, /obj/structure/disposalpipe/segment{ @@ -31313,23 +31556,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) -"jRF" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 5 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"jRQ" = ( -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/ore_silo, -/obj/machinery/airalarm/directional/north, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, +"jRG" = ( +/obj/structure/table/reinforced/plastitaniumglass, +/obj/item/mmi, /turf/open/floor/iron/dark, -/area/station/command/vault) +/area/station/service/chapel/storage) "jRR" = ( /obj/structure/cable, /obj/machinery/door/airlock/maintenance{ @@ -31459,12 +31690,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"jTt" = ( -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "jUa" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, @@ -31546,6 +31771,11 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/maintenance/department/crew_quarters/bar) +"jVW" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/aft) "jVZ" = ( /obj/structure/disposalpipe/segment, /obj/effect/landmark/event_spawn, @@ -31597,6 +31827,12 @@ /obj/machinery/suit_storage_unit/engine, /turf/open/floor/iron/dark, /area/station/engineering/storage_shared) +"jWN" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "jXd" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -31625,6 +31861,11 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/hallway/primary/aft) +"jXU" = ( +/obj/effect/landmark/start/paramedic, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "jXX" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -31634,13 +31875,6 @@ }, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"jYb" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "jYc" = ( /obj/machinery/meter, /obj/effect/turf_decal/stripes/line{ @@ -31776,22 +32010,6 @@ /obj/effect/landmark/navigate_destination/chemfactory, /turf/open/floor/iron/dark, /area/station/medical/chemistry) -"kat" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 4 - }, -/obj/machinery/light/directional/east, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "kax" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -31810,15 +32028,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"kaM" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/shower/directional/west, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/box, -/turf/open/floor/iron/dark, -/area/station/engineering/main) "kaT" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 @@ -31847,24 +32056,18 @@ /obj/effect/mapping_helpers/airlock/access/all/supply/general, /turf/open/floor/iron/dark, /area/station/cargo/miningoffice) +"kba" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/security/processing) "kbc" = ( /obj/structure/sign/poster/random/directional/north, /obj/effect/spawner/random/structure/steam_vent, /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/department/bridge) -"kbf" = ( -/obj/machinery/light/directional/east, -/obj/machinery/status_display/ai/directional/east, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/cable, -/turf/open/floor/engine, -/area/station/ai/satellite/chamber) "kbl" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -31923,26 +32126,19 @@ }, /turf/open/floor/iron, /area/station/security/prison) -"kbH" = ( -/obj/machinery/computer/records/medical{ - dir = 8 +"kbR" = ( +/obj/structure/table/wood/fancy/black, +/obj/item/food/grown/flower/poppy/lily{ + pixel_x = -6; + pixel_y = 6 }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 +/obj/item/food/grown/flower/poppy/lily{ + pixel_x = 6; + pixel_y = 6 }, -/obj/machinery/light/directional/east, +/obj/item/food/grown/flower/poppy/lily, /turf/open/floor/iron/dark, -/area/station/command/heads_quarters/cmo) -"kbT" = ( -/obj/structure/closet/secure_closet/hydroponics, -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/newscaster/directional/south, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/green/fourcorners, -/turf/open/floor/iron/checker, -/area/station/service/hydroponics) +/area/station/service/chapel/monastery) "kbV" = ( /obj/structure/table, /obj/item/storage/box/evidence, @@ -31999,13 +32195,6 @@ /obj/machinery/duct, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"kcy" = ( -/obj/machinery/newscaster/directional/east, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) "kcE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -32103,11 +32292,6 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) -"keO" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) "kfh" = ( /obj/effect/turf_decal/box/corners{ dir = 1 @@ -32118,6 +32302,36 @@ }, /turf/open/floor/iron/dark, /area/station/command/gateway) +"kfx" = ( +/obj/structure/table, +/obj/machinery/light_switch/directional/north{ + pixel_x = -8 + }, +/obj/item/clothing/gloves/color/orange, +/obj/item/reagent_containers/spray/cleaner{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/flashlight{ + pixel_y = 4 + }, +/obj/item/flashlight{ + pixel_y = 4 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/button/door/directional/north{ + id = "custodialwagon"; + name = "Custodial Bay Toggle"; + pixel_x = 8; + req_access = list("janitor") + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/janitor) "kfT" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -32167,6 +32381,24 @@ /obj/effect/mapping_helpers/airlock/access/all/security/brig, /turf/open/floor/iron/dark, /area/station/security/processing) +"kgU" = ( +/obj/machinery/computer/cargo, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/airalarm/directional/east, +/obj/machinery/requests_console/directional/north{ + department = "Quartermaster's Desk"; + name = "Quartermaster's Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/mapping_helpers/requests_console/supplies, +/obj/structure/extinguisher_cabinet/directional/north{ + pixel_x = 32 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/qm) "khe" = ( /obj/structure/cable, /obj/effect/turf_decal/stripes/line{ @@ -32328,6 +32560,10 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar/atrium) +"kjG" = ( +/obj/effect/spawner/structure/window/reinforced/tinted, +/turf/open/floor/plating, +/area/station/commons/vacant_room/commissary) "kjK" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -32403,17 +32639,6 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/medical/cryo) -"kll" = ( -/obj/machinery/computer/mech_bay_power_console{ - dir = 4 - }, -/obj/machinery/light/directional/north, -/obj/item/radio/intercom/directional/north, -/obj/structure/cable, -/turf/open/floor/circuit/green{ - luminosity = 2 - }, -/area/station/science/robotics/mechbay) "klm" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -32430,18 +32655,23 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron, /area/station/science/robotics/mechbay) -"klB" = ( -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 8 +"klw" = ( +/obj/machinery/shower/directional/west, +/obj/effect/turf_decal/box, +/obj/structure/fluff/shower_drain, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/cryo) +"klA" = ( +/obj/effect/landmark/event_spawn, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 1 }, -/obj/effect/turf_decal/tile/purple{ - dir = 8 +/obj/effect/turf_decal/tile/yellow{ + dir = 1 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/sorting) "klK" = ( /obj/structure/railing{ dir = 4 @@ -32466,18 +32696,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden) -"klN" = ( -/obj/effect/turf_decal/stripes/white/line{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/obj/structure/mannequin/skeleton{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) "kmo" = ( /turf/open/floor/iron, /area/station/engineering/main) @@ -32546,17 +32764,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"knL" = ( -/obj/effect/turf_decal/tile/blue, -/obj/machinery/firealarm/directional/south, -/obj/machinery/stasis, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/box, -/obj/machinery/vitals_reader/directional/east, -/turf/open/floor/iron/dark, -/area/station/medical/treatment_center) "knR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -32611,15 +32818,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/hallway/primary/aft) -"kpq" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/closet/firecloset, -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/obj/machinery/light/small/dim/directional/north, -/turf/open/floor/plating, -/area/station/hallway/secondary/exit/departure_lounge) "kpH" = ( /obj/effect/turf_decal/tile/red/half/contrasted, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -32685,6 +32883,17 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar) +"kqL" = ( +/obj/machinery/status_display/evac/directional/west, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "kra" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -32704,27 +32913,6 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"krg" = ( -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/machinery/camera/directional/north{ - c_tag = "Morgue Entrance"; - name = "medical camera"; - network = list("ss13","medical") - }, -/obj/machinery/firealarm/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/siding/white{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/fore) "kri" = ( /obj/machinery/light/small/directional/west, /obj/effect/turf_decal/stripes/corner{ @@ -32740,16 +32928,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/showroomfloor, /area/station/science) -"krk" = ( -/obj/structure/closet/secure_closet/brig{ - name = "Prisoner Locker" - }, -/obj/effect/turf_decal/bot, -/obj/machinery/airalarm/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/security/execution/transfer) "krr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -32834,6 +33012,19 @@ }, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/port) +"ksu" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/sign/warning/vacuum/external/directional/east, +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/duct, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only, +/turf/open/floor/plating, +/area/station/science) "ksJ" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, @@ -32932,6 +33123,12 @@ /obj/effect/mapping_helpers/airlock/access/all/supply/vault, /turf/open/floor/iron/dark, /area/station/command/vault) +"kuE" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "kuK" = ( /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/structure/rack, @@ -32948,40 +33145,13 @@ "kvl" = ( /turf/closed/wall/r_wall/rust, /area/station/security/evidence) -"kvo" = ( -/obj/structure/table, -/obj/machinery/firealarm/directional/west, -/obj/item/ai_module/reset{ - pixel_x = 2; - pixel_y = 3 - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/item/ai_module/supplied/freeform, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"kvJ" = ( -/obj/structure/table, -/obj/item/storage/toolbox/mechanical{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/storage/toolbox/mechanical, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/machinery/light/directional/east, -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/sign/warning/fire/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, +"kvE" = ( +/obj/machinery/airalarm/directional/north, +/obj/machinery/computer/cargo, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, -/area/station/commons/storage/primary) +/area/station/cargo/sorting) "kvL" = ( /obj/effect/turf_decal/bot, /obj/machinery/computer/piratepad_control/civilian{ @@ -33027,6 +33197,15 @@ /obj/structure/closet/firecloset/full, /turf/open/floor/iron, /area/station/service/chapel/dock) +"kwt" = ( +/obj/effect/turf_decal/bot, +/obj/structure/rack, +/obj/item/storage/toolbox/emergency, +/obj/item/stack/rods/ten, +/obj/machinery/camera/autoname/directional/south, +/obj/machinery/light/dim/directional/south, +/turf/open/floor/iron/checker, +/area/station/security/mechbay) "kwx" = ( /obj/structure/sign/xenobio_guide, /turf/closed/wall/rust, @@ -33137,15 +33316,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron/dark, /area/station/hallway/primary/fore) -"kyj" = ( -/obj/structure/chair/sofa/left/brown{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/poster/contraband/the_big_gas_giant_truth/directional/west, -/turf/open/floor/iron/dark, -/area/station/cargo/miningoffice) "kyR" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -33153,17 +33323,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) -"kyT" = ( -/obj/machinery/vending/wardrobe/engi_wardrobe, -/obj/effect/turf_decal/bot, -/obj/machinery/camera/directional/north{ - c_tag = "Engineering Lockers"; - name = "engineering camera"; - network = list("ss13","engine") - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/engineering/storage_shared) "kyU" = ( /obj/machinery/light/small/directional/north, /obj/machinery/shieldgen, @@ -33197,12 +33356,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/cargo/miningoffice) -"kzz" = ( -/obj/machinery/light/directional/north, -/obj/structure/chair/stool/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/locker) "kzG" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/wood{ @@ -33249,12 +33402,38 @@ /obj/effect/mapping_helpers/airlock/access/any/science/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/starboard/fore) +"kzZ" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/shower/directional/east{ + name = "emergency shower" + }, +/obj/structure/extinguisher_cabinet/directional/west, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 8 + }, +/obj/structure/fluff/shower_drain, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/pharmacy) "kAb" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"kAg" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/engineering/main) "kAj" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -33267,6 +33446,26 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"kAr" = ( +/obj/structure/table/wood, +/obj/item/storage/fancy/candle_box, +/obj/item/storage/fancy/candle_box, +/obj/item/storage/fancy/candle_box{ + pixel_x = 6; + pixel_y = 5 + }, +/obj/item/storage/fancy/candle_box{ + pixel_x = -5; + pixel_y = 5 + }, +/obj/item/storage/box/matches{ + pixel_x = -3; + pixel_y = 2 + }, +/obj/machinery/computer/security/telescreen/monastery/directional/north, +/obj/machinery/light/warm/dim/directional/north, +/turf/open/floor/iron/grimy, +/area/station/service/chapel/office) "kAt" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -33349,14 +33548,6 @@ }, /turf/open/floor/engine, /area/station/science/auxlab) -"kBU" = ( -/obj/machinery/light_switch/directional/south, -/obj/effect/decal/cleanable/blood/old, -/obj/effect/turf_decal/stripes/line, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/small/directional/south, -/turf/open/floor/engine, -/area/station/ai/satellite/interior) "kCa" = ( /obj/structure/cable, /obj/structure/disposalpipe/sorting/mail{ @@ -33372,6 +33563,21 @@ /obj/effect/spawner/random/structure/crate, /turf/open/floor/plating, /area/station/maintenance/starboard) +"kCi" = ( +/obj/structure/cable, +/obj/effect/landmark/start/hangover, +/obj/structure/disposalpipe/sorting/mail/flip{ + dir = 8; + name = "disposals sorting disposal pipe" + }, +/obj/effect/mapping_helpers/mail_sorting/supply/disposals, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "kCk" = ( /turf/closed/wall/rust, /area/station/maintenance/disposal/incinerator) @@ -33389,6 +33595,12 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"kCR" = ( +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 8 + }, +/turf/open/floor/plating, +/area/station/maintenance/disposal/incinerator) "kCZ" = ( /obj/effect/turf_decal/tile/blue, /obj/effect/turf_decal/tile/blue/half/contrasted, @@ -33469,11 +33681,6 @@ /obj/structure/sign/warning/secure_area, /turf/closed/wall, /area/station/engineering/storage/tech) -"kEm" = ( -/obj/item/kirbyplants/random, -/obj/structure/sign/poster/contraband/self_ai_liberation/directional/west, -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) "kEp" = ( /obj/structure/flora/grass/jungle/a/style_random{ icon_state = "grassb2" @@ -33591,17 +33798,12 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"kGc" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/event_spawn, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/engineering/lobby) +"kFY" = ( +/obj/effect/turf_decal/bot, +/obj/structure/closet/crate/freezer/surplus_limbs, +/obj/item/radio/intercom/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/surgery/theatre) "kGj" = ( /obj/machinery/door/airlock/engineering{ name = "Port Bow Solar Access" @@ -33617,6 +33819,17 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/solars/port/fore) +"kGm" = ( +/obj/structure/bed, +/obj/machinery/iv_drip, +/obj/effect/turf_decal/bot_white/right, +/obj/effect/turf_decal/stripes/corner, +/obj/item/bedsheet/medical, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/medbay/central) "kGs" = ( /obj/machinery/door/morgue{ name = "Religious Equipment"; @@ -33665,21 +33878,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/aft) -"kHs" = ( -/obj/machinery/modular_computer/preset/cargochat/engineering{ - dir = 8 - }, -/obj/machinery/light/directional/east, -/obj/machinery/light_switch/directional/east, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/red{ - dir = 4 +"kHt" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 1 }, -/turf/open/floor/iron, -/area/station/engineering/main) +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "kHw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -33707,6 +33912,11 @@ /obj/machinery/meter, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"kHN" = ( +/obj/structure/closet/secure_closet/psychology, +/obj/item/radio/intercom/directional/north, +/turf/open/floor/carpet, +/area/station/medical/psychology) "kHQ" = ( /obj/structure/sign/poster/official/wtf_is_co2, /turf/closed/wall, @@ -33729,16 +33939,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal) -"kIf" = ( -/obj/structure/rack, -/obj/effect/spawner/random/techstorage/service_all, -/obj/effect/turf_decal/bot, -/obj/machinery/status_display/ai/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/effect/spawner/random/techstorage/arcade_boards, -/obj/effect/spawner/random/techstorage/arcade_boards, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "kIz" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/delivery, @@ -33816,15 +34016,6 @@ /obj/machinery/holopad, /turf/open/floor/iron/showroomfloor, /area/station/medical/surgery/theatre) -"kKa" = ( -/obj/structure/table/wood/fancy/blue, -/obj/effect/turf_decal/siding/wood{ - dir = 8 - }, -/obj/item/reagent_containers/cup/glass/trophy/bronze_cup, -/obj/machinery/camera/autoname/directional/west, -/turf/open/floor/carpet/royalblue, -/area/station/service/chapel/office) "kKm" = ( /obj/machinery/door/airlock/public/glass{ name = "Library" @@ -33853,11 +34044,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) -"kLm" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/random/structure/steam_vent, -/turf/open/floor/plating, -/area/station/maintenance/starboard) "kLo" = ( /obj/effect/turf_decal/tile/yellow/half/contrasted{ dir = 8 @@ -33900,14 +34086,6 @@ "kMe" = ( /turf/closed/wall/r_wall/rust, /area/station/maintenance/solars/port/aft) -"kMi" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/poster/contraband/random/directional/east, -/obj/structure/cable, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/machinery/camera/autoname/directional/east, -/turf/open/floor/wood, -/area/station/commons/vacant_room/commissary) "kMs" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/sign/poster/random/directional/east, @@ -33984,6 +34162,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard) +"kMR" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/sign/warning/electric_shock/directional/south, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "kNe" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -34202,13 +34388,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/engineering/atmos) -"kPE" = ( -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "kPI" = ( /obj/structure/closet/secure_closet/miner, /obj/effect/turf_decal/delivery, @@ -34362,6 +34541,15 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar/atrium) +"kSe" = ( +/obj/machinery/light/small/directional/south, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/structure/sign/warning/vacuum/external/directional/south, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "kSn" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -34408,6 +34596,20 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/storage) +"kSJ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/duct, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "kSR" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -34459,22 +34661,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/command/eva) -"kTD" = ( -/obj/machinery/light/directional/south, -/obj/machinery/status_display/ai/directional/south, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/structure/frame/computer{ - anchored = 1; - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/drone_bay) "kTJ" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -34619,6 +34805,12 @@ /obj/item/exodrone, /turf/open/floor/plating, /area/station/cargo/drone_bay) +"kVJ" = ( +/obj/effect/turf_decal/siding/thinplating/light/corner{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "kVN" = ( /obj/structure/lattice, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, @@ -34647,24 +34839,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) -"kWm" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/directional/north, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/item/kirbyplants/organic/plant21, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "kWz" = ( /obj/machinery/door/airlock/maintenance{ name = "Disposal Access" @@ -34693,6 +34867,14 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/maintenance/fore) +"kWN" = ( +/obj/machinery/airalarm/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/drone_bay) "kWW" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/door/airlock/security/glass{ @@ -34704,14 +34886,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/security/mechbay) -"kXp" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/north, -/obj/machinery/newscaster/directional/north, -/obj/machinery/computer/security/qm, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/qm) "kXw" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/cable, @@ -34719,6 +34893,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet/royalblue, /area/station/service/chapel/office) +"kXA" = ( +/obj/item/kirbyplants, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "kXJ" = ( /obj/machinery/door/poddoor/preopen{ id = "xeno1"; @@ -34999,6 +35182,15 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/service/lawoffice) +"lax" = ( +/obj/structure/closet/secure_closet/brig{ + name = "Prisoner Locker" + }, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/security/execution/transfer) "laB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -35015,41 +35207,16 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/plating, /area/station/maintenance/department/bridge) -"laH" = ( -/obj/structure/table/wood/fancy, -/obj/item/storage/fancy/candle_box, -/obj/machinery/light/directional/north, -/turf/open/floor/iron/dark, -/area/station/service/chapel) -"lbj" = ( -/obj/structure/cable, -/obj/structure/table, -/obj/machinery/reagentgrinder{ - pixel_y = 5 - }, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) -"lbq" = ( -/obj/item/radio/intercom/directional/south, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/obj/item/flatpack/flatpacker, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/effect/turf_decal/tile/red, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "lby" = ( /turf/closed/wall, /area/station/science/robotics/mechbay) +"lbz" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/green/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "lbD" = ( /obj/structure/closet/secure_closet/security/sec, /obj/effect/turf_decal/bot, @@ -35059,6 +35226,15 @@ }, /turf/open/floor/iron/dark, /area/station/security/lockers) +"lbE" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue/opposingcorners{ + dir = 1 + }, +/obj/machinery/light/cold/dim/directional/east, +/obj/machinery/newscaster/directional/east, +/turf/open/floor/iron/cafeteria, +/area/station/security/prison/garden) "lbJ" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -35163,16 +35339,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/maintenance/starboard/fore) -"ldc" = ( -/obj/machinery/atmospherics/components/unary/thermomachine/freezer{ - dir = 8 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) "lds" = ( /obj/effect/decal/cleanable/blood/old, /obj/item/stack/rods, @@ -35186,6 +35352,13 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/storage) +"lec" = ( +/obj/machinery/door/poddoor/preopen{ + id = "brigfrontdoor"; + name = "Front Security Blast Door" + }, +/turf/open/floor/iron/dark, +/area/station/security/brig) "lep" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/machinery/computer/operating{ @@ -35243,6 +35416,16 @@ "leH" = ( /turf/closed/wall/r_wall/rust, /area/station/maintenance/solars/port/fore) +"leR" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/machinery/newscaster/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/security/lockers) "lfd" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/tile/purple{ @@ -35301,15 +35484,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/medical) -"lfU" = ( -/obj/machinery/teleport/station, -/obj/machinery/status_display/evac/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/teleporter) "lgb" = ( /obj/effect/turf_decal/bot, /obj/structure/rack, @@ -35394,13 +35568,6 @@ /obj/structure/cable, /turf/open/floor/plastic, /area/station/security/prison/shower) -"lhx" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "lhB" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -35433,10 +35600,6 @@ /obj/structure/cable, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/science) -"lia" = ( -/obj/effect/spawner/structure/window/reinforced, -/turf/open/floor/plating, -/area/station/medical/paramedic) "lie" = ( /obj/effect/turf_decal/bot, /obj/machinery/vending/wardrobe/science_wardrobe, @@ -35488,6 +35651,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/aft) +"lja" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "ljb" = ( /obj/effect/turf_decal/stripes/corner, /obj/effect/decal/cleanable/dirt, @@ -35533,18 +35707,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/science/research) -"lkp" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/turf_decal/siding/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "lkB" = ( /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) @@ -35581,6 +35743,19 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/fore) +"lln" = ( +/obj/machinery/modular_computer/preset/command{ + dir = 4 + }, +/obj/machinery/requests_console/directional/west{ + department = "Head of Personnel's Desk"; + name = "Head of Personnel's Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/mapping_helpers/requests_console/information, +/obj/effect/mapping_helpers/requests_console/assistance, +/turf/open/floor/carpet/blue, +/area/station/command/heads_quarters/hop) "llr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -35659,14 +35834,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) -"lmr" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/door/airlock{ - name = "Prison Kitchen" - }, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "lmA" = ( /obj/effect/turf_decal/bot, /obj/machinery/disposal/bin/tagger, @@ -35692,6 +35859,19 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/security/brig) +"lmN" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 8 + }, +/obj/machinery/power/apc/auto_name/directional/south, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/storage) "lmO" = ( /turf/closed/wall/rust, /area/station/cargo/storage) @@ -35736,15 +35916,6 @@ /obj/machinery/status_display/evac/directional/south, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) -"lnU" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/processing) "lox" = ( /obj/effect/turf_decal/trimline/green/line{ dir = 5 @@ -35752,6 +35923,16 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/service/hydroponics) +"loE" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "loI" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -35759,6 +35940,18 @@ /obj/effect/decal/cleanable/cobweb, /turf/open/floor/plating, /area/station/maintenance/fore) +"loJ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/commons/fitness/recreation) "loO" = ( /obj/machinery/airlock_sensor/incinerator_ordmix{ pixel_x = -24 @@ -35771,14 +35964,6 @@ }, /turf/open/floor/engine, /area/station/science/ordnance/burnchamber) -"lpf" = ( -/obj/machinery/airalarm/directional/north, -/obj/machinery/light/directional/north, -/obj/machinery/computer/cargo, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/cargo/sorting) "lpp" = ( /obj/machinery/door/airlock/maintenance, /obj/effect/mapping_helpers/airlock/abandoned, @@ -35867,12 +36052,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/fore) -"lqM" = ( -/obj/structure/table, -/obj/machinery/firealarm/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/commons/locker) "lqN" = ( /obj/structure/table/wood, /obj/item/storage/box/evidence{ @@ -35888,6 +36067,25 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"lqO" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/door/airlock/atmos{ + name = "Incinerator" + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ + dir = 6 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/atmos, +/obj/machinery/duct, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/engineering/atmos) "lqP" = ( /obj/machinery/door/airlock/grunge{ id_tag = "Cabin_1"; @@ -35897,10 +36095,6 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron/dark, /area/station/commons/locker) -"lqR" = ( -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron/dark, -/area/station/service/chapel/storage) "lqU" = ( /turf/closed/wall, /area/station/engineering/atmos/pumproom) @@ -35912,16 +36106,6 @@ /obj/structure/table, /turf/open/floor/iron/dark, /area/station/medical/morgue) -"lre" = ( -/obj/machinery/status_display/evac/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/west, -/obj/item/kirbyplants/random, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/courtroom) "lrk" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/wood{ @@ -35955,6 +36139,14 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/port) +"lrM" = ( +/obj/machinery/power/shieldwallgen/xenobiologyaccess, +/obj/effect/turf_decal/box, +/obj/structure/extinguisher_cabinet/directional/west, +/obj/structure/cable, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/science/xenobiology) "lrO" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -36005,6 +36197,19 @@ }, /turf/closed/wall, /area/station/engineering/atmos) +"lsk" = ( +/obj/effect/turf_decal/tile/green/anticorner/contrasted{ + dir = 4 + }, +/obj/structure/closet/l3closet/virology, +/obj/item/tank/internals/emergency_oxygen, +/obj/item/tank/internals/emergency_oxygen, +/obj/effect/turf_decal/stripes/line{ + dir = 5 + }, +/obj/structure/sink/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "lst" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -36100,31 +36305,6 @@ }, /turf/open/floor/engine, /area/station/maintenance/disposal/incinerator) -"ltQ" = ( -/obj/structure/table, -/obj/item/clipboard, -/obj/item/disk/computer/ordnance, -/obj/item/disk/computer/ordnance, -/obj/item/disk/computer/ordnance, -/obj/item/circuitboard/aicore{ - pixel_y = 5 - }, -/obj/item/hand_labeler, -/obj/machinery/airalarm/directional/west, -/obj/machinery/light/directional/west, -/obj/item/pai_card{ - pixel_x = 6 - }, -/obj/item/aicard, -/obj/item/taperecorder{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/rd) "lue" = ( /obj/structure/sign/warning/fire/directional/east, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -36145,14 +36325,6 @@ /obj/effect/decal/cleanable/blood/old, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"luk" = ( -/obj/machinery/light/small/directional/south, -/obj/effect/decal/cleanable/blood/oil, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/service/chapel/storage) "lun" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -36160,12 +36332,6 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/engine/telecomms, /area/station/tcommsat/server) -"lux" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "luA" = ( /obj/effect/turf_decal/plaque{ icon_state = "L5" @@ -36229,6 +36395,21 @@ }, /turf/open/floor/iron/dark, /area/station/science/robotics/mechbay) +"lvg" = ( +/obj/structure/sign/departments/cargo/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/fore) +"lvw" = ( +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 1 + }, +/turf/open/floor/plating, +/area/station/maintenance/disposal/incinerator) "lvy" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -36250,6 +36431,14 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/showroomfloor, /area/station/security/office) +"lvR" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/science/xenobiology) "lvS" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -36261,19 +36450,27 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/aft) -"lvV" = ( -/obj/machinery/computer/operating{ - dir = 1; - name = "Robotics Operating Computer" - }, +"lvZ" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/fore) +"lwj" = ( /obj/effect/turf_decal/bot, -/obj/machinery/light/directional/south, -/obj/item/radio/intercom/directional/south, +/obj/machinery/computer/records/security{ + dir = 4 + }, +/obj/machinery/newscaster/directional/west, +/obj/machinery/camera/directional/west{ + c_tag = "Security Office Computers" + }, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 + dir = 4 }, /turf/open/floor/iron/dark, -/area/station/science/robotics/lab) +/area/station/security/office) "lwn" = ( /obj/structure/closet/secure_closet/evidence, /obj/effect/turf_decal/bot, @@ -36288,6 +36485,28 @@ "lwu" = ( /turf/closed/wall, /area/station/security/medical) +"lwz" = ( +/obj/structure/closet/secure_closet/chemical, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/structure/extinguisher_cabinet/directional/west, +/obj/item/storage/box/pillbottles, +/obj/item/storage/box/pillbottles, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/medical/pharmacy) +"lwM" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/red/anticorner/contrasted, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/firealarm/directional/east, +/turf/open/floor/iron, +/area/station/security/processing) "lwT" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, @@ -36383,15 +36602,6 @@ }, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"lxL" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "lxP" = ( /turf/closed/wall, /area/station/service/bar/backroom) @@ -36438,19 +36648,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/security/prison) -"lyr" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/structure/closet/secure_closet/medical2, -/turf/open/floor/iron/dark, -/area/station/medical/virology) "lyT" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/blue/half/contrasted{ @@ -36461,6 +36658,14 @@ }, /turf/open/floor/iron, /area/station/command/eva) +"lza" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "lzg" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -36469,19 +36674,20 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/showroomfloor, /area/station/security/office) -"lzs" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/processing) -"lzv" = ( -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) +"lzA" = ( +/obj/structure/cable, +/obj/structure/table/wood, +/obj/machinery/computer/libraryconsole/bookmanagement{ + pixel_y = 5 + }, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/machinery/power/apc/auto_name/directional/south, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/station/service/library) "lzF" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -36584,18 +36790,16 @@ /obj/effect/turf_decal/tile/purple, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"lBB" = ( -/obj/structure/bed, -/obj/machinery/iv_drip, -/obj/effect/turf_decal/bot_white/right, -/obj/effect/turf_decal/stripes/corner, -/obj/item/bedsheet/medical, -/obj/machinery/light/directional/east, +"lBu" = ( +/obj/machinery/newscaster/directional/south, +/obj/effect/turf_decal/siding/white{ + dir = 4 + }, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 + dir = 4 }, /turf/open/floor/iron/dark, -/area/station/medical/medbay/central) +/area/station/hallway/primary/fore) "lBN" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -36663,6 +36867,13 @@ /obj/structure/flora/bush/flowers_pp/style_random, /turf/open/floor/grass, /area/station/service/chapel) +"lCU" = ( +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/iron, +/area/station/security/brig) "lCV" = ( /obj/machinery/door/airlock/mining{ name = "Auxiliary Base" @@ -36680,24 +36891,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"lDb" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/power/terminal{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/light/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/supermatter/room) "lDc" = ( /obj/structure/lattice/catwalk, /obj/effect/landmark/carpspawn, @@ -36773,6 +36966,16 @@ /obj/structure/sign/warning/docking, /turf/closed/wall, /area/station/maintenance/starboard) +"lEz" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/structure/cable, +/obj/machinery/door/poddoor/shutters{ + name = "Virology Isolation Shutters"; + id = "virocells"; + dir = 1 + }, +/turf/open/floor/plating, +/area/station/medical/surgery/theatre) "lED" = ( /obj/machinery/telecomms/message_server/preset, /turf/open/floor/circuit/green/telecomms/mainframe, @@ -36854,15 +37057,6 @@ "lGs" = ( /turf/closed/wall, /area/station/security/warden) -"lGF" = ( -/obj/machinery/light/directional/east, -/obj/machinery/firealarm/directional/east, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/command/bridge) "lGO" = ( /obj/structure/lattice/catwalk, /obj/structure/railing{ @@ -36876,18 +37070,14 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) -"lGS" = ( -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) +"lGT" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/structure/grille/broken, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "lGV" = ( /obj/effect/spawner/structure/window/reinforced/tinted, /turf/open/floor/plating, @@ -36899,21 +37089,16 @@ /obj/effect/spawner/random/maintenance/two, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"lHk" = ( -/obj/structure/railing{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) -"lHy" = ( -/obj/machinery/light/small/directional/south, +"lHt" = ( +/obj/effect/turf_decal/stripes/line, +/obj/item/radio/intercom/directional/west, /obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark/corner{ dir = 1 }, -/obj/structure/window/spawner/directional/east, -/obj/structure/table/optable, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) +/area/station/hallway/primary/starboard) "lHE" = ( /obj/structure/table/wood, /obj/structure/displaycase/forsale/kitchen{ @@ -36939,6 +37124,21 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) +"lIf" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/button/door/directional/south{ + id = "custodialwagon"; + name = "Custodial Bay Toggle"; + req_access = list("janitor") + }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/mob/living/basic/bot/cleanbot/autopatrol, +/obj/effect/turf_decal/tile/purple/half/contrasted, +/obj/effect/turf_decal/tile/blue, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "lIr" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -36953,6 +37153,11 @@ "lIs" = ( /turf/closed/wall/rust, /area/station/security/execution/education) +"lIA" = ( +/obj/structure/sign/poster/contraband/self_ai_liberation/directional/west, +/obj/item/kirbyplants/photosynthetic, +/turf/open/floor/circuit, +/area/station/ai/upload/chamber) "lIR" = ( /obj/machinery/door/poddoor/shutters{ dir = 1; @@ -37031,6 +37236,18 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/medical) +"lJM" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/structure/table, +/obj/item/storage/toolbox/electrical{ + pixel_y = 3 + }, +/obj/item/multitool{ + pixel_x = -4; + pixel_y = 2 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "lJR" = ( /obj/machinery/button/crematorium/indestructible{ pixel_x = -25 @@ -37124,16 +37341,6 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/chamber) -"lLl" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"lLp" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "lLv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -37209,6 +37416,14 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) +"lNd" = ( +/obj/effect/turf_decal/stripes/line, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "lNe" = ( /obj/docking_port/stationary/escape_pod, /turf/open/floor/plating, @@ -37295,6 +37510,13 @@ "lOG" = ( /turf/closed/wall/rust, /area/station/service/chapel/monastery) +"lON" = ( +/obj/effect/spawner/random/vending/snackvend, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/security/courtroom) "lOX" = ( /turf/closed/wall/r_wall, /area/station/engineering/supermatter/engine) @@ -37351,18 +37573,6 @@ /obj/machinery/computer/security/telescreen/engine, /turf/open/floor/iron/dark, /area/station/security/checkpoint/engineering) -"lPE" = ( -/obj/machinery/firealarm/directional/east, -/obj/machinery/computer/dna_console{ - dir = 8 - }, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/science/genetics) "lPK" = ( /obj/machinery/door/airlock/external{ name = "Departure Shuttle Airlock"; @@ -37502,13 +37712,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) -"lRJ" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/security/processing) "lRS" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -37522,13 +37725,13 @@ /obj/machinery/duct, /turf/open/floor/plating, /area/station/science) -"lSg" = ( -/obj/machinery/conveyor/inverted{ - dir = 6; - id = "mining" - }, -/turf/open/floor/iron/dark, -/area/station/cargo/miningoffice) +"lSd" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "lSl" = ( /obj/machinery/door/airlock/public/glass{ name = "Auxiliary Dock" @@ -37592,13 +37795,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/command/eva) -"lTZ" = ( -/obj/structure/chair/comfy/brown{ - dir = 4 - }, -/obj/machinery/light/directional/north, -/turf/open/floor/carpet/royalblack, -/area/station/command/heads_quarters/captain) "lUq" = ( /turf/closed/wall/r_wall/rust, /area/station/service/chapel/storage) @@ -37606,6 +37802,23 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/security/courtroom) +"lUG" = ( +/obj/structure/table, +/obj/item/clothing/under/rank/prisoner/skirt{ + pixel_x = -13; + pixel_y = 5 + }, +/obj/item/clothing/under/rank/prisoner/skirt{ + pixel_x = 9; + pixel_y = 5 + }, +/obj/item/clothing/under/rank/prisoner{ + pixel_x = -2; + pixel_y = 5 + }, +/obj/effect/turf_decal/tile/red/half/contrasted, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "lUH" = ( /obj/structure/table/reinforced, /obj/structure/window/reinforced/spawner/directional/east, @@ -37691,6 +37904,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/service/bar/backroom) "lVD" = ( @@ -37790,6 +38004,17 @@ /obj/machinery/light/directional/south, /turf/open/floor/plating/airless, /area/station/hallway/secondary/entry) +"lWF" = ( +/obj/structure/chair/office, +/obj/effect/turf_decal/siding/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "lWJ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 @@ -37813,6 +38038,16 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/engineering/storage/tech) +"lWV" = ( +/obj/structure/table/wood, +/obj/machinery/computer/records/medical/laptop{ + dir = 4; + pixel_y = 3; + pixel_x = 3 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/psychology) "lXe" = ( /obj/structure/table, /obj/item/storage/box/lights/mixed{ @@ -37823,6 +38058,20 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/port) +"lXj" = ( +/obj/machinery/firealarm/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/commons/toilet/restrooms) "lXB" = ( /obj/structure/window/reinforced/spawner/directional/east, /obj/effect/decal/cleanable/dirt, @@ -37872,6 +38121,13 @@ }, /turf/open/floor/iron/dark, /area/ruin/space/has_grav/abandoned_warehouse) +"lXT" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "lYa" = ( /obj/structure/chair/comfy/brown, /obj/effect/landmark/start/detective, @@ -38011,19 +38267,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"maX" = ( -/obj/machinery/computer/order_console/cook{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/newscaster/directional/east, -/obj/machinery/camera/directional/east{ - c_tag = "Kitchen Coldroom"; - name = "kitchen camera" - }, -/obj/machinery/light/cold/directional/east, -/turf/open/floor/iron/kitchen_coldroom/freezerfloor, -/area/station/service/kitchen/coldroom) "mbc" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -38090,19 +38333,6 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating/airless, /area/space/nearstation) -"mbE" = ( -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "departures-entrance" - }, -/obj/machinery/door/airlock/public/glass{ - name = "Departure Lounge" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "mbP" = ( /obj/effect/turf_decal/bot, /obj/machinery/holopad, @@ -38158,6 +38388,13 @@ }, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) +"mcu" = ( +/obj/structure/flora/bush/flowers_br/style_random, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/grass, +/area/station/service/chapel) "mcN" = ( /obj/effect/turf_decal/bot, /turf/open/floor/iron, @@ -38174,16 +38411,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/general, /turf/open/floor/engine, /area/station/engineering/supermatter/engine) -"mdl" = ( -/obj/effect/turf_decal/box/corners{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "mdp" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -38204,6 +38431,11 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/maintenance/starboard) +"mdy" = ( +/obj/structure/flora/bush/flowers_yw/style_random, +/obj/machinery/light/dim/directional/east, +/turf/open/floor/grass, +/area/station/service/chapel) "mdB" = ( /obj/machinery/door/airlock/external{ name = "External Freight Airlock" @@ -38259,24 +38491,6 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"mez" = ( -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/effect/turf_decal/delivery, -/obj/structure/table, -/obj/item/storage/belt/utility, -/turf/open/floor/iron, -/area/station/engineering/lobby) -"meO" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/commons/fitness/recreation) "meP" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -38287,6 +38501,22 @@ /obj/effect/mapping_helpers/mail_sorting/science/ordnance, /turf/open/floor/iron/showroomfloor, /area/station/science/lobby) +"meR" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/obj/machinery/wall_healer/directional/east, +/obj/machinery/light/warm/directional/east, +/turf/open/floor/iron, +/area/station/engineering/lobby) "meU" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ @@ -38390,29 +38620,6 @@ }, /turf/open/floor/iron, /area/station/security/office) -"mfT" = ( -/obj/structure/chair/sofa/right/maroon{ - dir = 8 - }, -/obj/structure/railing{ - dir = 4 - }, -/obj/machinery/newscaster/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/bar/atrium) -"mfU" = ( -/obj/effect/turf_decal/stripes/end{ - dir = 4 - }, -/obj/machinery/light/small/directional/east, -/obj/item/radio/intercom/directional/east, -/obj/machinery/status_display/ai/directional/south, -/turf/open/floor/engine, -/area/station/tcommsat/computer) "mfY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -38471,6 +38678,19 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"mgU" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/firealarm/directional/west, +/obj/machinery/suit_storage_unit/standard_unit{ + desc = "An industrial suit storage device carrying retro space suits. Neat!"; + helmet_type = /obj/item/clothing/head/helmet/space; + suit_type = /obj/item/clothing/suit/space + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/command/eva) "mha" = ( /obj/machinery/porta_turret/ai, /obj/machinery/airalarm/directional/west, @@ -38555,14 +38775,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"miK" = ( -/obj/effect/decal/cleanable/blood/footprints{ - dir = 4; - pixel_x = 24; - pixel_y = 8 - }, -/turf/open/floor/plating/rust, -/area/station/security/prison/shower) "miM" = ( /obj/structure/chair, /obj/effect/landmark/start/hangover, @@ -38632,18 +38844,15 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"mjU" = ( -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "mjW" = ( /turf/closed/wall, /area/station/cargo/miningdock) +"mkj" = ( +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 4 + }, +/turf/open/floor/plating, +/area/station/maintenance/disposal/incinerator) "mkk" = ( /turf/closed/wall/rust, /area/station/hallway/secondary/entry) @@ -38685,21 +38894,6 @@ }, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"mlj" = ( -/obj/structure/table, -/obj/item/storage/box/deputy, -/obj/item/stack/package_wrap, -/obj/item/hand_labeler, -/obj/machinery/camera/directional/west{ - c_tag = "Warden's Office" - }, -/obj/structure/reagent_dispensers/wall/peppertank/directional/west, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/warden) "mlm" = ( /obj/machinery/door/poddoor/shutters/preopen{ dir = 8; @@ -38736,16 +38930,6 @@ }, /turf/open/floor/iron, /area/station/cargo/office) -"mlz" = ( -/obj/structure/table, -/obj/item/stack/medical/mesh, -/obj/item/stack/medical/suture, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/machinery/wall_healer/directional/north, -/turf/open/floor/iron/dark, -/area/station/medical/medbay/lobby) "mlB" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -38773,6 +38957,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/cargo/storage) +"mlK" = ( +/obj/effect/decal/cleanable/ash, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "mlT" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/structure/window/reinforced/spawner/directional/east, @@ -38952,13 +39141,6 @@ /obj/effect/decal/cleanable/blood/old, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"moc" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/airalarm/directional/east, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "mog" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39015,16 +39197,6 @@ /obj/effect/decal/cleanable/ash, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) -"mpd" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/landmark/start/warden, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/warden) "mpv" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -39178,6 +39350,12 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/engineering/storage/tcomms) +"msy" = ( +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/cable, +/obj/machinery/portable_atmospherics/canister/carbon_dioxide, +/turf/open/floor/iron/dark, +/area/station/science/ordnance/storage) "msL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39214,16 +39392,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/chapel, /area/station/service/chapel) -"msZ" = ( -/obj/structure/chair/office, -/obj/effect/turf_decal/siding/red{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "mtc" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -39453,6 +39621,18 @@ /obj/effect/turf_decal/bot, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"mwU" = ( +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted, +/obj/machinery/disposal/bin/tagger, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "mxf" = ( /turf/closed/wall/r_wall/rust, /area/station/security/execution/transfer) @@ -39496,6 +39676,20 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/security/brig) +"mxT" = ( +/obj/machinery/recharge_station, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/science/robotics/mechbay) "myc" = ( /obj/structure/rack, /obj/item/wirecutters{ @@ -39522,6 +39716,12 @@ "myp" = ( /turf/closed/wall/r_wall, /area/station/security/prison/garden) +"myt" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/wall_healer/directional/south, +/obj/machinery/light/directional/south, +/turf/open/floor/iron, +/area/station/hallway/primary/central/fore) "myS" = ( /obj/effect/turf_decal/box, /obj/structure/cable, @@ -39557,14 +39757,6 @@ }, /turf/open/floor/iron, /area/station/command/bridge) -"mzs" = ( -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/vending/wardrobe/hydro_wardrobe, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/green/fourcorners, -/turf/open/floor/iron/checker, -/area/station/service/hydroponics) "mzB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39573,10 +39765,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"mzI" = ( -/obj/structure/transit_tube/diagonal/crossing/topleft, -/turf/open/space/basic, -/area/space/nearstation) "mAa" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/stripes/line{ @@ -39597,14 +39785,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, /turf/open/floor/iron/dark, /area/station/service/chapel/storage) -"mAf" = ( -/obj/effect/spawner/random/vending/snackvend, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/courtroom) "mAj" = ( /obj/effect/turf_decal/bot, /obj/machinery/status_display/ai/directional/east, @@ -39618,43 +39798,16 @@ }, /turf/open/floor/iron/dark, /area/station/ai/satellite/maintenance/storage) -"mAH" = ( -/obj/structure/table, -/obj/item/clothing/gloves/latex, -/obj/item/clothing/gloves/latex, -/obj/item/storage/box/monkeycubes{ - pixel_x = 2; - pixel_y = 5 - }, -/obj/item/storage/box/monkeycubes{ - pixel_y = 3 - }, -/obj/effect/turf_decal/siding/purple/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/science/xenobiology) "mBm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/engineering/storage/tech) -"mBL" = ( -/obj/item/tank/internals/emergency_oxygen/engi{ - pixel_x = -5 - }, -/obj/item/tank/internals/emergency_oxygen/engi{ - pixel_x = 5 - }, -/obj/item/geiger_counter, -/obj/item/geiger_counter, -/obj/structure/table, -/obj/structure/sign/warning/xeno_mining/directional/west, -/turf/open/floor/iron/dark, -/area/station/engineering/supermatter/room) +"mBp" = ( +/obj/structure/lattice, +/obj/structure/transit_tube/horizontal, +/turf/open/space/basic, +/area/space/nearstation) "mBS" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 1 @@ -39729,27 +39882,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"mCu" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/machinery/light/directional/north, -/obj/machinery/status_display/ai/directional/north, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) -"mCE" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/north, -/obj/machinery/newscaster/directional/north, -/obj/machinery/computer/atmos_control/nocontrol/master, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos/storage/gas) "mCF" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -39758,6 +39890,16 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"mCG" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "mCO" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -39791,25 +39933,11 @@ "mDD" = ( /turf/closed/wall/rust, /area/ruin/space/has_grav/abandoned_warehouse) -"mDM" = ( -/obj/machinery/portable_atmospherics/pump, -/obj/machinery/light/directional/south, -/obj/machinery/airalarm/directional/south, -/obj/effect/turf_decal/box, -/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer2{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/engineering/hallway) -"mDQ" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/turf/open/floor/engine, -/area/station/science/auxlab) +"mDY" = ( +/obj/effect/turf_decal/tile/brown/half/contrasted, +/obj/effect/turf_decal/tile/yellow, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "mEg" = ( /turf/closed/wall/r_wall/rust, /area/station/security/prison/mess) @@ -39888,6 +40016,14 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/fore) +"mGi" = ( +/obj/effect/landmark/event_spawn, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "mGj" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -39905,33 +40041,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) -"mGz" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/ai_module/reset, -/obj/item/assembly/flash/handheld, -/obj/item/assembly/flash/handheld, -/obj/machinery/airalarm/directional/east, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) -"mGG" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/processing) -"mGS" = ( -/obj/machinery/light/directional/south, -/obj/machinery/firealarm/directional/south, -/obj/effect/turf_decal/tile/purple/opposingcorners, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/lab) "mHa" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39977,6 +40086,15 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/service/chapel) +"mHh" = ( +/obj/effect/turf_decal/bot, +/obj/structure/reagent_dispensers/water_cooler, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "mHm" = ( /obj/effect/landmark/start/depsec/medical, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -40013,19 +40131,21 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"mIe" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ - dir = 4 +"mIi" = ( +/obj/structure/table, +/obj/item/storage/box/bodybags{ + pixel_x = 4; + pixel_y = 6 }, -/obj/effect/turf_decal/stripes/line{ - dir = 5 +/obj/item/storage/box/bodybags{ + pixel_y = 2 }, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/cargo/storage) +/obj/machinery/light_switch/directional/east, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "mIo" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -40043,6 +40163,18 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/tcoms, /turf/open/floor/iron/dark, /area/station/tcommsat/server) +"mIC" = ( +/obj/machinery/computer/operating{ + dir = 1; + name = "Robotics Operating Computer" + }, +/obj/effect/turf_decal/bot, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/science/robotics/lab) "mIR" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -40118,6 +40250,18 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"mKe" = ( +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "mKv" = ( /obj/structure/chair/comfy, /obj/item/radio/intercom/command/directional/north, @@ -40238,6 +40382,10 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) +"mMu" = ( +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "mMv" = ( /obj/structure/lattice/catwalk, /obj/structure/cable, @@ -40265,13 +40413,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, /turf/closed/wall/r_wall, /area/station/engineering/supermatter/engine) -"mME" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/brown{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "mMJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/stripes/corner{ @@ -40358,15 +40499,6 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos) -"mOt" = ( -/obj/machinery/light/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/science/xenobiology) "mOz" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -40380,17 +40512,27 @@ }, /turf/open/floor/iron, /area/station/command/bridge) -"mPo" = ( -/obj/structure/table, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/glass/fifty, -/obj/machinery/airalarm/directional/east, +"mOC" = ( +/obj/machinery/camera/directional/east{ + c_tag = "Transferring Centre" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/airalarm/directional/east, +/turf/open/floor/iron, +/area/station/security/processing) +"mPB" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/north, +/obj/machinery/computer/security/qm, +/obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, -/area/station/ai/satellite/maintenance/storage) +/area/station/command/heads_quarters/qm) "mPC" = ( /obj/machinery/door/firedoor, /obj/effect/turf_decal/delivery, @@ -40440,10 +40582,6 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"mPZ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/cargo/sorting) "mQe" = ( /obj/effect/turf_decal/bot, /obj/machinery/holopad, @@ -40457,6 +40595,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/construction/mining/aux_base) +"mQs" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "mQu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -40566,28 +40711,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/storage/tcomms) -"mRm" = ( -/obj/structure/table, -/obj/item/toy/cards/deck{ - pixel_x = -9 - }, -/obj/item/storage/dice{ - pixel_x = -8; - pixel_y = 11 - }, -/obj/item/storage/photo_album/prison{ - pixel_x = 8; - pixel_y = -3 - }, -/turf/open/floor/iron/grimy, -/area/station/security/prison) -"mRo" = ( -/obj/structure/table, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/courtroom) "mRp" = ( /turf/closed/wall/rust, /area/station/security/evidence) @@ -40625,6 +40748,21 @@ }, /turf/open/floor/plating, /area/station/maintenance/aft) +"mSe" = ( +/obj/structure/table, +/obj/machinery/cell_charger, +/obj/item/stock_parts/power_store/cell/high, +/obj/item/screwdriver{ + pixel_y = 18 + }, +/obj/item/radio/intercom/directional/south, +/obj/machinery/firealarm/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "mSf" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible{ dir = 4 @@ -40674,20 +40812,17 @@ }, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"mSu" = ( -/obj/structure/table, -/obj/machinery/light/directional/west, -/obj/item/clipboard, -/obj/item/airlock_painter{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/airlock_painter, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 +"mSs" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 }, -/turf/open/floor/iron/dark, -/area/station/engineering/hallway) +/obj/machinery/duct, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "mSC" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -40785,6 +40920,19 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/iron, /area/station/security/prison) +"mUH" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/closet/cardboard, +/obj/structure/sign/warning/no_smoking{ + pixel_x = 30 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/space_heater, +/obj/machinery/light/small/directional/south, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "mUT" = ( /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, @@ -40793,6 +40941,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/cargo/bitrunning/den) +"mUV" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/yellow, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "mVf" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -40819,6 +40977,16 @@ }, /turf/open/floor/iron, /area/station/engineering/hallway) +"mVr" = ( +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron, +/area/station/engineering/main) "mVu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/landmark/start/shaft_miner, @@ -40828,37 +40996,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/cargo/miningdock) -"mVL" = ( -/obj/structure/rack, -/obj/effect/turf_decal/bot, -/obj/item/storage/box/bodybags{ - pixel_y = 5 - }, -/obj/item/storage/medkit/regular{ - empty = 1; - name = "First-Aid (empty)" - }, -/obj/item/storage/medkit/regular{ - empty = 1; - name = "First-Aid (empty)" - }, -/obj/item/storage/medkit/regular{ - empty = 1; - name = "First-Aid (empty)" - }, -/obj/item/stack/package_wrap, -/obj/item/healthanalyzer, -/obj/item/healthanalyzer, -/obj/item/healthanalyzer, -/obj/machinery/camera/directional/north{ - c_tag = "Robotics Lab"; - name = "science camera"; - network = list("ss13","rd") - }, -/obj/item/radio/intercom/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/science/robotics/lab) "mVP" = ( /obj/effect/turf_decal/arrows, /obj/effect/decal/cleanable/dirt, @@ -41057,11 +41194,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/fore) -"mZS" = ( -/obj/machinery/light/directional/east, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "mZT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, /turf/closed/wall/rust, @@ -41131,21 +41263,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) -"naG" = ( -/obj/structure/chair/sofa/left/maroon{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/structure/railing{ - dir = 8 - }, -/obj/effect/landmark/start/hangover, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/bar/atrium) "naK" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/red/half/contrasted{ @@ -41154,6 +41271,17 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"naO" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "naQ" = ( /obj/effect/turf_decal/stripes/corner, /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, @@ -41207,6 +41335,15 @@ /obj/item/radio/intercom/prison/directional/south, /turf/open/floor/iron/white, /area/station/security/prison/mess) +"nbG" = ( +/obj/machinery/vending/dinnerware, +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/kitchen) "nbH" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/decal/cleanable/cobweb/cobweb2, @@ -41257,6 +41394,16 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/monastery) +"nce" = ( +/obj/machinery/vending/wardrobe/sec_wardrobe, +/obj/effect/turf_decal/bot, +/obj/structure/reagent_dispensers/wall/peppertank/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/directional/west, +/turf/open/floor/iron/dark, +/area/station/security/lockers) "ncj" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -41266,6 +41413,10 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/lesser) +"nct" = ( +/obj/structure/sign/warning, +/turf/closed/wall/r_wall, +/area/station/security/prison/safe) "ncz" = ( /obj/structure/flora/bush/pale/style_random, /turf/open/misc/asteroid, @@ -41326,10 +41477,42 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/dark, /area/station/command/bridge) +"ndj" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 5 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) +"ndq" = ( +/obj/machinery/door/poddoor/shutters{ + id = "secmechbay"; + name = "Security Mech Bay Shutters"; + dir = 1 + }, +/turf/open/floor/plating, +/area/station/security/mechbay) "ndz" = ( /obj/machinery/smartfridge/extract/preloaded, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"ndC" = ( +/obj/effect/turf_decal/tile/blue/opposingcorners{ + dir = 1 + }, +/turf/open/floor/iron/cafeteria, +/area/station/security/prison/garden) "ndP" = ( /obj/machinery/atmospherics/pipe/bridge_pipe/green/visible, /obj/effect/decal/cleanable/dirt, @@ -41430,11 +41613,18 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) -"nfD" = ( -/obj/structure/spirit_board, -/obj/machinery/light/very_dim/directional/south, -/turf/open/floor/cult, -/area/station/service/chapel/office) +"nfF" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/requests_console/directional/east{ + department = "Pharmacy"; + name = "Pharmacy Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/ore_update, +/obj/effect/mapping_helpers/requests_console/supplies, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/obj/machinery/chem_dispenser, +/turf/open/floor/iron/dark, +/area/station/medical/pharmacy) "nfI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -41444,17 +41634,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"nfN" = ( -/obj/structure/rack, -/obj/effect/spawner/random/techstorage/rnd_secure_all, -/obj/machinery/light/small/directional/south, -/obj/machinery/status_display/shuttle{ - pixel_y = -32 - }, -/turf/open/floor/circuit/green{ - luminosity = 2 - }, -/area/station/engineering/storage/tech) "nfR" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/structure/flora/bush/lavendergrass/style_random, @@ -41505,23 +41684,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/iron, /area/station/security/prison) -"ngT" = ( -/obj/structure/bed, -/obj/item/bedsheet/medical, -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/machinery/camera/autoname/directional/west, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) -"ngV" = ( -/obj/machinery/conveyor/inverted{ - dir = 5; - id = "mining" - }, -/turf/open/floor/iron/dark, -/area/station/cargo/miningoffice) "ngW" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, @@ -41735,24 +41897,30 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/wood, /area/station/security/detectives_office) -"nlM" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "nlR" = ( /turf/closed/wall/rust, /area/station/medical/pharmacy) -"nmo" = ( -/obj/machinery/atmospherics/pipe/layer_manifold/supply/hidden{ +"nlT" = ( +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ dir = 4 }, -/turf/closed/wall/r_wall, -/area/station/maintenance/disposal/incinerator) +/obj/item/flatpack/flatpacker, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/effect/turf_decal/tile/red, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) +"nme" = ( +/obj/structure/chair/sofa/bench/left{ + dir = 4 + }, +/obj/effect/landmark/start/assistant, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/turf/open/floor/wood, +/area/station/service/chapel/monastery) "nmq" = ( /obj/machinery/conveyor_switch/oneway{ dir = 8; @@ -41808,6 +41976,14 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/rd) +"nnd" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/space/nearstation) "nny" = ( /obj/effect/turf_decal/bot, /obj/machinery/portable_atmospherics/canister/nitrogen, @@ -41832,14 +42008,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/command/teleporter) -"noj" = ( -/obj/machinery/light/small/directional/north, -/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "nol" = ( /obj/machinery/disposal/bin/tagger, /obj/effect/turf_decal/bot, @@ -41850,13 +42018,6 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, /area/station/service/hydroponics) -"not" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "noE" = ( /obj/structure/chair, /obj/effect/turf_decal/tile/red{ @@ -42014,24 +42175,23 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) -"nqR" = ( -/obj/structure/table, -/obj/item/clothing/under/rank/prisoner/skirt{ - pixel_x = -13; - pixel_y = 5 +"nqF" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/airlock/highsecurity{ + name = "AI Upload" }, -/obj/item/clothing/under/rank/prisoner/skirt{ - pixel_x = 9; - pixel_y = 5 +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 }, -/obj/item/clothing/under/rank/prisoner{ - pixel_x = -2; - pixel_y = 5 +/obj/effect/mapping_helpers/airlock/access/all/command/ai_upload, +/obj/effect/landmark/navigate_destination/aiupload, +/obj/effect/turf_decal/stripes/line{ + dir = 4 }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/red/anticorner/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "nqX" = ( /obj/structure/lattice, /obj/structure/disposalpipe/segment{ @@ -42068,6 +42228,11 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/pharmacy) +"nrm" = ( +/obj/item/nullrod, +/obj/structure/table/wood, +/turf/open/floor/cult, +/area/station/service/chapel/office) "nrv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42228,19 +42393,6 @@ /obj/structure/window/reinforced/spawner/directional/south, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"ntR" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/machinery/firealarm/directional/north, -/turf/open/floor/iron, -/area/station/security/processing) "nuf" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, @@ -42265,6 +42417,42 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/aft) +"nuy" = ( +/obj/structure/table, +/obj/machinery/cell_charger, +/obj/item/assembly/prox_sensor{ + desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/assembly/prox_sensor{ + desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/assembly/prox_sensor{ + desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/assembly/prox_sensor{ + desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/stock_parts/power_store/cell/high, +/obj/item/stock_parts/power_store/cell/high, +/obj/item/stock_parts/power_store/cell/high, +/obj/item/crowbar/red, +/obj/item/toy/figure/roboticist{ + pixel_x = 6 + }, +/obj/machinery/light_switch/directional/north, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/science/robotics/lab) "nuz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42406,6 +42594,13 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/science/genetics) +"nwu" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/structure/closet/secure_closet/atmospherics, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "nwE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42444,12 +42639,6 @@ /obj/effect/turf_decal/tile/purple/anticorner/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"nwW" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 5 - }, -/turf/open/floor/engine, -/area/station/science/auxlab) "nwY" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -42516,6 +42705,10 @@ /obj/structure/cable, /turf/open/space/basic, /area/space/nearstation) +"nxX" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/carpet/green, +/area/station/service/lawoffice) "nxY" = ( /obj/structure/extinguisher_cabinet/directional/east, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -42536,25 +42729,26 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"nyw" = ( -/obj/machinery/door/airlock/engineering{ - name = "Emergency Storage" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/corner, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/unres/delayed, -/obj/effect/mapping_helpers/airlock/access/any/service/janitor, -/obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) +"nyA" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/structure/table, +/obj/item/storage/belt/utility, +/turf/open/floor/iron, +/area/station/engineering/lobby) "nyN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/computer/security/telescreen/research/directional/west, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/science) +"nyV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "nzz" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/stripes/line{ @@ -42577,6 +42771,14 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"nzR" = ( +/obj/effect/landmark/start/warden, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/turf/open/floor/iron/showroomfloor, +/area/station/security/warden) "nzW" = ( /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 4 @@ -42632,6 +42834,19 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"nBl" = ( +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 4 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/pharmacy) "nBD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42722,15 +42937,6 @@ "nCL" = ( /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"nCU" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/pharmacy) "nDi" = ( /obj/machinery/meter, /obj/effect/turf_decal/stripes/line{ @@ -42748,6 +42954,18 @@ /obj/effect/turf_decal/tile/blue/opposingcorners, /turf/open/floor/iron, /area/station/engineering/atmos) +"nDk" = ( +/obj/structure/table/wood, +/obj/structure/cable, +/obj/item/pen/fountain{ + pixel_x = 4 + }, +/obj/item/storage/photo_album/chapel{ + pixel_x = -16 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/grimy, +/area/station/service/chapel/office) "nDp" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42757,17 +42975,13 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"nDK" = ( -/obj/machinery/flasher/directional/east{ - id = "AI"; - name = "Meatbag Pacifier" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 6 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) +"nDu" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "nDM" = ( /obj/machinery/button/door/directional/south{ id = "ordnancemix"; @@ -42793,12 +43007,6 @@ }, /turf/open/floor/iron/dark, /area/station/security/detectives_office) -"nDX" = ( -/obj/machinery/holopad, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "nDZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/siding/purple{ @@ -42875,6 +43083,11 @@ /obj/item/kirbyplants/organic/plant10, /turf/open/floor/iron/dark, /area/station/science/lobby) +"nEB" = ( +/obj/structure/flora/bush/flowers_pp/style_random, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/grass, +/area/station/service/chapel) "nEQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42899,23 +43112,11 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden) -"nFe" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) -"nFg" = ( -/obj/effect/turf_decal/box/corners{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +"nFl" = ( +/obj/structure/urinal/directional/north, +/obj/machinery/duct, +/turf/open/floor/plating/rust, +/area/station/security/prison/shower) "nFm" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/yellow/half/contrasted{ @@ -42937,6 +43138,14 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/commons/storage/primary) +"nFw" = ( +/obj/structure/grille/broken, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/maintenance/aft) "nFy" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -42968,6 +43177,13 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"nGx" = ( +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/security/brig) "nGC" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -42977,20 +43193,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/break_room) -"nGE" = ( -/obj/structure/table/wood/fancy, -/obj/item/food/grown/poppy{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy, -/obj/machinery/firealarm/directional/north, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "nGH" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -43057,25 +43259,11 @@ }, /turf/open/floor/iron, /area/station/commons/storage/primary) -"nHv" = ( -/obj/effect/turf_decal/bot, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/machinery/light/small/directional/north, -/obj/machinery/power/shieldwallgen, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/command/teleporter) "nHy" = ( /obj/effect/landmark/start/hangover, /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"nHC" = ( -/obj/structure/chair/office{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "nHO" = ( /turf/closed/wall/rust, /area/station/service/janitor) @@ -43263,10 +43451,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"nKy" = ( -/obj/machinery/light/small/directional/west, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "nKC" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -43302,6 +43486,15 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"nLt" = ( +/obj/structure/table, +/obj/machinery/firealarm/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/item/ai_module/law/supplied/freeform, +/obj/item/ai_module/law/supplied/freeform, +/obj/item/ai_module/law/supplied/freeform, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "nLT" = ( /obj/docking_port/stationary/escape_pod{ dir = 2 @@ -43322,6 +43515,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"nMh" = ( +/obj/machinery/light_switch/directional/south, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/engine, +/area/station/ai/satellite/interior) "nMj" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -43353,6 +43553,18 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"nMX" = ( +/obj/machinery/computer/crew{ + dir = 1 + }, +/obj/effect/turf_decal/bot, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/security/checkpoint/medical) "nNb" = ( /obj/structure/lattice/catwalk, /turf/open/space/basic, @@ -43450,6 +43662,18 @@ /obj/machinery/incident_display/delam/directional/north, /turf/open/floor/iron/dark, /area/station/engineering/lobby) +"nPx" = ( +/obj/structure/railing{ + dir = 4 + }, +/obj/effect/turf_decal/siding/wideplating/dark/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "nPS" = ( /obj/structure/rack, /obj/item/storage/toolbox/drone, @@ -43531,42 +43755,19 @@ /obj/machinery/status_display/evac/directional/south, /turf/open/floor/plating, /area/station/cargo/storage) -"nQX" = ( +"nSa" = ( /obj/effect/turf_decal/bot, -/obj/structure/closet/wardrobe/mixed, -/obj/machinery/light/small/directional/south, +/obj/structure/closet/secure_closet/personal, +/obj/item/storage/backpack, +/obj/item/storage/backpack/satchel, +/obj/item/clothing/suit/hooded/wintercoat, +/obj/item/clothing/shoes/winterboots, +/obj/machinery/airalarm/directional/north, /obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/warm/directional/north, /turf/open/floor/iron/dark, /area/station/commons/locker) -"nRC" = ( -/obj/structure/rack, -/obj/item/storage/toolbox/emergency{ - pixel_y = 5 - }, -/obj/item/stack/cable_coil, -/obj/item/wirecutters, -/obj/effect/turf_decal/bot, -/obj/machinery/airalarm/directional/west, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/machinery/camera/directional/north{ - c_tag = "Recreation Arcade"; - name = "recreation camera" - }, -/obj/effect/decal/cleanable/cobweb, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) -"nSf" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/light/directional/east, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance/office) "nSh" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -43591,17 +43792,6 @@ /obj/structure/sign/warning/no_smoking, /turf/closed/wall/rust, /area/station/maintenance/port/greater) -"nSZ" = ( -/obj/structure/table, -/obj/item/storage/box/bodybags{ - pixel_x = 6 - }, -/obj/item/storage/box/gloves{ - pixel_x = -5 - }, -/obj/machinery/light/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) "nTd" = ( /obj/structure/cable, /obj/effect/decal/cleanable/dirt, @@ -43734,6 +43924,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/showroomfloor, /area/station/medical/virology) +"nXg" = ( +/obj/effect/landmark/start/warden, +/obj/structure/chair/office, +/obj/effect/turf_decal/tile/red/half/contrasted, +/turf/open/floor/iron/showroomfloor, +/area/station/security/warden) "nXp" = ( /obj/structure/table/reinforced, /obj/machinery/button/door{ @@ -43780,6 +43976,17 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hos) +"nYg" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "nYo" = ( /obj/structure/table, /obj/item/storage/backpack/duffelbag/engineering{ @@ -43847,13 +44054,6 @@ /obj/structure/noticeboard/directional/east, /turf/open/floor/plating, /area/station/maintenance/fore) -"nZE" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/processing) "nZF" = ( /obj/effect/turf_decal/siding/yellow{ dir = 9 @@ -43948,18 +44148,6 @@ /obj/machinery/atmospherics/pipe/bridge_pipe/scrubbers/visible, /turf/open/space/basic, /area/space/nearstation) -"oaU" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/turf/open/floor/iron/dark/corner{ - dir = 1 - }, -/area/station/hallway/primary/fore) "obh" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -43974,6 +44162,15 @@ /obj/structure/grille/broken, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"obv" = ( +/obj/structure/displaycase/labcage, +/obj/effect/turf_decal/box, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/rd) "obM" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -43983,6 +44180,21 @@ }, /turf/open/floor/iron/dark, /area/station/commons/locker) +"obO" = ( +/obj/machinery/firealarm/directional/north, +/obj/machinery/camera/directional/north{ + c_tag = "Arrivals Dock"; + name = "shuttle camera" + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/closet/firecloset, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "obU" = ( /obj/structure/table/glass, /obj/item/clothing/gloves/latex, @@ -43998,6 +44210,17 @@ }, /turf/open/floor/iron/dark, /area/station/medical/virology) +"ocd" = ( +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 4 + }, +/obj/effect/turf_decal/delivery, +/obj/machinery/light_switch/directional/north, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "oce" = ( /obj/structure/flora/bush/lavendergrass/style_random, /obj/structure/flora/bush/sparsegrass/style_random, @@ -44020,12 +44243,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) -"ocq" = ( -/obj/structure/closet/secure_closet/brig, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/red/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) "oct" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, @@ -44096,15 +44313,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/service/hydroponics) -"odc" = ( -/obj/machinery/door/poddoor/preopen{ - id = "brigfrontdoor"; - name = "Front Security Blast Door" - }, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/firedoor, -/turf/open/floor/iron/dark, -/area/station/security/brig) "odd" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, /obj/machinery/door/airlock/research/glass{ @@ -44130,6 +44338,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) +"oeL" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "oeS" = ( /obj/machinery/atmospherics/components/unary/outlet_injector/monitored/nitrogen_input{ dir = 4 @@ -44212,6 +44430,14 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/aft) +"ofu" = ( +/obj/machinery/power/emitter/welded{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/engineering/supermatter/room) "ofx" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/preopen{ @@ -44233,15 +44459,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/paramedic) -"ogg" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark/corner{ - dir = 4 - }, -/area/station/hallway/primary/fore) "ogk" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/brown/half/contrasted{ @@ -44254,20 +44471,20 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating/lowpressure, /area/space/nearstation) -"ogM" = ( -/obj/structure/table/wood/fancy, -/obj/effect/turf_decal/siding/wideplating/dark/corner, -/obj/item/food/grown/poppy{ - pixel_x = -6; - pixel_y = 6 +"ohe" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ + dir = 4 }, -/obj/item/food/grown/poppy{ - pixel_x = 6; - pixel_y = 6 +/obj/structure/sign/warning/vacuum/external/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/duct, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 }, -/obj/item/food/grown/poppy, /turf/open/floor/iron/dark, -/area/station/service/chapel) +/area/station/engineering/atmos) "ohj" = ( /obj/effect/turf_decal/loading_area{ dir = 8 @@ -44385,35 +44602,34 @@ "oiO" = ( /turf/closed/wall/mineral/bronze, /area/station/maintenance/department/chapel) +"oiR" = ( +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/obj/machinery/portable_atmospherics/scrubber, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "oiS" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"oiU" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "oiV" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/remains/human, /obj/item/clothing/neck/tie/detective, /turf/open/floor/carpet/green, /area/station/maintenance/port/greater) -"ojj" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red, -/obj/machinery/wall_healer/directional/east, -/turf/open/floor/iron, -/area/station/engineering/lobby) "ojs" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/newscaster/directional/east, @@ -44427,13 +44643,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"ojW" = ( -/obj/structure/closet/secure_closet/security/sec, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/turf/open/floor/iron/dark, -/area/station/security/lockers) "ojZ" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -44443,14 +44652,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/central) -"okc" = ( -/obj/machinery/light/directional/north, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/iron/smooth_large, -/area/station/maintenance/department/chapel/monastery) "oki" = ( /obj/machinery/airalarm/directional/west, /obj/machinery/status_display/evac/directional/north, @@ -44478,18 +44679,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/engineering/atmos) -"okr" = ( -/obj/structure/bodycontainer/morgue{ - dir = 8 - }, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/stripes/white/line{ - dir = 8 - }, -/turf/open/floor/iron/dark/side{ - dir = 8 - }, -/area/station/service/chapel/funeral) "okt" = ( /obj/machinery/door/window/left/directional/east{ name = "Creature Cell"; @@ -44504,6 +44693,29 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"okE" = ( +/obj/structure/table, +/obj/item/clothing/gloves/latex, +/obj/item/clothing/gloves/latex, +/obj/item/storage/box/monkeycubes{ + pixel_x = 2; + pixel_y = 5 + }, +/obj/item/storage/box/monkeycubes{ + pixel_y = 3 + }, +/obj/effect/turf_decal/siding/purple/corner{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 1 + }, +/obj/item/paper/crumpled{ + default_raw_text = "This isn't funny, I'm trapped on the least fun room on the station."; + name = "poorly written complaint" + }, +/turf/open/floor/iron/dark, +/area/station/science/xenobiology) "okJ" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ @@ -44516,29 +44728,6 @@ "okN" = ( /turf/closed/wall/rust, /area/station/service/bar/backroom) -"okQ" = ( -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/east, -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 8 - }, -/obj/machinery/portable_atmospherics/canister/anesthetic_mix, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/cryo) -"olq" = ( -/obj/effect/turf_decal/tile/brown/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "olv" = ( /obj/structure/flora/bush/grassy/style_random, /obj/structure/flora/bush/sparsegrass/style_random, @@ -44553,6 +44742,18 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/dark, /area/station/hallway/primary/aft) +"olF" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/button/door/directional/south{ + id = "morgueprivacy"; + name = "Morgue Privacy Shutter"; + pixel_x = -8 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "olL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -44567,13 +44768,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science) -"olN" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/west, -/obj/machinery/newscaster/directional/west, -/obj/effect/turf_decal/stripes/line, -/turf/open/floor/iron, -/area/station/command/heads_quarters/hop) "olP" = ( /obj/machinery/door/airlock/atmos{ name = "Filter Chamber" @@ -44611,6 +44805,21 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) +"omr" = ( +/obj/machinery/conveyor{ + dir = 4; + id = "packagereturn"; + name = "crate return belt" + }, +/obj/effect/turf_decal/bot, +/obj/machinery/camera/directional/south{ + c_tag = "Delivery Office"; + name = "cargo camera"; + network = list("ss13","qm") + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/cargo/sorting) "omH" = ( /obj/machinery/door/poddoor/shutters{ id = "Skynet_launch"; @@ -44675,10 +44884,20 @@ /obj/item/circuitboard/machine/telecomms/processor, /turf/open/floor/iron/dark, /area/station/engineering/storage/tcomms) -"onr" = ( -/obj/machinery/light/directional/west, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) +"onn" = ( +/obj/structure/cable, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "ons" = ( /obj/machinery/recharger{ pixel_x = -7 @@ -44708,6 +44927,27 @@ }, /turf/open/floor/iron/dark, /area/station/security/warden) +"onu" = ( +/obj/structure/closet/secure_closet/security/sec, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/turf/open/floor/iron/dark, +/area/station/security/lockers) +"ooo" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/south, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/machinery/disposal/bin/tagger, +/obj/machinery/light/cold/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/pharmacy) "oou" = ( /obj/machinery/telecomms/receiver/preset_right, /obj/effect/turf_decal/stripes/line{ @@ -44734,16 +44974,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/fore) -"opE" = ( -/obj/effect/landmark/start/scientist, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/science/xenobiology) "opL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -44756,12 +44986,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) -"opV" = ( -/obj/structure/transit_tube/curved/flipped{ - dir = 1 - }, -/turf/open/space/basic, -/area/space/nearstation) "oqa" = ( /obj/effect/decal/cleanable/cobweb, /obj/machinery/light/small/directional/west, @@ -44773,26 +44997,6 @@ }, /turf/open/floor/plating, /area/station/science) -"oqu" = ( -/obj/effect/turf_decal/plaque{ - icon_state = "L9" - }, -/obj/machinery/camera/directional/north{ - c_tag = "Fore Hallway Centre"; - name = "fore camera" - }, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/machinery/light/small/directional/north, -/turf/open/floor/iron, -/area/station/hallway/primary/central/fore) "oqA" = ( /obj/structure/flora/grass/jungle/b/style_5, /turf/open/misc/asteroid, @@ -44856,11 +45060,34 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"orI" = ( +/obj/machinery/teleport/station, +/obj/machinery/status_display/evac/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/teleporter) "orT" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, /turf/open/floor/plating, /area/station/tcommsat/computer) +"orW" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "orY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/extinguisher_cabinet/directional/south, @@ -44914,34 +45141,17 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/port) +"otn" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 5 + }, +/obj/effect/spawner/random/gizmo, +/turf/open/floor/engine, +/area/station/science/auxlab) "otr" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/science/auxlab) -"otA" = ( -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) -"otF" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/light/small/directional/west, -/obj/structure/bookcase/random, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) "otO" = ( /obj/structure/table, /obj/item/taperecorder, @@ -44979,19 +45189,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/engineering/atmos) -"ouD" = ( -/obj/machinery/light/directional/south, -/obj/machinery/light_switch/directional/south, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/structure/table/glass, -/obj/item/mod/module/thermal_regulator, -/obj/item/mod/module/plasma_stabilizer, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/break_room) "ouE" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/stripes/line{ @@ -45006,19 +45203,6 @@ }, /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) -"ouK" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/east, -/obj/machinery/requests_console/directional/east{ - department = "Pharmacy"; - name = "Pharmacy Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/ore_update, -/obj/effect/mapping_helpers/requests_console/supplies, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/obj/machinery/chem_dispenser, -/turf/open/floor/iron/dark, -/area/station/medical/pharmacy) "ouM" = ( /obj/structure/cable, /obj/effect/landmark/start/hangover, @@ -45103,6 +45287,35 @@ /obj/structure/cable, /turf/open/floor/iron/grimy, /area/station/hallway/primary/fore) +"owl" = ( +/obj/structure/table, +/obj/item/mmi, +/obj/item/bodypart/chest/robot{ + pixel_y = 4 + }, +/obj/item/bodypart/leg/right/robot{ + pixel_x = 6 + }, +/obj/item/bodypart/arm/right/robot{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/bodypart/leg/left/robot{ + pixel_x = -6 + }, +/obj/item/bodypart/arm/left/robot{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/bodypart/head/robot, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "owm" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/stripes/corner, @@ -45196,27 +45409,65 @@ /obj/item/kirbyplants/organic/plant21, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"oxp" = ( -/obj/machinery/light/directional/west, -/obj/structure/closet/secure_closet/psychology, -/obj/item/radio/intercom/directional/north, -/turf/open/floor/carpet, -/area/station/medical/psychology) -"oxC" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 4 +"oxn" = ( +/obj/effect/turf_decal/box, +/obj/structure/cable, +/obj/machinery/power/shieldwallgen/xenobiologyaccess, +/obj/effect/mapping_helpers/burnt_floor, +/obj/structure/sign/warning/gas_mask/directional/north, +/turf/open/floor/plating, +/area/station/science/xenobiology) +"oxG" = ( +/obj/item/canvas/nineteen_nineteen, +/obj/item/canvas/nineteen_nineteen, +/obj/item/canvas/nineteen_nineteen, +/obj/item/canvas/nineteen_nineteen, +/obj/structure/table/rolling, +/obj/item/storage/crayons{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/effect/spawner/random/contraband/prison, +/obj/item/storage/crayons{ + pixel_x = -6; + pixel_y = 1 + }, +/obj/item/paper_bin{ + pixel_x = 11; + pixel_y = 4 + }, +/obj/item/pen{ + pixel_x = 13; + pixel_y = 9 + }, +/obj/item/pen{ + pixel_x = 11; + pixel_y = 6 + }, +/obj/item/pen{ + pixel_x = 11; + pixel_y = 4 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/prison/garden) -"oxD" = ( /obj/structure/cable, +/turf/open/floor/iron/grimy, +/area/station/security/prison) +"oxJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/service/chapel) +/obj/structure/cable, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "oxK" = ( /obj/structure/table, /obj/machinery/light/small/directional/north, @@ -45228,17 +45479,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"oye" = ( -/obj/machinery/light/small/directional/north, -/obj/structure/closet/emcloset/anchored, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/plating, -/area/station/service/chapel) "oyA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -45261,13 +45501,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"ozd" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) "ozk" = ( /obj/machinery/atmospherics/pipe/smart/simple/orange/visible{ dir = 9 @@ -45322,17 +45555,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"ozV" = ( -/obj/structure/rack, -/obj/effect/turf_decal/bot, -/obj/item/storage/medkit/o2, -/obj/item/tank/internals/emergency_oxygen, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) "ozZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -45365,6 +45587,19 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/port) +"oAF" = ( +/obj/structure/bed, +/obj/machinery/iv_drip, +/obj/effect/turf_decal/bot_white/right, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/item/bedsheet/medical, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/medbay/central) "oAJ" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -45387,27 +45622,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/hallway/primary/fore) -"oAO" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/obj/machinery/wall_healer/directional/north, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/central/fore) -"oAS" = ( -/obj/machinery/light/directional/south, -/obj/machinery/requests_console/directional/south{ - department = "Janitorial"; - name = "Janitorial Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/turf_decal/stripes/corner, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple/half/contrasted, -/obj/effect/turf_decal/tile/blue, -/turf/open/floor/iron, -/area/station/service/janitor) "oBe" = ( /obj/effect/turf_decal/delivery, /obj/machinery/door/window/right/directional/north{ @@ -45639,6 +45853,13 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"oFk" = ( +/obj/structure/transit_tube/curved/flipped{ + dir = 1 + }, +/obj/structure/lattice, +/turf/open/space/basic, +/area/space/nearstation) "oFl" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -45695,18 +45916,6 @@ /obj/effect/turf_decal/siding/thinplating/dark, /turf/open/floor/iron/smooth_large, /area/station/maintenance/department/chapel/monastery) -"oGO" = ( -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "oGV" = ( /obj/machinery/status_display/evac/directional/south, /obj/effect/turf_decal/tile/purple/half/contrasted, @@ -45751,6 +45960,17 @@ /obj/structure/cable, /turf/open/floor/iron/showroomfloor, /area/station/security/office) +"oHB" = ( +/obj/machinery/door/airlock/engineering{ + name = "Emergency Storage" + }, +/obj/effect/turf_decal/stripes/corner, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/unres/delayed, +/obj/effect/mapping_helpers/airlock/access/any/service/janitor, +/obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/entry) "oHD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -45776,6 +45996,17 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/plating, /area/station/maintenance/starboard) +"oIf" = ( +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/ore_silo, +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/machinery/light/small/red/directional/east, +/turf/open/floor/iron/dark, +/area/station/command/vault) "oIg" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ @@ -45827,26 +46058,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/security/checkpoint/engineering) -"oIU" = ( -/obj/item/radio/intercom/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/blood/old, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) -"oJa" = ( -/obj/machinery/airalarm/directional/north, -/obj/machinery/light/small/directional/north, -/obj/machinery/navbeacon{ - codes_txt = "delivery;dir=8"; - location = "QM #1" - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/effect/turf_decal/delivery, -/obj/effect/decal/cleanable/blood/oil, -/mob/living/basic/bot/mulebot, -/turf/open/floor/iron/dark, -/area/station/cargo/warehouse) "oJd" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/machinery/door/window/right/directional/east{ @@ -45863,24 +46074,6 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/sorting) -"oJm" = ( -/obj/structure/cable, -/turf/open/floor/iron/showroomfloor, -/area/station/science/xenobiology) -"oJq" = ( -/obj/machinery/disposal/bin/tagger, -/obj/effect/turf_decal/bot, -/obj/item/radio/intercom/directional/east, -/obj/machinery/light/directional/east, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/hop) "oJr" = ( /obj/structure/reagent_dispensers/watertank, /obj/effect/turf_decal/delivery, @@ -45922,6 +46115,18 @@ /obj/structure/chair/stool/directional/north, /turf/open/floor/wood, /area/station/service/library) +"oJW" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/purple, +/obj/machinery/duct, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "oKp" = ( /obj/structure/chair/office/light{ dir = 4 @@ -45974,6 +46179,15 @@ /obj/structure/lattice, /turf/open/space/basic, /area/space/nearstation) +"oLq" = ( +/obj/structure/table, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/item/storage/medkit/emergency, +/obj/item/clothing/under/rank/security/officer, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "oLs" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -46117,22 +46331,18 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/cargo/office) +"oOo" = ( +/obj/structure/chair/office{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/aft) "oOW" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"oPf" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "oPh" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/holopad, @@ -46163,6 +46373,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) +"oPN" = ( +/obj/item/kirbyplants/organic/plant21, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark/corner{ + dir = 8 + }, +/area/station/hallway/primary/central/fore) "oPO" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/window/reinforced/spawner/directional/east, @@ -46172,6 +46391,12 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"oPS" = ( +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/green/half/contrasted, +/obj/machinery/light/cold/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "oPX" = ( /obj/machinery/atmospherics/components/unary/thermomachine/freezer/layer2{ dir = 8 @@ -46185,6 +46410,18 @@ "oQg" = ( /turf/closed/wall/r_wall, /area/station/science/auxlab) +"oQh" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/status_display/evac/directional/east, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark/corner{ + dir = 4 + }, +/area/station/hallway/primary/port) "oQk" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, @@ -46250,21 +46487,6 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/sorting) -"oRz" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/light/directional/south, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) -"oRA" = ( -/mob/living/carbon/human/species/monkey, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/grass, -/area/station/science/auxlab) "oRI" = ( /obj/effect/spawner/random/structure/table, /obj/item/flashlight/flare/candle{ @@ -46294,6 +46516,26 @@ }, /turf/open/floor/iron, /area/station/security/office) +"oRX" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/structure/table, +/obj/item/surgery_tray/full, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/medical/surgery/theatre) +"oSd" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "oSf" = ( /obj/machinery/atmospherics/components/binary/pump{ dir = 8 @@ -46303,6 +46545,13 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/freezerchamber) +"oSq" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/tile/yellow{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "oSs" = ( /obj/structure/flora/grass/jungle/a/style_random, /obj/effect/turf_decal/stripes/line{ @@ -46362,6 +46611,10 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) +"oSW" = ( +/obj/effect/turf_decal/tile/yellow, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "oSX" = ( /obj/item/paper_bin{ pixel_x = -4; @@ -46394,16 +46647,6 @@ }, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"oTt" = ( -/obj/structure/sign/painting/library{ - pixel_x = 32 - }, -/obj/machinery/photocopier, -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/box/white, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) "oTw" = ( /obj/machinery/telecomms/processor/preset_one, /turf/open/floor/circuit/green/telecomms/mainframe, @@ -46412,20 +46655,6 @@ /obj/machinery/atmospherics/pipe/smart/simple/dark/visible, /turf/closed/wall/r_wall, /area/station/science/ordnance/burnchamber) -"oTE" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "oTP" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/gibs/old, @@ -46435,6 +46664,22 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"oTQ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/spawner/xmastree/rdrod, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lobby) "oTU" = ( /obj/effect/turf_decal/box/corners, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -46568,17 +46813,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/carpet/red, /area/station/service/chapel) -"oWj" = ( -/obj/machinery/atmospherics/components/binary/tank_compressor{ - dir = 8 - }, -/obj/effect/turf_decal/delivery, -/obj/structure/sign/poster/random/directional/north, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) "oWm" = ( /obj/effect/mapping_helpers/airlock/access/all/science/genetics, /obj/machinery/door/airlock/maintenance{ @@ -46595,12 +46829,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"oWG" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "oWI" = ( /obj/structure/table/reinforced, /obj/structure/reagent_dispensers/servingdish, @@ -46658,16 +46886,20 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/security/brig) -"oXm" = ( +"oXh" = ( /obj/structure/cable, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/duct, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) +"oXt" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/caution/stand_clear, +/obj/machinery/newscaster/directional/south, +/obj/machinery/light/directional/south, +/turf/open/floor/iron/dark, +/area/station/security/brig) "oXx" = ( /obj/machinery/door/airlock/maintenance, /obj/structure/disposalpipe/segment, @@ -46708,27 +46940,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) -"oYd" = ( -/obj/machinery/light/directional/south, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/loading_area{ - dir = 4; - pixel_x = 5 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/atmos/storage/gas) "oYi" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -46744,26 +46955,36 @@ dir = 1 }, /area/station/hallway/primary/central) -"oYu" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/disposal/bin/tagger, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/virology) +"oYp" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral, +/obj/structure/cable, +/turf/open/floor/iron, +/area/station/security/processing) "oYx" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/old, /obj/effect/landmark/blobstart, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"oYE" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 8 + }, +/obj/machinery/portable_atmospherics/canister/anesthetic_mix, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/airalarm/directional/east, +/turf/open/floor/iron/dark, +/area/station/medical/cryo) +"oYK" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/wood, +/area/station/commons/vacant_room/commissary) "oYN" = ( /obj/machinery/air_sensor/nitrous_tank, /turf/open/floor/engine/n2o, @@ -46905,6 +47126,20 @@ /obj/structure/sign/warning/vacuum/external, /turf/closed/wall/rust, /area/station/maintenance/disposal) +"pbt" = ( +/obj/structure/table, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/construction/plumbing, +/obj/item/construction/plumbing, +/obj/machinery/light_switch/directional/north, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "pbx" = ( /obj/machinery/computer/libraryconsole/bookmanagement, /obj/structure/table, @@ -46967,24 +47202,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"pcO" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/camera/directional/east{ - c_tag = "E.V.A. Storage"; - name = "command camera" - }, -/obj/machinery/light/directional/east, -/obj/machinery/suit_storage_unit/standard_unit{ - desc = "An industrial suit storage device carrying retro space suits. Neat!"; - helmet_type = /obj/item/clothing/head/helmet/space; - suit_type = /obj/item/clothing/suit/space - }, -/obj/machinery/status_display/ai/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/eva) "pdg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -47070,18 +47287,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"peu" = ( -/obj/structure/reagent_dispensers/watertank, -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/atmos) "peC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -47107,17 +47312,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) -"peJ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red, -/turf/open/floor/iron, -/area/station/commons/fitness/recreation) "peT" = ( /obj/machinery/light/small/directional/west, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -47126,16 +47320,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/hallway/primary/aft) -"peY" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/cargo/office) "peZ" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/machinery/newscaster/directional/west, @@ -47197,6 +47381,15 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/maintenance/department/bridge) +"pgq" = ( +/obj/machinery/door/poddoor/preopen{ + id = "brigfrontdoor"; + name = "Front Security Blast Door" + }, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/effect/turf_decal/caution/stand_clear, +/turf/open/floor/iron/dark, +/area/station/security/brig) "pgA" = ( /obj/effect/turf_decal/bot, /obj/machinery/portable_atmospherics/canister/oxygen, @@ -47257,11 +47450,6 @@ }, /turf/open/misc/asteroid, /area/space/nearstation) -"phX" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "phY" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/tile/yellow, @@ -47305,6 +47493,13 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) +"pim" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/item/radio/intercom/directional/north, +/obj/machinery/light/warm/dim/directional/north, +/turf/open/floor/iron, +/area/station/security/prison) "pio" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/cleanable/dirt, @@ -47344,22 +47539,6 @@ /obj/effect/turf_decal/tile/red/fourcorners, /turf/open/floor/iron/white, /area/station/security/prison/safe) -"piN" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/poddoor/shutters/preopen{ - dir = 8; - id = "ceprivate"; - name = "Chief Engineer's Privacy Shutters" - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "piQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -47424,6 +47603,19 @@ }, /turf/open/floor/iron/dark, /area/station/command/teleporter) +"pjx" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "pjC" = ( /obj/machinery/status_display/ai/directional/south, /obj/effect/turf_decal/stripes/corner{ @@ -47460,13 +47652,6 @@ /obj/item/pen, /turf/open/floor/wood, /area/station/maintenance/starboard/fore) -"pjX" = ( -/obj/effect/turf_decal/bot, -/obj/structure/closet/secure_closet/hydroponics, -/obj/structure/sign/poster/contraband/kudzu/directional/east, -/obj/effect/turf_decal/tile/green/fourcorners, -/turf/open/floor/iron/checker, -/area/station/service/hydroponics) "pjZ" = ( /obj/machinery/status_display/ai/directional/east, /obj/structure/disposalpipe/segment, @@ -47487,25 +47672,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) -"pkg" = ( -/obj/structure/cable, -/obj/machinery/power/terminal{ - dir = 8 - }, -/turf/open/floor/catwalk_floor/iron_smooth, -/area/station/maintenance/department/chapel/monastery) -"pkr" = ( -/obj/machinery/light/directional/east, -/obj/machinery/newscaster/directional/east, -/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/engineering/atmos) "pkC" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/machinery/atmospherics/pipe/bridge_pipe/green/visible{ @@ -47707,24 +47873,12 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lobby) -"pmI" = ( -/obj/item/kirbyplants/random, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "pmM" = ( /obj/effect/turf_decal/stripes/line{ dir = 6 }, /turf/open/floor/plating/airless, /area/space/nearstation) -"pnd" = ( -/obj/effect/turf_decal/bot, -/obj/effect/spawner/random/structure/crate_abandoned, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "pnl" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -47851,6 +48005,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"poC" = ( +/obj/structure/filingcabinet/chestdrawer, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/security/courtroom) "poK" = ( /obj/machinery/suit_storage_unit/ce, /obj/effect/turf_decal/delivery, @@ -47893,25 +48053,6 @@ /obj/effect/spawner/random/trash/food_packaging, /turf/open/floor/plating, /area/station/maintenance/department/bridge) -"ppy" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp/green{ - pixel_x = -5; - pixel_y = 7 - }, -/obj/item/pen{ - pixel_x = 8; - pixel_y = 5 - }, -/obj/item/pen/red{ - pixel_x = 5; - pixel_y = 1 - }, -/obj/item/toy/figure/detective, -/obj/machinery/light/directional/east, -/obj/item/radio/intercom/directional/east, -/turf/open/floor/carpet/green, -/area/station/security/detectives_office) "ppB" = ( /obj/structure/table, /obj/item/storage/box/syringes{ @@ -47951,19 +48092,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/command/eva) -"pqh" = ( -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/structure/closet/crate, -/obj/structure/grille/broken, -/obj/item/clothing/gloves/color/fyellow, -/obj/item/flashlight, -/obj/item/flashlight/flare, -/obj/machinery/light/small/directional/west, -/obj/item/relic, -/obj/structure/sign/poster/contraband/random/directional/west, -/turf/open/floor/plating, -/area/station/maintenance/starboard) "pqm" = ( /obj/effect/turf_decal/siding/purple{ dir = 1 @@ -47971,6 +48099,14 @@ /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/iron, /area/station/science/robotics/mechbay) +"pqp" = ( +/obj/effect/turf_decal/box/corners{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "pqu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -48014,17 +48150,6 @@ "pqD" = ( /turf/closed/wall, /area/station/maintenance/port/lesser) -"pqF" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/command{ - name = "Chief Engineer's Office" - }, -/obj/effect/turf_decal/siding/yellow/corner{ - dir = 4 - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/ce, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) "pqH" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, @@ -48034,10 +48159,24 @@ "pqS" = ( /turf/open/floor/carpet, /area/station/medical/psychology) -"pra" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/cargo/miningdock) +"pqU" = ( +/obj/effect/landmark/secequipment, +/obj/effect/turf_decal/bot, +/obj/machinery/status_display/evac/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/security/office) +"prc" = ( +/obj/structure/table, +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/effect/spawner/random/aimodule/harmful, +/obj/machinery/door/window/brigdoor/left/directional/south{ + name = "High-Risk Modules"; + req_access = list("captain") + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "pri" = ( /obj/structure/window/spawner/directional/north, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -48073,6 +48212,26 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"prC" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/siding/yellow{ + dir = 1 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "prK" = ( /obj/machinery/disposal/bin/tagger, /obj/machinery/firealarm/directional/south, @@ -48118,25 +48277,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/storage/tech) -"psn" = ( -/obj/item/clothing/mask/gas/sechailer{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/clothing/mask/gas/sechailer, -/obj/item/clothing/mask/gas/sechailer{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/flashlight/seclite, -/obj/item/flashlight/seclite, -/obj/item/flashlight/seclite, -/obj/effect/decal/cleanable/cobweb, -/obj/machinery/airalarm/directional/west, -/obj/structure/table, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/security/armory) "psv" = ( /turf/closed/wall/rust, /area/station/medical/psychology) @@ -48173,16 +48313,6 @@ /obj/item/poster/random_contraband, /turf/open/floor/plating/airless, /area/space/nearstation) -"ptQ" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron/cafeteria, -/area/station/security/prison/garden) "puy" = ( /obj/structure/flora/grass/jungle/b/style_random, /obj/structure/flora/bush/grassy/style_random, @@ -48215,6 +48345,13 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"puL" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/red/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/obj/structure/cable, +/turf/open/floor/iron, +/area/station/security/processing) "puS" = ( /obj/effect/turf_decal/delivery, /obj/machinery/power/emitter, @@ -48359,6 +48496,13 @@ "pxe" = ( /turf/closed/wall/mineral/plastitanium, /area/station/maintenance/port/aft) +"pxl" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "pxE" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall/r_wall, @@ -48444,17 +48588,13 @@ /obj/machinery/telecomms/bus/preset_one, /turf/open/floor/circuit/green/telecomms/mainframe, /area/station/tcommsat/server) -"pAq" = ( -/obj/machinery/newscaster/directional/south, -/obj/effect/turf_decal/siding/white{ - dir = 4 - }, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/fore) +"pzW" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/service/chapel/dock) "pAr" = ( /obj/machinery/door/airlock/external/glass{ name = "Supply Door Airlock" @@ -48524,6 +48664,33 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) +"pBV" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/machinery/door_buttons/airlock_controller{ + idExterior = "virology_airlock_exterior"; + idInterior = "virology_airlock_interior"; + idSelf = "virology_airlock_control"; + name = "Virology Access Console"; + pixel_x = 24; + pixel_y = -24; + req_access = list("virology") + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/tile/green/anticorner/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "pCa" = ( /obj/machinery/door/poddoor/preopen{ id = "xeno3"; @@ -48551,6 +48718,32 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/electrical) +"pCp" = ( +/obj/structure/table, +/obj/item/paper_bin{ + pixel_x = -5 + }, +/obj/item/pen{ + pixel_x = -5 + }, +/obj/machinery/flasher/directional/east{ + id = "hopflash"; + name = "Crowd Pacifier" + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/status_display/evac/directional/north, +/obj/structure/desk_bell{ + pixel_x = 7; + pixel_y = 10 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/hop) "pCu" = ( /turf/open/floor/plating, /area/station/hallway/primary/fore) @@ -48669,6 +48862,12 @@ /obj/effect/decal/cleanable/generic, /turf/open/floor/plating, /area/station/maintenance/department/bridge) +"pEp" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "pEt" = ( /obj/machinery/door/airlock/maintenance, /obj/structure/cable, @@ -48791,6 +48990,12 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/office) +"pGc" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/maintenance/aft) "pGe" = ( /obj/item/kirbyplants, /obj/effect/turf_decal/tile/neutral{ @@ -48798,14 +49003,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) -"pGg" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/tile/brown/half/contrasted, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "pGj" = ( /obj/effect/turf_decal/bot, /obj/structure/rack, @@ -48835,24 +49032,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) -"pGE" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/light/directional/east, -/obj/structure/rack, -/obj/item/mod/module/thermal_regulator, -/obj/item/mod/module/plasma_stabilizer, -/obj/item/stack/cable_coil, -/obj/item/electronics/apc, -/obj/item/stack/cable_coil, -/obj/item/electronics/apc, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark, -/area/station/engineering/main) "pGM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -48926,16 +49105,12 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"pIb" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/components/binary/pump/on/layer4{ - dir = 4; - name = "Airlock Pump"; - target_pressure = 300; - hide = 1 - }, +"pIf" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, -/area/station/cargo/miningdock) +/area/station/hallway/secondary/exit/departure_lounge) "pIl" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/public/glass{ @@ -48946,18 +49121,13 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) -"pIy" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/corner{ +"pIm" = ( +/obj/effect/turf_decal/stripes/line{ dir = 4 }, -/obj/machinery/light/directional/south, -/obj/structure/extinguisher_cabinet/directional/west, -/obj/machinery/status_display/ai/directional/south, -/obj/structure/cable/layer3, -/turf/open/floor/engine, -/area/station/ai/satellite/chamber) +/obj/machinery/airalarm/directional/north, +/turf/open/floor/iron/dark, +/area/station/service/chapel/dock) "pID" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -49025,6 +49195,13 @@ }, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"pJn" = ( +/obj/structure/railing/corner{ + dir = 8 + }, +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "pJr" = ( /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ dir = 1 @@ -49060,19 +49237,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) -"pJK" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment{ - dir = 9 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/commons/fitness/recreation) "pJM" = ( /obj/structure/table/reinforced, /obj/structure/window/reinforced/spawner/directional/west, @@ -49107,42 +49271,6 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/chamber) -"pKv" = ( -/obj/machinery/computer/cargo, -/obj/effect/turf_decal/bot, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/airalarm/directional/east, -/obj/machinery/requests_console/directional/north{ - department = "Quartermaster's Desk"; - name = "Quartermaster's Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/mapping_helpers/requests_console/supplies, -/obj/structure/extinguisher_cabinet/directional/north{ - pixel_x = 32 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/qm) -"pKB" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/decal/cleanable/blood/old, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ - dir = 1 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/plating, -/area/station/maintenance/disposal) "pKM" = ( /obj/machinery/mass_driver{ id = "trash" @@ -49155,14 +49283,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal) -"pKQ" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/light/directional/east, -/obj/machinery/computer/security/telescreen/entertainment/directional/east, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "pKR" = ( /obj/structure/cable, /turf/closed/wall/r_wall/rust, @@ -49228,6 +49348,13 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"pLy" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/obj/effect/landmark/start/station_engineer, +/turf/open/floor/iron, +/area/station/engineering/main) "pLB" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, @@ -49256,20 +49383,6 @@ /obj/item/radio, /turf/open/floor/iron/dark, /area/station/hallway/primary/fore) -"pLV" = ( -/obj/effect/decal/cleanable/dirt, -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted, -/obj/effect/turf_decal/tile/purple, -/obj/machinery/camera/autoname/directional/east, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) "pMe" = ( /obj/effect/turf_decal/siding/wideplating/dark{ dir = 4 @@ -49277,10 +49390,12 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/iron/dark, /area/station/service/chapel) -"pMj" = ( -/obj/effect/landmark/start/paramedic, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) +"pMz" = ( +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/ai_law_rack/base/core, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "pMS" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -49308,11 +49423,33 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, /turf/closed/wall, /area/station/maintenance/disposal/incinerator) +"pNh" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/fore) "pNA" = ( /obj/effect/turf_decal/siding/wood/corner, /obj/machinery/newscaster/directional/south, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) +"pNG" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "pNL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -49396,15 +49533,6 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/plating, /area/station/maintenance/starboard) -"pOG" = ( -/obj/machinery/light/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden, -/obj/machinery/airlock_sensor/incinerator_atmos{ - pixel_x = 24 - }, -/turf/open/floor/engine, -/area/station/maintenance/disposal/incinerator) "pOI" = ( /obj/structure/window/reinforced/spawner/directional/east, /obj/effect/turf_decal/stripes/line{ @@ -49416,15 +49544,6 @@ }, /turf/open/floor/plating, /area/station/hallway/primary/central/fore) -"pOO" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/prison/garden) "pOS" = ( /obj/item/paper_bin{ pixel_x = -3; @@ -49455,6 +49574,15 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"pOZ" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/structure/table, +/obj/item/storage/box, +/obj/item/radio/off{ + pixel_x = 6 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "pPe" = ( /obj/effect/turf_decal/loading_area, /obj/machinery/door/poddoor/shutters/preopen{ @@ -49463,21 +49591,6 @@ }, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) -"pPj" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/structure/sign/departments/science/alt/directional/north, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "pPw" = ( /obj/structure/table/wood, /obj/effect/turf_decal/siding/wood{ @@ -49506,6 +49619,21 @@ /obj/structure/flora/bush/sunny/style_random, /turf/open/floor/grass, /area/station/service/hydroponics/garden) +"pPM" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/command/heads_quarters/rd) "pPZ" = ( /obj/machinery/door/airlock/security/glass{ name = "Hazard Closet" @@ -49513,21 +49641,6 @@ /obj/effect/mapping_helpers/airlock/access/all/security/armory, /turf/open/floor/iron/dark, /area/station/maintenance/port/aft) -"pQd" = ( -/obj/structure/cable, -/obj/structure/table/wood, -/obj/machinery/computer/libraryconsole/bookmanagement{ - pixel_y = 5 - }, -/obj/effect/turf_decal/siding/wood{ - dir = 8 - }, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/machinery/power/apc/auto_name/directional/south, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) "pQi" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -49582,6 +49695,17 @@ /obj/structure/cable, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) +"pRc" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/iron, +/area/station/hallway/primary/aft) "pRf" = ( /obj/structure/table, /obj/item/folder/white, @@ -49640,6 +49764,18 @@ }, /turf/open/space/basic, /area/space/nearstation) +"pRL" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/disposal/bin/tagger, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "pRO" = ( /obj/machinery/door/poddoor/preopen{ id = "atmos"; @@ -49739,6 +49875,17 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/maintenance/solars/port/fore) +"pSV" = ( +/obj/machinery/medical_kiosk{ + pixel_y = 4 + }, +/obj/effect/turf_decal/box, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/medbay/lobby) "pSW" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -49749,14 +49896,17 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"pTc" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 +"pTd" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 }, -/obj/machinery/light/directional/south, -/obj/machinery/newscaster/directional/south, -/turf/open/floor/iron/kitchen_coldroom/freezerfloor, -/area/station/medical/coldroom) +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/execution/education) "pTC" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/landmark/event_spawn, @@ -49775,6 +49925,18 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"pTU" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple/opposingcorners, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "pUb" = ( /obj/structure/table/wood, /obj/effect/turf_decal/siding/wood/end{ @@ -49898,6 +50060,12 @@ }, /turf/open/floor/plating, /area/station/maintenance/fore) +"pWx" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/noticeboard/directional/east, +/obj/machinery/computer/gateway_control, +/turf/open/floor/iron/dark, +/area/station/command/gateway) "pWC" = ( /obj/effect/turf_decal/bot, /obj/machinery/conveyor{ @@ -49979,6 +50147,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/commons/locker) +"pXR" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/atmospherics/components/binary/pump/on/layer4{ + dir = 2; + name = "Airlock Pump"; + target_pressure = 300; + hide = 1 + }, +/turf/open/floor/plating, +/area/station/maintenance/aft) "pXU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -49994,17 +50173,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"pYb" = ( -/obj/structure/bodycontainer/morgue{ - dir = 8 - }, -/obj/effect/turf_decal/bot_white, -/obj/machinery/airalarm/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) "pYm" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -50027,6 +50195,15 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/command/heads_quarters/cmo) +"pZd" = ( +/obj/machinery/smartfridge/food, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id = "kitchenshutters"; + name = "Kitchen Shutters" + }, +/turf/open/floor/iron/showroomfloor, +/area/station/service/kitchen) "pZe" = ( /obj/effect/turf_decal/bot, /obj/structure/rack, @@ -50040,10 +50217,6 @@ /obj/item/food/grown/mushroom/glowshroom, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"pZj" = ( -/obj/effect/turf_decal/tile/red, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "pZx" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -50056,6 +50229,17 @@ /obj/structure/sign/departments/security, /turf/closed/wall, /area/station/hallway/secondary/exit/departure_lounge) +"pZF" = ( +/obj/structure/reagent_dispensers/watertank, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/atmos) "pZI" = ( /obj/machinery/modular_computer/preset/cargochat/security{ dir = 4 @@ -50085,16 +50269,6 @@ dir = 8 }, /area/station/hallway/primary/starboard) -"qaa" = ( -/obj/effect/mapping_helpers/broken_floor, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/obj/machinery/camera/autoname/directional/north, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "qai" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, @@ -50226,16 +50400,6 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"qbP" = ( -/obj/machinery/firealarm/directional/south, -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, -/obj/machinery/wall_healer/directional/south, -/turf/open/floor/iron/stairs/left{ - dir = 4 - }, -/area/station/service/chapel) "qbV" = ( /obj/structure/table, /obj/machinery/reagentgrinder{ @@ -50270,6 +50434,12 @@ }, /turf/open/floor/iron, /area/station/security/brig) +"qca" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/red, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "qci" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -50329,6 +50499,26 @@ /obj/machinery/door/firedoor, /turf/open/floor/engine/xenobio, /area/station/science/xenobiology) +"qei" = ( +/obj/structure/table/wood/fancy, +/obj/effect/turf_decal/siding/wideplating/dark/corner, +/obj/item/food/grown/flower/poppy{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy, +/obj/machinery/airalarm/directional/north, +/turf/open/floor/iron/dark, +/area/station/service/chapel) +"qel" = ( +/obj/machinery/atmospherics/components/tank/air, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/station/maintenance/department/chapel/monastery) "qew" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -50437,6 +50627,21 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) +"qfv" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/pharmacy) "qgu" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/maintenance, @@ -50534,11 +50739,6 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/showroomfloor, /area/station/security/execution/transfer) -"qhZ" = ( -/obj/machinery/light/directional/north, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "qic" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -50578,18 +50778,6 @@ }, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/starboard) -"qiL" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/siding/red{ - dir = 9 - }, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "qiM" = ( /obj/machinery/door/airlock/maintenance, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -50600,6 +50788,13 @@ /obj/effect/mapping_helpers/airlock/access/any/security/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/port/lesser) +"qiT" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "qjq" = ( /obj/structure/closet/secure_closet/security, /obj/effect/turf_decal/delivery, @@ -50686,24 +50881,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/construction/mining/aux_base) -"qkA" = ( -/obj/structure/grille/broken, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/sign/warning/electric_shock/directional/south, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) -"qld" = ( -/obj/effect/turf_decal/box, -/obj/machinery/light/directional/north, -/obj/structure/cable, -/obj/machinery/power/shieldwallgen/xenobiologyaccess, -/obj/effect/mapping_helpers/burnt_floor, -/obj/structure/sign/warning/gas_mask/directional/north, -/turf/open/floor/plating, -/area/station/science/xenobiology) "qlg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -50727,19 +50904,6 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/command/vault) -"qlw" = ( -/obj/machinery/airalarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/green/half/contrasted, -/obj/machinery/light/cold/directional/south, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "qly" = ( /obj/structure/frame/computer{ anchored = 1; @@ -50756,11 +50920,15 @@ "qlC" = ( /turf/closed/wall/rust, /area/station/maintenance/fore) -"qlD" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/maintenance/aft) +"qlI" = ( +/obj/machinery/atmospherics/components/unary/thermomachine/freezer{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "qlX" = ( /obj/effect/decal/cleanable/dirt, /obj/item/tank/internals/plasmaman/belt/full, @@ -50798,6 +50966,13 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/security/courtroom) +"qmM" = ( +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "qmR" = ( /obj/machinery/atmospherics/pipe/layer_manifold/scrubbers/visible, /turf/open/floor/engine, @@ -50844,6 +51019,16 @@ }, /turf/open/floor/iron/dark, /area/station/service/janitor) +"qnA" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/machinery/light/dim/directional/south, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "qnH" = ( /obj/structure/window/reinforced/plasma/spawner/directional/east, /obj/structure/cable, @@ -50864,33 +51049,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, /turf/open/floor/iron/dark, /area/station/maintenance/department/crew_quarters/bar) -"qnL" = ( -/obj/machinery/vending/wardrobe/law_wardrobe, -/obj/structure/extinguisher_cabinet/directional/east, -/obj/effect/turf_decal/delivery, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron/dark, -/area/station/service/lawoffice) -"qoi" = ( -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/components/binary/pump{ - dir = 4; - name = "Plasma to Incinerator" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "qoo" = ( /obj/machinery/airalarm/directional/west, /obj/machinery/camera/directional/west{ @@ -50922,6 +51080,24 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) +"qoN" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/structure/sign/nanotrasen{ + pixel_y = 32 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/light/directional/north, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/central/fore) "qoT" = ( /obj/machinery/light/small/directional/east, /obj/effect/turf_decal/tile/blue{ @@ -50977,15 +51153,6 @@ /obj/machinery/light/floor, /turf/open/floor/engine/n2o, /area/station/engineering/atmos) -"qpJ" = ( -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/purple, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "qpP" = ( /obj/structure/chair/office/light, /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ @@ -51016,34 +51183,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard) -"qqB" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/machinery/defibrillator_mount/directional/west, -/obj/machinery/stasis{ - dir = 4 - }, -/obj/effect/turf_decal/box, -/obj/machinery/vitals_reader/directional/north, -/turf/open/floor/iron/dark, -/area/station/medical/treatment_center) -"qqJ" = ( -/obj/structure/disposalpipe/trunk{ - dir = 4 - }, -/obj/machinery/disposal/bin/tagger, -/obj/machinery/light/directional/north, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red/opposingcorners{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "qqM" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -51054,6 +51193,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/engine, /area/station/engineering/gravity_generator) +"qqS" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "qqV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/siding/purple{ @@ -51074,6 +51218,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"qrj" = ( +/obj/machinery/status_display/ai/directional/east, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/cable, +/turf/open/floor/engine, +/area/station/ai/satellite/chamber) "qrk" = ( /obj/machinery/power/smes, /obj/effect/turf_decal/bot, @@ -51099,6 +51254,14 @@ }, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"qrK" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/security/processing) "qrP" = ( /obj/effect/turf_decal/bot, /obj/structure/closet/secure_closet/personal, @@ -51190,22 +51353,27 @@ }, /turf/open/floor/iron/dark, /area/station/security/checkpoint/customs) -"qug" = ( -/obj/structure/table, -/obj/machinery/cell_charger, -/obj/item/stock_parts/power_store/cell/high, -/obj/item/screwdriver{ - pixel_y = 18 - }, -/obj/item/radio/intercom/directional/south, -/obj/machinery/firealarm/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 +"qtO" = ( +/obj/machinery/vending/wardrobe/engi_wardrobe, +/obj/effect/turf_decal/bot, +/obj/machinery/camera/directional/north{ + c_tag = "Engineering Lockers"; + name = "engineering camera"; + network = list("ss13","engine") }, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/warm/directional/north, /turf/open/floor/iron/dark, -/area/station/commons/fitness/recreation) +/area/station/engineering/storage_shared) +"qtS" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/security/brig) "qur" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -51293,6 +51461,16 @@ /obj/structure/flora/rock/style_random, /turf/open/misc/asteroid/lowpressure, /area/space/nearstation) +"qvs" = ( +/obj/effect/turf_decal/stripes/white/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "qvG" = ( /obj/effect/turf_decal/stripes/corner, /obj/structure/cable, @@ -51360,20 +51538,6 @@ }, /turf/open/floor/carpet/green, /area/station/service/lawoffice) -"qwi" = ( -/obj/machinery/airalarm/directional/east, -/obj/machinery/camera/directional/east{ - c_tag = "Transferring Centre" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/processing) "qwx" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -51506,6 +51670,24 @@ /obj/structure/lattice/catwalk, /turf/open/space/basic, /area/station/solars/starboard/fore) +"qzq" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/public/glass{ + name = "Chapel Hallway" + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/fore) +"qzs" = ( +/obj/effect/turf_decal/bot, +/obj/structure/closet/wardrobe/mixed, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/commons/locker) "qzu" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -51592,13 +51774,16 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"qAx" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) +"qAr" = ( +/obj/machinery/computer/records/medical{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/cmo) "qAK" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -51622,16 +51807,12 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"qBs" = ( -/turf/closed/wall/rust, -/area/station/commons/vacant_room/commissary) -"qBz" = ( -/obj/machinery/light/small/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/burnt_floor, -/obj/structure/sign/poster/contraband/random/directional/west, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) +"qBB" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/grass, +/area/station/service/chapel) "qBC" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/effect/landmark/start/cook, @@ -51648,33 +51829,12 @@ }, /turf/open/floor/iron/dark, /area/station/security/office) -"qCi" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 10 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/warning/vacuum/external/directional/west, -/obj/machinery/light/small/directional/west, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/execution/education) "qCj" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/tile/neutral/half/contrasted, /obj/machinery/chem_dispenser, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) -"qCu" = ( -/obj/structure/flora/bush/large/style_random, -/obj/structure/window/spawner/directional/east, -/obj/structure/window/spawner/directional/north, -/obj/structure/window/spawner/directional/south, -/obj/structure/window/spawner/directional/west, -/turf/open/floor/grass, -/area/station/medical/medbay/central) "qCI" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ @@ -51722,6 +51882,13 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"qDC" = ( +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/lobby) "qDE" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line, @@ -51801,30 +51968,6 @@ dir = 1 }, /area/station/hallway/primary/fore) -"qED" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/machinery/atmospherics/components/binary/pump/on/layer4{ - dir = 4; - name = "Airlock Pump"; - target_pressure = 300; - hide = 1 - }, -/turf/open/floor/plating, -/area/station/maintenance/starboard/aft) -"qEJ" = ( -/obj/structure/table, -/obj/structure/bedsheetbin, -/obj/machinery/status_display/evac/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/commons/toilet/restrooms) "qEM" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -51868,11 +52011,18 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) -"qFs" = ( -/obj/structure/flora/bush/flowers_pp/style_random, -/obj/machinery/light/directional/east, -/turf/open/floor/grass, -/area/station/service/chapel) +"qFl" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "qFw" = ( /obj/machinery/door/airlock/research{ name = "Auxiliary Research" @@ -51940,15 +52090,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"qIv" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/structure/cable, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/starboard) "qJf" = ( /turf/open/floor/wood, /area/station/service/bar/atrium) @@ -52069,25 +52210,6 @@ }, /turf/open/floor/plating/plasma/rust, /area/station/maintenance/space_hut/plasmaman) -"qKI" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/general/visible, -/obj/machinery/space_heater, -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/south, -/obj/structure/sign/poster/contraband/fun_police/directional/south, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/execution/education) "qKP" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -52271,16 +52393,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/service/chapel/storage) -"qOi" = ( -/obj/structure/closet/secure_closet/engineering_personal, -/obj/item/clothing/suit/hooded/wintercoat/engineering, -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/north, -/obj/machinery/light_switch/directional/north, -/obj/item/pickaxe/mini, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/turf/open/floor/iron/dark, -/area/station/engineering/storage_shared) "qOq" = ( /obj/effect/turf_decal/loading_area, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/visible, @@ -52433,6 +52545,15 @@ }, /turf/open/floor/iron, /area/station/command/bridge) +"qQR" = ( +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/vending/wardrobe/hydro_wardrobe, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/green/fourcorners, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/checker, +/area/station/service/hydroponics) "qRf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -52649,6 +52770,24 @@ /obj/item/radio/intercom/directional/north, /turf/open/floor/iron, /area/station/cargo/warehouse) +"qTO" = ( +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/computer/security/telescreen/turbine/directional/west, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "qTT" = ( /obj/docking_port/stationary/random{ name = "lavaland"; @@ -52674,10 +52813,17 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, /area/station/hallway/secondary/entry) -"qUj" = ( -/obj/machinery/smartfridge/food, -/turf/open/floor/iron/showroomfloor, -/area/station/service/kitchen) +"qUm" = ( +/obj/structure/closet/secure_closet/bar, +/obj/item/flashlight/lantern, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/west, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/item/storage/photo_album/bar, +/turf/open/floor/iron/dark, +/area/station/service/bar/backroom) "qUp" = ( /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -52699,6 +52845,15 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"qUB" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/firealarm/directional/south, +/obj/item/kirbyplants/organic/plant21, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "qUJ" = ( /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, @@ -52727,16 +52882,6 @@ /obj/item/stack/cable_coil/cut, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"qVc" = ( -/obj/machinery/chem_master, -/obj/effect/turf_decal/bot, -/obj/machinery/light_switch/directional/south, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/science/xenobiology) "qVk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -52801,12 +52946,6 @@ dir = 1 }, /area/station/hallway/primary/fore) -"qVF" = ( -/obj/machinery/holopad, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "qVJ" = ( /obj/structure/cable, /obj/effect/landmark/start/hangover, @@ -52904,20 +53043,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"qWJ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted, -/obj/effect/turf_decal/tile/purple, -/obj/structure/sign/poster/contraband/lusty_xenomorph/directional/north, -/obj/machinery/light/small/directional/north, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/science) "qWN" = ( /obj/machinery/plate_press, /obj/machinery/light/small/directional/south, @@ -52930,16 +53055,6 @@ }, /turf/open/floor/engine/plasma, /area/station/engineering/atmos) -"qWZ" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/structure/table, -/obj/effect/spawner/random/armory/barrier_grenades, -/obj/item/key/security, -/obj/item/key/security, -/turf/open/floor/iron/dark, -/area/station/security/armory) "qXa" = ( /obj/structure/cable, /obj/effect/landmark/start/hangover, @@ -52947,19 +53062,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"qXc" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/light_switch/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/camera/motion/directional/south{ - c_tag = "Vault"; - name = "vault camera"; - network = list("vault") - }, -/turf/open/floor/engine, -/area/station/command/vault) "qXn" = ( /obj/machinery/door/airlock/maintenance{ name = "Hydroponics Maintenance" @@ -52974,6 +53076,15 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/command/bridge) +"qXw" = ( +/obj/machinery/status_display/evac/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/security/courtroom) "qXA" = ( /obj/machinery/portable_atmospherics/canister/nitrogen, /turf/open/floor/engine/n2, @@ -52993,6 +53104,12 @@ }, /turf/open/floor/iron/dark, /area/station/security/evidence) +"qYe" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/dock) "qYr" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -53003,12 +53120,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/aft) -"qYt" = ( -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "qYv" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -53076,6 +53187,15 @@ "qZx" = ( /turf/closed/wall/r_wall/rust, /area/station/tcommsat/server) +"qZC" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/obj/machinery/portable_atmospherics/canister, +/obj/effect/turf_decal/box, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron/dark, +/area/station/engineering/supermatter/room) "qZL" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -53108,16 +53228,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/atmos) -"raM" = ( -/obj/machinery/light/directional/west, -/obj/machinery/status_display/ai/directional/west, -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/cable/layer3, -/turf/open/floor/engine, -/area/station/ai/satellite/chamber) "raN" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -53131,20 +53241,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"raS" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/small/directional/east, +"raT" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/power/terminal{ + dir = 4 + }, /obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/machinery/computer/operating{ - dir = 1 +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ +/obj/effect/turf_decal/tile/red{ dir = 1 }, -/obj/machinery/vitals_reader/directional/south, -/turf/open/floor/iron/dark, -/area/station/medical/surgery/theatre) +/turf/open/floor/iron, +/area/station/engineering/supermatter/room) "raY" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -53167,14 +53279,6 @@ }, /turf/open/floor/engine/n2, /area/station/engineering/atmos) -"rbp" = ( -/obj/item/radio/intercom/directional/east, -/obj/machinery/computer/station_alert{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron, -/area/station/engineering/main) "rbz" = ( /obj/machinery/disposal/bin/tagger, /obj/effect/turf_decal/bot, @@ -53204,6 +53308,23 @@ "rbO" = ( /turf/open/floor/iron, /area/station/service/hydroponics) +"rbQ" = ( +/obj/structure/table/wood/fancy, +/obj/effect/turf_decal/siding/wideplating/dark/corner{ + dir = 4 + }, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/machinery/wall_healer/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "rbT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -53228,9 +53349,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/cargo/miningoffice) -"rcB" = ( -/turf/open/floor/grass, -/area/station/service/chapel) "rcI" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple/layer2{ dir = 5 @@ -53278,12 +53396,6 @@ dir = 8 }, /area/station/service/chapel) -"rcY" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/obj/effect/turf_decal/tile/brown/half/contrasted, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "rdb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -53345,12 +53457,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"rdI" = ( -/obj/machinery/computer/security/telescreen/monastery/directional/north, -/turf/open/floor/iron/stairs/left{ - dir = 8 - }, -/area/station/hallway/primary/fore) "rdM" = ( /obj/structure/sign/warning/secure_area, /turf/closed/wall, @@ -53368,13 +53474,6 @@ /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, /area/station/engineering/supermatter/room) -"rec" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron/cafeteria, -/area/station/security/prison/garden) "rel" = ( /obj/structure/falsewall{ name = "suspicious wall" @@ -53398,6 +53497,12 @@ /obj/machinery/portable_atmospherics/canister/nitrous_oxide, /turf/open/floor/iron/dark, /area/station/science/ordnance/storage) +"req" = ( +/obj/effect/turf_decal/bot, +/obj/effect/spawner/random/structure/crate_abandoned, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "ret" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on, /turf/open/floor/engine/vacuum, @@ -53415,13 +53520,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/science/auxlab) -"reT" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "reX" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -53470,6 +53568,14 @@ /obj/effect/mapping_helpers/airlock/access/all/security/general, /turf/open/floor/iron/dark, /area/station/security/checkpoint/supply) +"rfK" = ( +/obj/effect/mapping_helpers/broken_floor, +/obj/effect/turf_decal/bot, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 8 + }, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "rfM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -53489,6 +53595,15 @@ /obj/effect/turf_decal/tile/red, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"rgl" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden, +/obj/machinery/airlock_sensor/incinerator_atmos{ + pixel_x = 24 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/engine, +/area/station/maintenance/disposal/incinerator) "rgv" = ( /obj/structure/reagent_dispensers/watertank, /obj/effect/turf_decal/delivery, @@ -53499,11 +53614,15 @@ "rgF" = ( /turf/closed/wall/r_wall/rust, /area/station/hallway/primary/fore) -"rgY" = ( -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) +"rgW" = ( +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/public/glass{ + name = "Arrivals Dock" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/entry) "rha" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/disposalpipe/segment, @@ -53576,32 +53695,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/solars/starboard/fore) -"riB" = ( -/obj/machinery/computer/shuttle/labor{ - dir = 1 - }, -/obj/structure/sign/warning/vacuum/external/directional/south, -/obj/machinery/camera/directional/south{ - c_tag = "Transferring Centre Dock" - }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/processing) -"riC" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/structure/cable, -/obj/effect/turf_decal/siding/purple/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/purple{ - dir = 4 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/lobby) "riI" = ( /obj/structure/table, /obj/item/clipboard, @@ -53609,20 +53702,6 @@ /obj/item/pipe_dispenser, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"riN" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 4; - name = "Gas to Chamber" - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/xenobiology) "riQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -53660,13 +53739,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/carpet/royalblue, /area/station/service/chapel/office) -"riX" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/blood/splatter/oil, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/service/chapel/storage) "rja" = ( /obj/structure/closet/secure_closet/engineering_chief, /obj/effect/turf_decal/delivery, @@ -53685,6 +53757,13 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/security/medical) +"rjk" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/duct, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "rjR" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, @@ -53695,6 +53774,14 @@ /obj/effect/spawner/random/clothing/costume, /turf/open/floor/plating, /area/station/maintenance/starboard) +"rjT" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/structure/bookcase/random, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/station/service/library) "rjU" = ( /obj/effect/landmark/start/medical_doctor, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -53794,6 +53881,30 @@ /obj/machinery/airalarm/directional/south, /turf/open/floor/iron/dark, /area/station/security/interrogation) +"rms" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/firealarm/directional/north, +/turf/open/floor/iron, +/area/station/security/processing) +"rmt" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/status_display/ai/directional/west, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/effect/turf_decal/box, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/command/vault) "rmu" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -53831,12 +53942,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/office) -"rmU" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/light/directional/north, -/turf/open/floor/iron/dark, -/area/station/science/auxlab) "rna" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -53869,6 +53974,11 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"rnt" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/security/processing) "rnB" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -54019,18 +54129,6 @@ "rpl" = ( /turf/closed/wall, /area/station/maintenance/department/electrical) -"rpB" = ( -/obj/structure/urinal/directional/north, -/turf/open/floor/plating/rust, -/area/station/security/prison/shower) -"rpD" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/brown{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/cargo/office) "rpF" = ( /obj/effect/turf_decal/bot, /obj/machinery/holopad, @@ -54077,11 +54175,6 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"rqp" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/cargo/storage) "rqq" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -54116,18 +54209,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/security/interrogation) -"rqG" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/railing/corner{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "rqJ" = ( /obj/effect/turf_decal/box, /obj/structure/cable, @@ -54384,27 +54465,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/fore) -"rtT" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/vending/cigarette, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) -"ruc" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/box, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "ruo" = ( /obj/machinery/computer/records/security{ dir = 8 @@ -54420,22 +54480,6 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/aft) -"rup" = ( -/obj/structure/disposalpipe/trunk{ - dir = 4 - }, -/obj/machinery/disposal/bin/tagger, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/bot, -/obj/machinery/firealarm/directional/west, -/obj/machinery/status_display/supply{ - pixel_y = -32 - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/storage) "ruB" = ( /obj/effect/turf_decal/tile/red{ dir = 1 @@ -54526,20 +54570,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) -"rvU" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/turf_decal/siding/yellow{ - dir = 8 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/box, -/turf/open/floor/iron, -/area/station/engineering/lobby) "rvW" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -54634,6 +54664,22 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"rxb" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/siding/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lobby) "rxi" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -54741,6 +54787,13 @@ /obj/item/storage/toolbox/electrical, /turf/open/floor/plating, /area/station/maintenance/department/electrical) +"rya" = ( +/obj/effect/turf_decal/box/corners, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "ryn" = ( /obj/structure/cable, /turf/open/floor/plating, @@ -54815,11 +54868,6 @@ }, /turf/open/floor/iron, /area/station/command/bridge) -"rzJ" = ( -/obj/machinery/light/floor, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "rzR" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/corner{ @@ -54833,20 +54881,31 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) -"rAr" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/west, -/obj/machinery/firealarm/directional/west, -/obj/machinery/suit_storage_unit/standard_unit{ - desc = "An industrial suit storage device carrying retro space suits. Neat!"; - helmet_type = /obj/item/clothing/head/helmet/space; - suit_type = /obj/item/clothing/suit/space +"rzZ" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 }, +/obj/structure/table, +/obj/item/soap/nanotrasen, +/obj/item/hand_labeler, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 + dir = 8 + }, +/obj/machinery/camera/directional/south{ + c_tag = "Recovery Room"; + name = "medical camera"; + network = list("ss13","medical") }, /turf/open/floor/iron/dark, -/area/station/command/eva) +/area/station/medical/medbay/central) +"rAt" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 1 + }, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron, +/area/station/engineering/main) "rAA" = ( /obj/effect/decal/cleanable/ash, /obj/effect/mapping_helpers/burnt_floor, @@ -54891,30 +54950,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/catwalk_floor/iron_dark, /area/station/service/chapel/dock) -"rBd" = ( -/obj/effect/turf_decal/bot, -/obj/structure/rack, -/obj/item/storage/toolbox/emergency, -/obj/item/stack/rods/ten, -/obj/machinery/light/directional/south, -/obj/machinery/camera/autoname/directional/south, -/turf/open/floor/iron/checker, -/area/station/security/mechbay) -"rBj" = ( -/obj/structure/transit_tube/curved/flipped, -/turf/open/space/basic, -/area/space/nearstation) -"rBq" = ( -/obj/machinery/conveyor{ - dir = 8; - id = "mining" - }, -/obj/machinery/light/directional/north, -/obj/machinery/bouldertech/refinery{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/miningoffice) "rBE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/door/airlock/maintenance{ @@ -54948,14 +54983,6 @@ /obj/item/radio/intercom/directional/west, /turf/open/floor/iron/showroomfloor, /area/station/medical/paramedic) -"rBV" = ( -/obj/structure/table/reinforced/plastitaniumglass, -/obj/item/mmi, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/storage) "rCi" = ( /turf/closed/wall, /area/station/hallway/secondary/entry) @@ -55022,15 +55049,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/engine, /area/station/engineering/gravity_generator) -"rDj" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/red, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "rDl" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -55171,16 +55189,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/security/lockers) -"rFL" = ( -/obj/structure/table, -/obj/item/storage/toolbox/mechanical, -/obj/item/clothing/head/utility/welding, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) "rFN" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/light/small/directional/west, @@ -55203,6 +55211,20 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar/atrium) +"rGr" = ( +/obj/structure/table/wood, +/obj/machinery/reagentgrinder{ + pixel_y = 5 + }, +/obj/item/toy/figure/bartender{ + pixel_x = -7 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/dark, +/area/station/service/bar/backroom) "rGx" = ( /obj/structure/flora/bush/jungle/c/style_2, /turf/open/misc/asteroid, @@ -55255,14 +55277,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/science) -"rHs" = ( -/obj/structure/table, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/item/clothing/under/rank/security/officer, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "rHz" = ( /obj/structure/cable, /obj/effect/landmark/start/hangover, @@ -55299,20 +55313,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"rIq" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/gulag_item_reclaimer{ - dir = 8; - pixel_x = 30 +"rIt" = ( +/obj/structure/sign/painting/library{ + pixel_x = 32 }, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 +/obj/effect/turf_decal/siding/wood{ + dir = 1 }, -/obj/effect/turf_decal/tile/red/anticorner/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/processing) +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/library) "rIy" = ( /obj/structure/mop_bucket/janitorialcart, /obj/effect/turf_decal/delivery, @@ -55322,31 +55333,15 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/service/janitor) -"rJw" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/yellow, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) -"rJJ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/light/directional/east, -/obj/machinery/status_display/evac/directional/east, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 +"rJt" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/dark/corner{ - dir = 4 + dir = 1 }, -/area/station/hallway/primary/port) -"rJN" = ( -/obj/structure/flora/bush/sparsegrass/style_random, -/obj/structure/flora/bush/sparsegrass/style_random, -/turf/open/floor/grass, -/area/station/service/chapel) +/area/station/hallway/primary/fore) "rJO" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -55472,6 +55467,18 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"rKZ" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/blood/gibs/up, +/obj/structure/sign/poster/official/wtf_is_co2/directional/north, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/broken_floor, +/turf/open/floor/plating, +/area/station/security/execution/education) "rLh" = ( /obj/machinery/airalarm/directional/south, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -55585,16 +55592,17 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) -"rMJ" = ( -/obj/structure/sign/departments/cargo/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ +"rNc" = ( +/obj/effect/turf_decal/siding/red{ dir = 1 }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/turf/open/floor/iron/dark/corner{ +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/red/half/contrasted{ dir = 1 }, -/area/station/hallway/primary/fore) +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "rNe" = ( /obj/structure/chair/sofa/right/brown{ dir = 1 @@ -55826,6 +55834,20 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"rPR" = ( +/obj/structure/chair/sofa/left/maroon{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/railing{ + dir = 8 + }, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/bar/atrium) "rPU" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/sign/poster/contraband/grey_tide/directional/north, @@ -55899,6 +55921,19 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"rQO" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/item/radio/intercom/directional/north, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/structure/table, +/obj/item/storage/medkit/emergency, +/turf/open/floor/iron, +/area/station/commons/fitness/recreation) "rQQ" = ( /obj/machinery/vending/hydronutrients, /obj/effect/turf_decal/delivery, @@ -56033,19 +56068,6 @@ "rRY" = ( /turf/closed/wall/r_wall, /area/station/security/interrogation) -"rRZ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/office) "rSe" = ( /obj/effect/turf_decal/bot, /obj/machinery/power/emitter, @@ -56084,6 +56106,14 @@ "rTk" = ( /turf/closed/wall, /area/station/service/chapel/office) +"rTm" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/engineering/hallway) "rTp" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -56135,6 +56165,17 @@ }, /turf/open/floor/engine, /area/station/ai/satellite/foyer) +"rTL" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/structure/table, +/obj/effect/spawner/random/armory/barrier_grenades, +/obj/item/key/security, +/obj/item/key/security, +/obj/machinery/light/small/red/directional/south, +/turf/open/floor/iron/dark, +/area/station/security/armory) "rTS" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -56204,23 +56245,28 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"rUZ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/siding/red{ + dir = 9 + }, +/obj/effect/landmark/start/depsec/supply, +/obj/effect/turf_decal/tile/red/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/security/checkpoint/supply) "rVj" = ( /obj/effect/turf_decal/tile/purple{ dir = 8 }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"rVp" = ( -/obj/machinery/computer/crew{ - dir = 1 - }, -/obj/effect/turf_decal/bot, -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/checkpoint/medical) "rVt" = ( /obj/structure/frame/computer{ anchored = 1; @@ -56229,6 +56275,12 @@ /obj/item/stack/cable_coil/cut, /turf/open/floor/wood, /area/ruin/space/has_grav/abandoned_warehouse) +"rVu" = ( +/obj/structure/bookcase/random/fiction, +/obj/item/radio/intercom/directional/north, +/obj/machinery/light/warm/dim/directional/north, +/turf/open/floor/wood, +/area/station/service/library) "rVw" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -56238,19 +56290,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) -"rVA" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark/corner{ - dir = 1 - }, -/area/station/hallway/primary/port) "rVE" = ( /obj/item/kirbyplants/organic/plant10, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -56309,16 +56348,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"rYa" = ( -/obj/effect/turf_decal/tile/brown{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "rYc" = ( /obj/structure/sign/nanotrasen, /turf/closed/wall, @@ -56356,6 +56385,13 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"rYA" = ( +/obj/structure/chair/sofa/bench{ + dir = 4 + }, +/obj/machinery/newscaster/directional/west, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "rYK" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/tile/neutral/opposingcorners, @@ -56413,14 +56449,6 @@ "rZN" = ( /turf/closed/wall/r_wall/rust, /area/station/ai/satellite/maintenance/storage) -"rZO" = ( -/obj/effect/turf_decal/box/corners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "rZU" = ( /obj/effect/spawner/random/structure/girder, /turf/open/floor/plating, @@ -56444,6 +56472,13 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/space/nearstation) +"say" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "saz" = ( /obj/structure/flora/grass/jungle/b/style_random, /obj/structure/window/reinforced/spawner/directional/south, @@ -56526,17 +56561,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"sbJ" = ( -/obj/machinery/light/directional/west, -/obj/machinery/status_display/evac/directional/west, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "sbX" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -56622,30 +56646,6 @@ dir = 4 }, /area/station/hallway/primary/central/fore) -"scR" = ( -/obj/structure/table, -/obj/structure/extinguisher_cabinet/directional/east, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/item/clothing/glasses/hud/security/sunglasses/gars{ - pixel_x = 3; - pixel_y = -2 - }, -/obj/item/clothing/glasses/hud/security/sunglasses/gars{ - pixel_x = 3; - pixel_y = 2 - }, -/obj/item/clothing/glasses/hud/security/sunglasses{ - pixel_x = -3; - pixel_y = 2 - }, -/obj/item/clothing/glasses/hud/security/sunglasses{ - pixel_x = -3; - pixel_y = -2 - }, -/turf/open/floor/iron/dark, -/area/station/security/armory) "scT" = ( /obj/structure/rack, /obj/item/wrench, @@ -56753,24 +56753,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"seK" = ( -/obj/structure/table/reinforced/plastitaniumglass, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/cell_charger, -/obj/item/stock_parts/power_store/cell, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/storage) -"seN" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/start/hangover, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "seO" = ( /obj/machinery/door/firedoor, /obj/effect/decal/cleanable/dirt, @@ -56911,6 +56893,15 @@ /obj/effect/mapping_helpers/airlock/access/all/security/armory, /turf/open/floor/iron/dark, /area/station/security/office) +"sgK" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/turf/open/floor/iron, +/area/station/engineering/lobby) "shk" = ( /obj/machinery/telecomms/bus/preset_four, /turf/open/floor/circuit/green/telecomms/mainframe, @@ -56972,15 +56963,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) -"shW" = ( -/obj/machinery/airalarm/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/drone_bay) "shZ" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -57008,6 +56990,15 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/department/medical/central) +"sip" = ( +/obj/effect/spawner/random/vending/colavend, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/port) "siF" = ( /obj/effect/decal/cleanable/dirt, /obj/item/kirbyplants/organic/plant16, @@ -57042,20 +57033,6 @@ /obj/item/toy/plush/ratplush, /turf/open/floor/bronze/filled, /area/station/maintenance/department/chapel) -"siS" = ( -/obj/machinery/door/airlock/external{ - name = "Prison External Airlock" - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "kilo-maint-1" - }, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 8 - }, -/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "sjd" = ( /obj/machinery/door/airlock/maintenance{ name = "Mining Dock Maintenance" @@ -57074,10 +57051,42 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"sjp" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "sjG" = ( /obj/effect/spawner/structure/window/reinforced/tinted, /turf/open/floor/plating, /area/station/service/chapel) +"sjK" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron/showroomfloor, +/area/station/science) +"sjS" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/service/chapel) "sjV" = ( /obj/machinery/door/airlock/maintenance, /obj/effect/mapping_helpers/airlock/abandoned, @@ -57181,6 +57190,19 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/atmos) +"slz" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/service/bar/atrium) "slB" = ( /obj/structure/chair/pew/left{ dir = 8 @@ -57194,12 +57216,21 @@ dir = 1 }, /area/station/service/chapel) -"slC" = ( -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 8 +"slM" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/newscaster/directional/west, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron, +/area/station/command/heads_quarters/hop) +"slX" = ( +/obj/effect/turf_decal/box/corners{ + dir = 4 }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, -/area/station/hallway/secondary/entry) +/area/station/hallway/secondary/exit/departure_lounge) "smb" = ( /obj/effect/turf_decal/bot, /obj/structure/rack, @@ -57233,18 +57264,6 @@ /obj/machinery/atmos_shield_gen/active, /turf/open/floor/plating, /area/station/maintenance/starboard) -"smq" = ( -/obj/machinery/light/small/directional/west, -/obj/structure/disposalpipe/segment, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/bridge) "smC" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -57300,15 +57319,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/cargo/office) -"snL" = ( -/obj/machinery/firealarm/directional/east, -/obj/effect/decal/cleanable/cobweb/cobweb2, -/obj/effect/turf_decal/delivery, -/obj/machinery/byteforge, -/obj/machinery/camera/autoname/directional/north, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/cargo/bitrunning/den) "snU" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -57337,35 +57347,20 @@ }, /turf/open/floor/carpet/green, /area/station/service/lawoffice) -"son" = ( -/obj/effect/landmark/event_spawn, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) -"sow" = ( -/obj/item/kirbyplants/organic/plant2{ - pixel_y = 3 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/machinery/firealarm/directional/north, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 +"sog" = ( +/obj/structure/table, +/obj/item/stack/sheet/iron/ten, +/obj/item/stack/cable_coil, +/obj/item/storage/backpack/duffelbag/sec/surgery{ + pixel_y = 5 }, +/obj/item/wirecutters, +/obj/machinery/firealarm/directional/east, /obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 }, -/turf/open/floor/iron, -/area/station/security/processing) +/turf/open/floor/iron/dark, +/area/station/security/execution/education) "soB" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/red/opposingcorners, @@ -57391,11 +57386,6 @@ /obj/machinery/firealarm/directional/east, /turf/open/floor/iron/showroomfloor, /area/station/science) -"soL" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/turf/open/floor/plating, -/area/station/maintenance/central) "soZ" = ( /obj/structure/table/reinforced, /obj/item/reagent_containers/condiment/saltshaker{ @@ -57457,22 +57447,20 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/command/bridge) -"spV" = ( -/obj/structure/table, -/obj/machinery/newscaster/directional/east, -/obj/effect/turf_decal/siding/green{ +"spU" = ( +/obj/machinery/modular_computer/preset/cargochat/engineering{ dir = 8 }, -/obj/item/storage/bag/plants/portaseeder, -/obj/machinery/light/small/directional/east, -/obj/item/crowbar/red, -/obj/item/plant_analyzer, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 +/obj/machinery/light_switch/directional/east, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 4 }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/engineering/main) "spX" = ( /obj/structure/cable, /obj/machinery/space_heater, @@ -57576,16 +57564,6 @@ /obj/effect/mapping_helpers/airlock/access/all/security/entrance, /turf/open/floor/iron/dark, /area/station/security/brig) -"srg" = ( -/obj/structure/sign/warning/biohazard/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 4 - }, -/obj/machinery/light/cold/directional/north, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "srs" = ( /obj/machinery/firealarm/directional/north, /turf/open/floor/iron/dark, @@ -57614,6 +57592,12 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"srB" = ( +/obj/item/kirbyplants/organic/plant21, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/turf/open/floor/iron/dark/corner, +/area/station/hallway/primary/central/fore) "srR" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -57648,11 +57632,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/engineering/atmos) -"ssg" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "ssi" = ( /obj/effect/turf_decal/bot, /obj/machinery/computer/security{ @@ -57695,27 +57674,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) -"ssw" = ( -/obj/structure/table, -/obj/item/hand_labeler, -/obj/item/crowbar/red, -/obj/item/gps/mining, -/obj/effect/decal/cleanable/cobweb, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light_switch/directional/north, -/obj/machinery/light/small/directional/north, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/cargo/miningdock) "ssx" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -57797,6 +57755,10 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"stU" = ( +/obj/machinery/telecomms/server/presets/common, +/turf/open/floor/circuit/green/telecomms/mainframe, +/area/station/tcommsat/server) "suj" = ( /obj/machinery/door/airlock/external{ name = "External Freight Airlock" @@ -57812,27 +57774,11 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/ruin/space/has_grav/abandoned_warehouse) -"suo" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) "suA" = ( /obj/effect/turf_decal/delivery, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/cargo/storage) -"suF" = ( -/obj/machinery/light/directional/north, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/turf/open/floor/engine, -/area/station/tcommsat/computer) "suQ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -57859,14 +57805,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/engineering/atmos) -"svC" = ( -/obj/machinery/light/small/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/blood/old, -/obj/effect/mapping_helpers/burnt_floor, -/obj/structure/sign/poster/contraband/random/directional/west, -/turf/open/floor/plating, -/area/station/maintenance/port/lesser) "svW" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, @@ -57892,25 +57830,30 @@ /obj/structure/girder, /turf/open/floor/plating, /area/station/maintenance/port/lesser) -"swA" = ( -/obj/item/reagent_containers/cup/glass/flask/gold{ - pixel_x = 3; - pixel_y = 8 +"swC" = ( +/obj/structure/table, +/obj/item/clipboard, +/obj/item/disk/computer/ordnance, +/obj/item/disk/computer/ordnance, +/obj/item/disk/computer/ordnance, +/obj/item/circuitboard/aicore{ + pixel_y = 5 }, -/obj/item/reagent_containers/cup/glass/bottle/rum{ - pixel_x = -4; - pixel_y = 4 +/obj/item/hand_labeler, +/obj/machinery/airalarm/directional/west, +/obj/item/pai_card{ + pixel_x = 6 }, -/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{ - pixel_x = 7; - pixel_y = 4 +/obj/item/aicard, +/obj/item/taperecorder{ + pixel_x = -6; + pixel_y = 6 }, -/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass, -/obj/structure/table/wood, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/cable, -/turf/open/floor/carpet/royalblack, -/area/station/command/heads_quarters/captain) +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/rd) "swR" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -57986,30 +57929,41 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/command/heads_quarters/ce) -"sxM" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/light/directional/south, +"syl" = ( /obj/structure/cable, -/obj/effect/turf_decal/siding/blue/corner{ - dir = 8 +/obj/effect/turf_decal/tile/brown{ + dir = 1 }, -/obj/machinery/power/apc/auto_name/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/storage) +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "syu" = ( /obj/structure/sign/warning/deathsposal, /turf/closed/wall, /area/station/science/xenobiology) +"syA" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) "syF" = ( /obj/structure/girder, /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"syU" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "syZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -58044,19 +57998,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/security/prison) -"szL" = ( -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted, -/obj/machinery/disposal/bin/tagger, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) "szX" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -58145,6 +58086,27 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/command/gateway) +"sBb" = ( +/obj/structure/table/wood, +/obj/item/storage/fancy/cigarettes/cigars/havana{ + pixel_y = 5 + }, +/obj/item/lighter, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/captain) +"sBd" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel/dock) "sBl" = ( /obj/effect/turf_decal/bot, /obj/effect/turf_decal/stripes/corner{ @@ -58220,6 +58182,25 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/science/auxlab) +"sCx" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/washing_machine, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/machinery/light_switch/directional/east, +/obj/effect/turf_decal/tile/neutral/opposingcorners{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/commons/toilet/restrooms) +"sCy" = ( +/obj/effect/landmark/event_spawn, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "sCC" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -58404,21 +58385,6 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance) -"sFu" = ( -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/table, -/obj/item/wrench/medical, -/obj/item/storage/pill_bottle/mannitol{ - pixel_x = 8; - pixel_y = 7 - }, -/obj/item/reagent_containers/cup/beaker/cryoxadone, -/obj/item/reagent_containers/cup/beaker/cryoxadone, -/obj/item/reagent_containers/cup/beaker/cryoxadone, -/obj/item/reagent_containers/cup/beaker/cryoxadone, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/cryo) "sFv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -58491,40 +58457,6 @@ /mob/living/basic/pet/fox/renault, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) -"sHh" = ( -/obj/structure/table, -/obj/machinery/camera/directional/west{ - c_tag = "ai_upload"; - name = "upload camera"; - network = list("aiupload") - }, -/obj/item/radio/intercom/directional/west, -/obj/machinery/light/cold/directional/west, -/obj/effect/spawner/round_default_module, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) -"sHk" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/lobby) "sHm" = ( /obj/machinery/barsign, /turf/closed/wall, @@ -58549,6 +58481,20 @@ }, /turf/open/floor/iron/dark, /area/station/medical/medbay/central) +"sHo" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/computer/security/labor, +/obj/machinery/requests_console/directional/north{ + department = "Security"; + name = "Security Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/supplies, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/cobweb, +/obj/machinery/airalarm/directional/west, +/turf/open/floor/iron/dark, +/area/station/security/checkpoint/supply) "sHC" = ( /obj/machinery/door/airlock/public/glass{ name = "Docking Hallway" @@ -58642,12 +58588,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"sIr" = ( -/obj/machinery/light/directional/south, -/obj/item/kirbyplants/organic/plant5, -/obj/effect/landmark/start/hangover, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "sIx" = ( /obj/effect/turf_decal/loading_area{ dir = 4; @@ -58664,17 +58604,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) -"sIF" = ( -/obj/machinery/vending/cola/red, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/security/prison/mess) "sIH" = ( /obj/machinery/light_switch/directional/north, /obj/machinery/bci_implanter, @@ -58694,32 +58623,6 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"sJd" = ( -/obj/item/storage/fancy/cigarettes/cigars{ - pixel_x = 2; - pixel_y = 6 - }, -/obj/item/storage/fancy/cigarettes/cigars/havana{ - pixel_x = 2; - pixel_y = 8 - }, -/obj/item/storage/fancy/cigarettes/cigars/cohiba{ - pixel_x = 2; - pixel_y = 10 - }, -/obj/item/holosign_creator/robot_seat/bar, -/obj/machinery/status_display/evac/directional/west, -/obj/structure/table, -/obj/machinery/light/directional/west, -/obj/machinery/camera/directional/west{ - c_tag = "Bar"; - name = "bar camera" - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/bar) "sJD" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -58922,13 +58825,6 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/service/janitor) -"sLU" = ( -/obj/machinery/light/small/directional/north, -/obj/structure/sign/warning/electric_shock/directional/north, -/obj/structure/cable, -/obj/machinery/power/smes/full, -/turf/open/floor/circuit/red/telecomms, -/area/station/tcommsat/server) "sLW" = ( /obj/machinery/atmospherics/pipe/smart/manifold/supply/visible{ dir = 4 @@ -58986,6 +58882,16 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"sMk" = ( +/obj/effect/spawner/random/vending/snackvend, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/central/fore) "sMu" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -59027,6 +58933,17 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"sNc" = ( +/obj/structure/bodycontainer/morgue{ + dir = 8 + }, +/obj/effect/turf_decal/bot_white, +/obj/structure/sign/poster/contraband/random/directional/east, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "sNe" = ( /obj/structure/filingcabinet/chestdrawer, /obj/machinery/firealarm/directional/west, @@ -59047,20 +58964,16 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"sNR" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/corner, -/obj/structure/disposalpipe/segment, -/obj/structure/cable, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 +"sND" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ + dir = 4 }, -/obj/effect/landmark/firealarm_sanity, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/duct, /turf/open/floor/iron/dark, -/area/station/maintenance/port/greater) +/area/station/engineering/atmos) "sOi" = ( /obj/effect/turf_decal/bot, /obj/machinery/status_display/door_timer{ @@ -59116,6 +59029,12 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"sOJ" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/central) "sOS" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/effect/turf_decal/siding/green, @@ -59151,10 +59070,21 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"sPx" = ( -/obj/machinery/light/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/chapel) +"sPg" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/blood/old, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/machinery/camera/autoname/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "sPG" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -59183,24 +59113,16 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/hallway/primary/aft) -"sPV" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/conveyor{ - dir = 1; - id = "QMLoad"; - name = "off ramp" - }, -/obj/machinery/light/directional/east, -/obj/machinery/camera/directional/east{ - c_tag = "Cargo Ramps"; - name = "cargo camera"; - network = list("ss13","qm") - }, +"sQd" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 + dir = 4 }, -/turf/open/floor/iron/dark, -/area/station/cargo/storage) +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark/corner{ + dir = 4 + }, +/area/station/hallway/primary/fore) "sQj" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/blue{ @@ -59223,12 +59145,6 @@ dir = 4 }, /area/station/service/chapel) -"sQt" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "sQw" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/preopen{ @@ -59341,14 +59257,20 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/science/ordnance) -"sRL" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron/dark/corner{ - dir = 1 +"sRO" = ( +/obj/structure/sign/warning/cold_temp/directional/north, +/obj/structure/table, +/obj/item/storage/pill_bottle/mannitol{ + pixel_x = 8; + pixel_y = 7 }, -/area/station/hallway/primary/fore) +/obj/item/wrench/medical, +/obj/item/reagent_containers/cup/beaker/cryoxadone, +/obj/item/reagent_containers/cup/beaker/cryoxadone, +/obj/item/reagent_containers/cup/beaker/cryoxadone, +/obj/item/reagent_containers/cup/beaker/cryoxadone, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/cryo) "sRZ" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -59371,18 +59293,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/security/interrogation) -"sSg" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/directional/south, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/hallway/primary/aft) "sSh" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -59509,6 +59419,17 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/office) +"sUj" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/structure/cable, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/office) "sUq" = ( /obj/structure/disposalpipe/segment{ dir = 5 @@ -59585,13 +59506,6 @@ dir = 1 }, /area/station/service/chapel/monastery) -"sVI" = ( -/obj/machinery/light/directional/east, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/structure/cable, -/obj/machinery/portable_atmospherics/canister/carbon_dioxide, -/turf/open/floor/iron/dark, -/area/station/science/ordnance/storage) "sVN" = ( /obj/structure/table, /obj/item/paper_bin{ @@ -59734,6 +59648,12 @@ /obj/effect/mapping_helpers/airlock/access/all/security/general, /turf/open/floor/iron/dark, /area/station/security/office) +"sXo" = ( +/obj/structure/chair/comfy/brown{ + dir = 4 + }, +/turf/open/floor/carpet/royalblack, +/area/station/command/heads_quarters/captain) "sXB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -59837,6 +59757,20 @@ /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"sYx" = ( +/obj/machinery/airalarm/directional/north, +/obj/machinery/camera/directional/north{ + c_tag = "Engineering Desk"; + name = "engineering camera"; + network = list("ss13","engine") + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/rnd/production/protolathe/department/engineering, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "sYM" = ( /obj/machinery/light/small/directional/south, /obj/effect/turf_decal/stripes/line{ @@ -59903,6 +59837,30 @@ }, /turf/open/floor/iron/dark, /area/station/commons/storage/primary) +"sZC" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/item/radio/intercom/directional/south, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/wood, +/area/station/service/bar/atrium) +"sZK" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/mapping_helpers/burnt_floor, +/obj/machinery/atmospherics/components/binary/pump/on/layer4{ + dir = 2; + name = "Airlock Pump"; + target_pressure = 300; + hide = 1 + }, +/obj/effect/spawner/random/structure/tank_holder, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "sZL" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -59912,10 +59870,29 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"sZR" = ( +/obj/structure/table, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/glass/fifty, +/obj/machinery/airalarm/directional/east, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/maintenance/storage) "sZV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"sZX" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/start/chaplain, +/obj/machinery/light/floor, +/turf/open/floor/carpet/red, +/area/station/service/chapel) "tav" = ( /obj/structure/cable, /obj/effect/turf_decal/bot, @@ -60004,6 +59981,16 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/iron/showroomfloor, /area/station/science/genetics) +"tbz" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 6 + }, +/obj/structure/sign/poster/official/enlist/directional/east, +/obj/machinery/light/small/directional/south, +/turf/open/floor/plating, +/area/station/hallway/secondary/exit/departure_lounge) "tbE" = ( /obj/machinery/door/airlock/external{ name = "Arrival Shuttle Airlock" @@ -60142,6 +60129,18 @@ }, /turf/open/floor/carpet/royalblue, /area/station/command/heads_quarters/captain) +"teg" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/hidden{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/bridge_pipe/dark/visible, +/turf/open/floor/iron/dark, +/area/station/maintenance/disposal/incinerator) "tej" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, @@ -60173,23 +60172,6 @@ }, /turf/open/floor/plating, /area/station/cargo/storage) -"teE" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/firealarm/directional/west, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/spawner/xmastree/rdrod, -/obj/effect/turf_decal/tile/purple{ - dir = 1 - }, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/lobby) "teH" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -60199,26 +60181,6 @@ /obj/item/radio/intercom/directional/east, /turf/open/floor/iron/cafeteria, /area/station/service/kitchen) -"teL" = ( -/obj/structure/table/wood, -/obj/item/storage/fancy/candle_box, -/obj/item/storage/fancy/candle_box, -/obj/item/storage/fancy/candle_box{ - pixel_x = 6; - pixel_y = 5 - }, -/obj/item/storage/fancy/candle_box{ - pixel_x = -5; - pixel_y = 5 - }, -/obj/machinery/light/directional/north, -/obj/item/storage/box/matches{ - pixel_x = -3; - pixel_y = 2 - }, -/obj/machinery/computer/security/telescreen/monastery/directional/north, -/turf/open/floor/iron/grimy, -/area/station/service/chapel/office) "teU" = ( /obj/structure/sign/warning/secure_area, /turf/closed/wall/r_wall/rust, @@ -60270,6 +60232,15 @@ "tfI" = ( /turf/closed/wall/r_wall/rust, /area/station/service/chapel/office) +"tfS" = ( +/obj/structure/table, +/obj/machinery/recharger, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/dim/directional/west, +/turf/open/floor/iron/dark, +/area/station/security/brig) "tfV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -60316,18 +60287,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/security/checkpoint/engineering) -"tgh" = ( -/obj/structure/closet/crate/trashcart/laundry, -/obj/effect/spawner/random/contraband/prison, -/obj/item/clothing/under/rank/prisoner, -/obj/item/clothing/under/rank/prisoner, -/obj/item/clothing/under/rank/prisoner/skirt, -/obj/item/clothing/under/rank/prisoner/skirt, -/obj/effect/turf_decal/tile/blue/opposingcorners{ - dir = 1 - }, -/turf/open/floor/iron/cafeteria, -/area/station/security/prison/garden) "tgp" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate, @@ -60374,6 +60333,11 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) +"thg" = ( +/obj/structure/lattice, +/obj/structure/transit_tube/crossing/horizontal, +/turf/open/space/basic, +/area/space/nearstation) "tho" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 @@ -60408,6 +60372,17 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/medical/storage) +"thT" = ( +/obj/item/radio/intercom/directional/west, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/cargo/storage) "thU" = ( /obj/structure/cable, /obj/structure/grille/broken, @@ -60445,20 +60420,17 @@ /obj/machinery/computer/order_console/bitrunning, /turf/open/floor/iron/dark, /area/station/cargo/storage) -"tiY" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/camera/directional/east{ - c_tag = "Brig Warden's Office" +"tjd" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 4 }, -/obj/machinery/airalarm/directional/east, -/obj/effect/turf_decal/tile/red/half/contrasted{ +/obj/effect/turf_decal/tile/yellow{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, /turf/open/floor/iron, -/area/station/security/brig) +/area/station/cargo/miningdock) "tjv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -60565,16 +60537,18 @@ }, /turf/open/space/basic, /area/space) -"tlu" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/line{ +"tlt" = ( +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/item/flatpack/flatpacker, +/obj/effect/turf_decal/delivery, +/obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/turf/open/floor/iron, -/area/station/engineering/lobby) +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/science/lab) "tly" = ( /obj/machinery/door/airlock/maintenance{ name = "Xenobiology Maintenance" @@ -60593,17 +60567,6 @@ /obj/structure/flora/bush/sparsegrass/style_random, /turf/open/misc/asteroid, /area/station/hallway/secondary/exit/departure_lounge) -"tlJ" = ( -/obj/structure/transit_tube/diagonal/topleft, -/obj/structure/lattice/catwalk, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/space/basic, -/area/space/nearstation) "tlL" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/decal/cleanable/dirt, @@ -60673,11 +60636,6 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/science/robotics/lab) -"tmj" = ( -/obj/machinery/light/directional/west, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "tmp" = ( /obj/machinery/light/small/directional/west, /obj/effect/turf_decal/stripes/corner{ @@ -60733,6 +60691,13 @@ }, /turf/open/floor/plating/plasma/rust, /area/station/maintenance/space_hut/plasmaman) +"tmx" = ( +/obj/machinery/modular_computer/preset/id, +/obj/effect/turf_decal/bot, +/obj/machinery/computer/security/telescreen/ce/directional/north, +/obj/machinery/light/directional/north, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) "tmD" = ( /obj/machinery/firealarm/directional/west, /obj/machinery/portable_atmospherics/canister/air, @@ -60780,6 +60745,24 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/department/bridge) +"tnl" = ( +/obj/structure/rack, +/obj/item/storage/toolbox/emergency{ + pixel_y = 5 + }, +/obj/item/stack/cable_coil, +/obj/item/wirecutters, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/west, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/machinery/camera/directional/north{ + c_tag = "Recreation Arcade"; + name = "recreation camera" + }, +/obj/effect/decal/cleanable/cobweb, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron/dark, +/area/station/commons/fitness/recreation) "tnp" = ( /obj/machinery/portable_atmospherics/canister/nitrous_oxide, /obj/effect/turf_decal/delivery, @@ -60812,11 +60795,6 @@ /obj/machinery/airalarm/directional/south, /turf/open/floor/plastic, /area/station/security/prison/shower) -"tnA" = ( -/obj/effect/turf_decal/siding/wideplating/dark/corner, -/obj/structure/railing/corner, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "tnC" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/stripes/line{ @@ -61011,6 +60989,15 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/chemistry) +"tqt" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/effect/spawner/random/structure/steam_vent, +/turf/open/floor/plating, +/area/station/maintenance/starboard) "tqz" = ( /turf/closed/wall/r_wall/rust, /area/station/maintenance/central) @@ -61095,6 +61082,14 @@ /obj/machinery/portable_atmospherics/canister/plasma, /turf/open/floor/engine/plasma, /area/station/engineering/atmos) +"trx" = ( +/obj/machinery/conveyor/inverted{ + dir = 6; + id = "mining" + }, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/dark, +/area/station/cargo/miningoffice) "tsf" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -61109,20 +61104,6 @@ /obj/structure/sign/warning/fire, /turf/closed/wall/rust, /area/station/commons/fitness/recreation) -"tsD" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/conveyor{ - dir = 4; - id = "QMLoad2"; - name = "on ramp" - }, -/obj/item/radio/intercom/directional/south, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/cargo/storage) "tsF" = ( /turf/closed/wall, /area/station/security/mechbay) @@ -61240,22 +61221,6 @@ /obj/effect/turf_decal/tile/red, /turf/open/floor/iron, /area/station/hallway/primary/central) -"tuO" = ( -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/button/door/directional/south{ - id = "custodialwagon"; - name = "Custodial Bay Toggle"; - req_access = list("janitor") - }, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/mob/living/basic/bot/cleanbot/autopatrol, -/obj/effect/turf_decal/tile/purple/half/contrasted, -/obj/effect/turf_decal/tile/blue, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "tuP" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -61267,12 +61232,31 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"tuS" = ( +/obj/effect/turf_decal/siding/yellow{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "tuT" = ( /obj/machinery/holopad, /obj/effect/turf_decal/bot, /obj/effect/landmark/start/cook, /turf/open/floor/iron/dark, /area/station/service/kitchen) +"tuV" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "tvB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -61284,6 +61268,20 @@ /obj/effect/landmark/start/bitrunner, /turf/open/floor/iron/dark, /area/station/cargo/bitrunning/den) +"tvD" = ( +/obj/structure/table/wood/fancy, +/obj/item/food/grown/flower/poppy{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy, +/obj/machinery/firealarm/directional/north, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "tvH" = ( /obj/structure/lattice, /obj/machinery/atmospherics/components/unary/passive_vent{ @@ -61408,6 +61406,11 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating/airless, /area/station/maintenance/solars/starboard/fore) +"txd" = ( +/obj/structure/lattice, +/obj/structure/transit_tube, +/turf/open/space/basic, +/area/space/nearstation) "txi" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/structure/table_or_rack, @@ -61423,6 +61426,14 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) +"txB" = ( +/obj/structure/closet/secure_closet/hydroponics, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/newscaster/directional/south, +/obj/effect/turf_decal/tile/green/fourcorners, +/turf/open/floor/iron/checker, +/area/station/service/hydroponics) "tyk" = ( /obj/structure/closet/secure_closet/warden, /obj/effect/turf_decal/delivery, @@ -61434,15 +61445,6 @@ }, /turf/open/floor/iron/dark, /area/station/security/warden) -"tyA" = ( -/obj/structure/table/wood, -/obj/machinery/computer/records/medical/laptop{ - dir = 4; - pixel_y = 3; - pixel_x = 3 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/psychology) "tyI" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -61450,6 +61452,25 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/engine, /area/station/engineering/main) +"tyY" = ( +/obj/effect/turf_decal/plaque{ + icon_state = "L9" + }, +/obj/machinery/camera/directional/north{ + c_tag = "Fore Hallway Centre"; + name = "fore camera" + }, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/central/fore) "tzg" = ( /obj/structure/cable, /obj/machinery/door/airlock/research{ @@ -61506,13 +61527,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"tzH" = ( -/obj/machinery/computer/station_alert, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/north, -/obj/machinery/newscaster/directional/north, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) "tzI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -61646,6 +61660,11 @@ }, /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) +"tBy" = ( +/obj/machinery/suit_storage_unit/industrial/loader, +/obj/machinery/light/warm/directional/south, +/turf/open/floor/iron, +/area/station/cargo/warehouse) "tBJ" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -61697,6 +61716,15 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/rd) +"tCo" = ( +/obj/item/radio/intercom/directional/east, +/obj/machinery/computer/station_alert{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/machinery/light/warm/directional/east, +/turf/open/floor/iron, +/area/station/engineering/main) "tCp" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -61715,23 +61743,6 @@ "tCU" = ( /turf/closed/wall, /area/station/maintenance/port/aft) -"tCY" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/structure/sign/nanotrasen{ - pixel_y = 32 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron/dark/corner{ - dir = 1 - }, -/area/station/hallway/primary/central/fore) "tDf" = ( /obj/structure/disposalpipe/segment, /obj/structure/cable, @@ -61809,6 +61820,12 @@ }, /turf/open/floor/iron/dark, /area/station/service/janitor) +"tDT" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "tDW" = ( /obj/structure/railing/corner{ dir = 8 @@ -61873,34 +61890,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/security/prison) -"tFq" = ( -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/structure/table, -/obj/item/experi_scanner{ - pixel_x = -4 - }, -/obj/item/experi_scanner{ - pixel_x = 4 - }, -/obj/machinery/requests_console/directional/north{ - department = "Research Lab"; - name = "Research Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/ore_update, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/mapping_helpers/requests_console/information, -/turf/open/floor/iron/dark, -/area/station/science/research) -"tFK" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/maintenance/fore) "tFS" = ( /obj/structure/rack, /obj/effect/turf_decal/bot, @@ -61929,13 +61918,6 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/storage) -"tGg" = ( -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) "tGj" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/machinery/atmospherics/pipe/smart/simple/green/visible, @@ -61999,6 +61981,16 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/tcommsat/computer) +"tHf" = ( +/obj/machinery/light/directional/east, +/obj/machinery/status_display/ai/directional/east, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/structure/tank_holder/extinguisher, +/turf/open/floor/iron, +/area/station/security/processing) "tHs" = ( /obj/structure/sign/warning/fire, /obj/structure/grille, @@ -62078,22 +62070,14 @@ /obj/effect/spawner/random/maintenance/two, /turf/open/floor/iron/dark, /area/station/maintenance/port/lesser) -"tIA" = ( -/obj/structure/table/wood/fancy/black, -/obj/item/food/grown/poppy/lily{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/lily{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/poppy/lily, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/mapping_helpers/apc/cell_5k, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) +"tIJ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "tIM" = ( /obj/machinery/rnd/experimentor, /turf/open/floor/engine, @@ -62106,6 +62090,23 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"tIX" = ( +/obj/structure/table, +/obj/item/clothing/head/utility/welding{ + pixel_y = 4 + }, +/obj/machinery/camera/directional/north{ + c_tag = "Tool Storage"; + name = "engineering camera"; + network = list("ss13","engine") + }, +/obj/structure/cable, +/obj/item/clothing/gloves/color/fyellow, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/obj/machinery/light/directional/north, +/turf/open/floor/iron/dark, +/area/station/commons/storage/primary) "tIY" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -62152,6 +62153,14 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) +"tKh" = ( +/obj/machinery/airalarm/directional/west, +/obj/effect/turf_decal/bot, +/obj/machinery/computer/prisoner/gulag_teleporter_computer{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/security/processing) "tKi" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -62249,11 +62258,56 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/tcommsat/computer) +"tMo" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/loading_area{ + dir = 4; + pixel_x = 5 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/engineering/atmos/storage/gas) "tMu" = ( /obj/machinery/shower/directional/east, /obj/structure/cable, /turf/open/floor/plastic, /area/station/security/prison/shower) +"tMB" = ( +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/command{ + name = "Chief Engineer's Office" + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/all/engineering/ce, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/ce) +"tMC" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/machinery/turretid{ + control_area = "/area/station/ai/upload/chamber"; + name = "AI Upload Turret Control"; + pixel_x = -30 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "tMO" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/emcloset, @@ -62294,6 +62348,9 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lobby) +"tNf" = ( +/turf/open/floor/iron, +/area/station/cargo/miningdock) "tNj" = ( /obj/effect/turf_decal/bot, /obj/effect/spawner/random/maintenance/two, @@ -62324,14 +62381,6 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos) -"tNC" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/purple/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/science/ordnance) "tNQ" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -62406,11 +62455,6 @@ "tOy" = ( /turf/closed/wall, /area/station/cargo/drone_bay) -"tOB" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/turf/open/floor/iron, -/area/station/security/processing) "tOH" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -62498,17 +62542,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/prison/mess) -"tPN" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/light/small/directional/north, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/maintenance/starboard) "tPP" = ( /turf/closed/wall/r_wall, /area/station/engineering/storage_shared) @@ -62569,6 +62602,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/science/ordnance/office) +"tQP" = ( +/obj/effect/mapping_helpers/broken_floor, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/noticeboard/directional/east, +/turf/open/floor/wood, +/area/station/commons/vacant_room/commissary) "tRg" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/brown/half/contrasted{ @@ -62581,15 +62621,19 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron, /area/station/cargo/miningdock) +"tRk" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/tile/yellow, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) +"tRq" = ( +/obj/machinery/telecomms/server/presets/supply, +/turf/open/floor/circuit/green/telecomms/mainframe, +/area/station/tcommsat/server) "tRw" = ( /obj/machinery/telecomms/bus/preset_three, /turf/open/floor/circuit/green/telecomms/mainframe, /area/station/tcommsat/server) -"tRL" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/plating, -/area/station/maintenance/central) "tRO" = ( /obj/structure/sink/directional/west, /obj/effect/decal/cleanable/blood/old, @@ -62657,6 +62701,22 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) +"tSE" = ( +/obj/structure/table, +/obj/item/toy/cards/deck{ + pixel_x = -9 + }, +/obj/item/storage/dice{ + pixel_x = -8; + pixel_y = 11 + }, +/obj/item/storage/photo_album/prison{ + pixel_x = 8; + pixel_y = -3 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/grimy, +/area/station/security/prison) "tSI" = ( /obj/machinery/door/airlock/grunge{ id_tag = "Cabin_4"; @@ -62750,6 +62810,15 @@ /obj/structure/sign/poster/contraband/the_griffin/directional/east, /turf/open/floor/iron/dark, /area/station/maintenance/fore) +"tUA" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/brown{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/office) "tUO" = ( /obj/structure/railing{ dir = 1 @@ -62781,11 +62850,12 @@ /obj/machinery/recharger, /turf/open/floor/iron/dark, /area/station/security/office) -"tVt" = ( -/obj/machinery/light/directional/south, -/obj/machinery/suit_storage_unit/industrial/loader, -/turf/open/floor/iron, -/area/station/cargo/warehouse) +"tVo" = ( +/obj/machinery/pdapainter, +/obj/structure/sign/poster/official/ian/directional/north, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/wood, +/area/station/command/heads_quarters/hop) "tVu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -62803,6 +62873,18 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) +"tVD" = ( +/obj/machinery/door/airlock/external{ + name = "Prison External Airlock" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/maintenance/port/lesser) "tVH" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -62812,6 +62894,11 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hos) +"tVL" = ( +/obj/structure/fluff/divine/nexus, +/obj/machinery/light/small/red/directional/west, +/turf/open/floor/mineral/silver, +/area/station/service/chapel/office) "tVS" = ( /obj/effect/turf_decal/bot, /obj/machinery/computer/prisoner/management{ @@ -62860,14 +62947,20 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"tWn" = ( -/obj/machinery/light/directional/east, +"tWx" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, /obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ dir = 4 }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) +/turf/open/floor/plating, +/area/station/maintenance/solars/port/fore) "tWA" = ( /obj/structure/table, /obj/structure/cable, @@ -62932,14 +63025,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"tXB" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/burnt_floor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/plating, -/area/station/hallway/secondary/entry) "tXR" = ( /obj/structure/chair/office/light{ dir = 4 @@ -63026,20 +63111,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"tZa" = ( -/obj/item/kirbyplants/organic/plant16, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "tZc" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -63127,36 +63198,10 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/service/bar) -"tZM" = ( -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/obj/machinery/computer/security/telescreen/turbine/directional/west, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "tZX" = ( /obj/structure/flora/bush/pale/style_random, /turf/open/misc/asteroid, /area/station/maintenance/port/lesser) -"uap" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/components/binary/pump/on/layer4{ - dir = 2; - name = "Airlock Pump"; - target_pressure = 300; - hide = 1 - }, -/turf/open/floor/iron, -/area/station/security/processing) "uba" = ( /obj/structure/lattice, /obj/structure/window/reinforced/spawner/directional/east, @@ -63190,13 +63235,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) -"ubC" = ( -/obj/machinery/door/poddoor/shutters{ - id = "secmechbay"; - name = "Security Mech Bay Shutters" - }, -/turf/open/floor/plating, -/area/station/security/mechbay) "ubF" = ( /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/structure/table/wood, @@ -63214,15 +63252,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"ubJ" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/structure/cable, -/obj/machinery/door/poddoor/shutters{ - name = "Virology Isolation Shutters"; - id = "virocells" - }, -/turf/open/floor/plating, -/area/station/medical/surgery/theatre) "ubM" = ( /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -63237,16 +63266,6 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"ubU" = ( -/obj/effect/landmark/blobstart, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, -/turf/open/floor/iron/dark/telecomms, -/area/station/science/server) "ucm" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -63265,27 +63284,24 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"ucP" = ( -/obj/structure/transit_tube/crossing, -/obj/structure/lattice, -/turf/open/space/basic, -/area/space/nearstation) "ucS" = ( /obj/structure/flora/bush/flowers_yw/style_random, /obj/effect/decal/cleanable/blood/old, /obj/structure/cable, /turf/open/floor/grass, /area/station/security/prison/garden) -"udg" = ( -/obj/effect/turf_decal/tile/red/half/contrasted{ +"udd" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/landmark/event_spawn, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ dir = 1 }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 8 +/obj/effect/turf_decal/tile/red{ + dir = 1 }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +/area/station/engineering/main) "udn" = ( /obj/structure/girder, /obj/effect/decal/cleanable/dirt, @@ -63386,24 +63402,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/iron/checker, /area/station/maintenance/port/aft) -"ueJ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/siding/yellow{ - dir = 1 - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) "ueO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -63432,21 +63430,19 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central) -"ufc" = ( -/obj/machinery/requests_console/directional/east{ - department = "Garden"; - name = "Garden Requests Console" - }, -/obj/effect/turf_decal/siding/green{ - dir = 8 +"ufm" = ( +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/turf_decal/siding/purple{ + dir = 4 }, -/obj/machinery/light/small/directional/east, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/lobby) "ufy" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -63502,13 +63498,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard) -"ufU" = ( -/obj/effect/turf_decal/bot, -/obj/structure/closet/crate/freezer/surplus_limbs, -/obj/item/radio/intercom/directional/south, -/obj/machinery/light/small/directional/south, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/surgery/theatre) "ufX" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -63569,6 +63558,15 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos) +"ugx" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "ugy" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron/showroomfloor, @@ -63609,6 +63607,15 @@ /obj/machinery/fishing_portal_generator, /turf/open/floor/iron/dark, /area/station/hallway/secondary/service) +"ugP" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, +/area/station/hallway/primary/fore) "ugY" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -63722,6 +63729,32 @@ /obj/structure/flora/rock, /turf/open/misc/asteroid, /area/space/nearstation) +"ujj" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) +"ujt" = ( +/obj/machinery/door/airlock/external{ + name = "Prison External Airlock" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, +/turf/open/floor/iron/dark, +/area/station/maintenance/port/lesser) "ujN" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/delivery, @@ -63790,6 +63823,12 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/showroomfloor, /area/station/medical/treatment_center) +"ukz" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/event_spawn, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "ukM" = ( /obj/structure/sign/warning/vacuum/external/directional/south, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -63834,20 +63873,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"ukV" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/item/radio/intercom/directional/north, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red/opposingcorners, -/obj/structure/table, -/obj/machinery/light/directional/east, -/obj/item/storage/medkit/emergency, -/turf/open/floor/iron, -/area/station/commons/fitness/recreation) "ulg" = ( /obj/machinery/atmospherics/components/unary/vent_pump/siphon/monitored/oxygen_output{ dir = 4 @@ -63905,6 +63930,23 @@ "ulS" = ( /turf/closed/wall, /area/station/security/courtroom) +"ulU" = ( +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/structure/rack, +/obj/item/mod/module/thermal_regulator, +/obj/item/mod/module/plasma_stabilizer, +/obj/item/stack/cable_coil, +/obj/item/electronics/apc, +/obj/item/stack/cable_coil, +/obj/item/electronics/apc, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/dark, +/area/station/engineering/main) "ulZ" = ( /obj/effect/turf_decal/tile/neutral{ dir = 8 @@ -63933,28 +63975,23 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/miningoffice) -"umA" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 4 +"umC" = ( +/obj/machinery/camera/directional/east{ + c_tag = "Supermatter Waste Line"; + name = "engineering camera"; + network = list("ss13","engine") }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/button/door/directional/east{ + id = "engineaccess"; + name = "Engine Access Lockdown"; + req_access = list("engineering") }, -/turf/open/floor/iron, -/area/station/security/prison/garden) -"umG" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/machinery/light/small/directional/east, -/obj/effect/turf_decal/tile/purple/anticorner/contrasted, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/tile/purple, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/science) +/obj/machinery/light/warm/directional/east, +/turf/open/floor/engine, +/area/station/engineering/supermatter/room) "umJ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -63968,16 +64005,10 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/department/medical/central) -"unb" = ( -/obj/effect/turf_decal/siding/red{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) +"uni" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/space/nearstation) "unp" = ( /obj/machinery/camera/directional/north{ c_tag = "Research Hallway"; @@ -63993,19 +64024,6 @@ }, /turf/open/floor/iron/dark, /area/station/science/lobby) -"unr" = ( -/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/obj/machinery/duct, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "unv" = ( /obj/structure/closet/secure_closet/injection{ name = "justice injections locker" @@ -64116,18 +64134,6 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/sorting) -"uoA" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/closet/cardboard, -/obj/structure/sign/warning/no_smoking{ - pixel_x = 30 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/machinery/space_heater, -/turf/open/floor/plating, -/area/station/maintenance/starboard) "uoB" = ( /obj/structure/table, /obj/effect/turf_decal/stripes/corner{ @@ -64172,17 +64178,6 @@ /obj/structure/window/reinforced/spawner/directional/east, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) -"upa" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/conveyor{ - dir = 4; - id = "packagereturn"; - name = "crate return belt" - }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/south, -/turf/open/floor/iron/dark, -/area/station/cargo/sorting) "upK" = ( /obj/effect/turf_decal/siding/thinplating/dark/end, /turf/open/floor/glass/reinforced, @@ -64219,6 +64214,16 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/port) +"uqi" = ( +/obj/effect/turf_decal/box/corners{ + dir = 8 + }, +/obj/effect/landmark/start/hangover, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "uqv" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -64250,6 +64255,13 @@ dir = 4 }, /area/station/hallway/primary/fore) +"uqS" = ( +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "uqT" = ( /obj/structure/grille/broken, /obj/effect/turf_decal/stripes/line, @@ -64299,20 +64311,12 @@ dir = 8 }, /area/station/service/chapel) -"urx" = ( -/obj/structure/table, -/obj/machinery/recharger, -/obj/machinery/light/small/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/brig) -"urA" = ( -/obj/effect/turf_decal/sand/plating, -/obj/structure/window/reinforced/spawner/directional/south, -/turf/open/floor/plating/airless, -/area/space/nearstation) +"urv" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/central) "urE" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/loading_area{ @@ -64321,10 +64325,6 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/maintenance/department/bridge) -"urM" = ( -/obj/structure/fluff/divine/nexus, -/turf/open/floor/mineral/silver, -/area/station/service/chapel/office) "urN" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -64454,22 +64454,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"utF" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/siding/blue{ - dir = 4 - }, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "utH" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -64486,14 +64470,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) -"utU" = ( -/obj/machinery/light/directional/west, -/obj/structure/chair/sofa/bench{ - dir = 4 - }, -/obj/machinery/newscaster/directional/west, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "uuu" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/public/glass{ @@ -64628,16 +64604,6 @@ dir = 8 }, /area/station/hallway/primary/port) -"uwT" = ( -/obj/effect/spawner/random/vending/snackvend, -/obj/effect/turf_decal/bot, -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/central/fore) "uwX" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -64799,15 +64765,6 @@ }, /turf/open/floor/iron/dark, /area/station/commons/locker) -"uAH" = ( -/obj/machinery/airalarm/directional/south, -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, -/turf/open/floor/iron/stairs/left{ - dir = 4 - }, -/area/station/service/chapel/monastery) "uAS" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, @@ -64817,6 +64774,11 @@ }, /turf/open/floor/plating, /area/station/maintenance/fore) +"uBd" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/cargo/miningdock) "uBo" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -64829,11 +64791,6 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/commons/toilet/restrooms) -"uBH" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/transit_tube/crossing, -/turf/open/floor/plating, -/area/station/maintenance/fore) "uBM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -64862,9 +64819,6 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/paramedic, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) -"uCa" = ( -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "uCd" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -64885,6 +64839,18 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"uCJ" = ( +/obj/structure/disposalpipe/sorting/mail/flip{ + dir = 8; + name = "virology sorting disposal pipe" + }, +/obj/effect/mapping_helpers/mail_sorting/medbay/virology, +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "uCQ" = ( /obj/effect/decal/cleanable/cobweb, /obj/structure/frame/machine, @@ -64959,11 +64925,6 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron/showroomfloor, /area/station/security/checkpoint/science) -"uEa" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "uEi" = ( /obj/machinery/portable_atmospherics/scrubber, /obj/effect/turf_decal/bot, @@ -64995,18 +64956,6 @@ /obj/effect/turf_decal/siding/wideplating/dark/corner, /turf/open/floor/iron/dark, /area/station/service/chapel) -"uEX" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/pharmacy) "uFb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/stripes/corner, @@ -65183,13 +65132,6 @@ /obj/structure/disposalpipe/trunk, /turf/open/floor/engine, /area/station/science/auxlab) -"uHg" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/lobby) "uHv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/segment{ @@ -65262,6 +65204,13 @@ }, /turf/open/floor/iron/dark, /area/station/service/bar/atrium) +"uHU" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "uHY" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -65274,6 +65223,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"uId" = ( +/obj/item/radio/intercom/directional/south, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/bot, +/obj/machinery/holopad, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/red/half/contrasted, +/obj/machinery/duct, +/obj/machinery/light/dim/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/security/execution/transfer) "uIs" = ( /obj/machinery/turretid{ control_area = "/area/station/ai/satellite/interior"; @@ -65322,17 +65284,6 @@ }, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"uJs" = ( -/obj/effect/turf_decal/stripes/white/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/item/radio/intercom/directional/west, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) "uJE" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -65443,6 +65394,23 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/security/brig) +"uLb" = ( +/obj/machinery/camera/directional/east{ + c_tag = "Cryogenics"; + name = "medical camera"; + network = list("ss13","medical") + }, +/obj/machinery/status_display/evac/directional/east, +/obj/structure/bed/medical{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/box, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/dark, +/area/station/medical/treatment_center) "uLt" = ( /obj/effect/turf_decal/bot, /obj/structure/reagent_dispensers/watertank, @@ -65463,22 +65431,6 @@ }, /turf/open/floor/iron/dark, /area/station/science/xenobiology) -"uLA" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/west, -/obj/machinery/computer/mechpad{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/button/door/directional/south{ - id = "Skynet_launch"; - name = "Mech Bay Door Control" - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/science/robotics/mechbay) "uLJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -65490,6 +65442,18 @@ }, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/port) +"uLL" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "uLW" = ( /obj/structure/table, /obj/machinery/cell_charger{ @@ -65551,6 +65515,17 @@ /obj/machinery/power/turbine/turbine_outlet, /turf/open/floor/engine, /area/station/maintenance/disposal/incinerator) +"uNy" = ( +/obj/machinery/newscaster/directional/east, +/obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron, +/area/station/engineering/atmos) "uNI" = ( /obj/structure/table, /obj/item/paper_bin, @@ -65569,6 +65544,11 @@ "uNO" = ( /turf/closed/wall, /area/station/commons/toilet/restrooms) +"uNR" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/hallway/secondary/entry) "uNY" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -65590,6 +65570,26 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron/dark, /area/station/service/chapel/monastery) +"uOg" = ( +/obj/structure/table, +/obj/machinery/newscaster/directional/east, +/obj/effect/turf_decal/siding/green{ + dir = 8 + }, +/obj/item/storage/bag/plants/portaseeder, +/obj/item/crowbar/red, +/obj/item/plant_analyzer, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) +"uOl" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "uOo" = ( /obj/machinery/newscaster/directional/west, /obj/effect/spawner/random/entertainment/arcade{ @@ -65600,6 +65600,13 @@ }, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"uOp" = ( +/obj/effect/turf_decal/siding/thinplating/light/corner{ + dir = 4 + }, +/obj/machinery/airalarm/directional/north, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "uOz" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/sign/poster/contraband/random/directional/west, @@ -65713,6 +65720,11 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"uQJ" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "uQN" = ( /obj/item/kirbyplants/organic/plant21, /obj/effect/turf_decal/stripes/corner, @@ -65766,25 +65778,66 @@ /obj/effect/mapping_helpers/airlock/access/all/supply/general, /turf/open/floor/iron/dark, /area/station/cargo/miningoffice) +"uRv" = ( +/obj/item/kirbyplants/organic/applebush, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/airalarm/directional/west, +/obj/machinery/status_display/ai/directional/north, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/opposingcorners{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "uRG" = ( /mob/living/carbon/human/species/monkey/punpun, /turf/open/floor/wood/large, /area/station/service/bar/atrium) +"uRK" = ( +/obj/structure/railing/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) +"uRL" = ( +/obj/item/kirbyplants/organic/plant5, +/obj/effect/landmark/start/hangover, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "uRT" = ( /turf/closed/wall/r_wall, /area/station/science) -"uSk" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/status_display/ai/directional/west, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 +"uSd" = ( +/obj/effect/decal/cleanable/blood/old, +/obj/structure/sign/departments/science/alt/directional/north, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ +/obj/effect/turf_decal/tile/purple/anticorner/contrasted{ dir = 1 }, -/turf/open/floor/iron, -/area/station/hallway/primary/aft) +/obj/effect/turf_decal/tile/purple{ + dir = 1 + }, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/science) +"uSu" = ( +/obj/machinery/computer/shuttle/mining{ + dir = 8; + req_access = null + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/obj/structure/extinguisher_cabinet/directional/east, +/turf/open/floor/iron/dark, +/area/station/cargo/miningdock) "uSB" = ( /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/components/unary/thermomachine/heater/on{ @@ -65854,18 +65907,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) -"uTT" = ( -/obj/structure/bodycontainer/morgue{ - dir = 8 - }, -/obj/effect/turf_decal/bot_white, -/obj/machinery/light/small/directional/north, -/obj/structure/sign/poster/contraband/random/directional/east, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) "uTY" = ( /obj/machinery/power/emitter/welded{ dir = 4 @@ -65975,6 +66016,13 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) +"uWP" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/light/dim/directional/west, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) "uWQ" = ( /obj/structure/flora/rock/pile/style_random{ pixel_x = -6; @@ -66065,6 +66113,16 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/science) +"uYg" = ( +/obj/effect/landmark/event_spawn, +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/machinery/duct, +/obj/machinery/light/floor, +/turf/open/floor/iron/showroomfloor, +/area/station/science/xenobiology) "uYh" = ( /obj/machinery/atmospherics/pipe/smart/simple/dark/visible, /obj/structure/disposalpipe/segment{ @@ -66095,31 +66153,12 @@ /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, /area/station/engineering/atmos) -"uYJ" = ( -/obj/structure/table/glass, -/obj/item/folder/white, -/obj/item/folder/white, -/obj/item/crowbar/red, -/obj/item/wrench, -/obj/item/storage/box/syringes{ - pixel_x = 4; - pixel_y = 6 - }, -/obj/item/storage/box/beakers{ - pixel_y = 2 - }, -/obj/machinery/airalarm/directional/east, -/obj/structure/noticeboard/directional/south, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/machinery/light/cold/directional/east, -/obj/item/storage/pill_bottle/sepsisillin{ - pixel_x = -5; - pixel_y = -2 +"uYx" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 8 }, -/turf/open/floor/iron/dark, -/area/station/medical/virology) +/turf/closed/wall/r_wall, +/area/station/maintenance/disposal/incinerator) "uYM" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -66142,20 +66181,24 @@ /obj/effect/mapping_helpers/airlock/access/all/science/general, /turf/open/floor/iron/dark, /area/station/science/research) +"uYZ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/disposalpipe/segment{ + dir = 9 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/obj/machinery/duct, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/commons/fitness/recreation) "uZg" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall/rust, /area/station/command/heads_quarters/hos) -"uZq" = ( -/obj/effect/turf_decal/siding/white{ - dir = 8 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "uZr" = ( /obj/structure/grille/broken, /obj/effect/decal/cleanable/dirt, @@ -66213,10 +66256,6 @@ /obj/effect/decal/cleanable/glass, /turf/open/floor/plating, /area/ruin/space/has_grav/abandoned_warehouse) -"uZT" = ( -/obj/effect/landmark/start/medical_doctor, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "uZX" = ( /obj/machinery/atmospherics/pipe/smart/simple/dark/visible, /obj/machinery/portable_atmospherics/pump/lil_pump, @@ -66243,6 +66282,15 @@ }, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"vae" = ( +/obj/effect/turf_decal/box, +/turf/open/floor/engine, +/area/station/science/auxlab) +"vak" = ( +/obj/machinery/firealarm/directional/north, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "vao" = ( /obj/effect/turf_decal/tile/purple, /obj/effect/turf_decal/tile/purple/half/contrasted{ @@ -66293,26 +66341,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/disposal) -"vaP" = ( -/obj/item/storage/box/chemimp{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/storage/box/trackimp{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/structure/table, -/obj/machinery/light/directional/south, -/obj/structure/reagent_dispensers/wall/peppertank/directional/south, -/obj/item/storage/box/firingpins, -/obj/item/storage/box/firingpins, -/obj/item/storage/lockbox/loyalty, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/armory) "vba" = ( /obj/machinery/computer/records/security{ dir = 1 @@ -66400,12 +66428,26 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/pharmacy) -"vcm" = ( +"vbV" = ( /obj/structure/cable, -/turf/open/floor/circuit/green{ - luminosity = 2 +/obj/structure/chair/sofa/left/maroon{ + dir = 8 }, -/area/station/ai/upload/chamber) +/obj/structure/railing{ + dir = 4 + }, +/obj/machinery/power/apc/auto_name/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/effect/landmark/start/hangover, +/obj/machinery/camera/directional/south{ + c_tag = "Atrium Port"; + name = "service camera" + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/bar/atrium) "vct" = ( /obj/effect/spawner/random/engineering/tracking_beacon, /obj/machinery/camera/directional/north{ @@ -66415,6 +66457,13 @@ }, /turf/open/floor/engine/xenobio, /area/station/science/xenobiology) +"vcx" = ( +/obj/item/beacon, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/floor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "vcL" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/airalarm/directional/south, @@ -66423,14 +66472,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) -"vcY" = ( -/obj/machinery/biogenerator, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 8 - }, -/obj/machinery/light/directional/west, -/turf/open/floor/iron, -/area/station/security/prison/garden) "vdy" = ( /obj/machinery/door/morgue{ name = "Confession Booth (Chaplain)"; @@ -66521,6 +66562,13 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"veY" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/central/fore) "veZ" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/hatch{ @@ -66578,6 +66626,28 @@ }, /turf/open/space/basic, /area/space/nearstation) +"vfW" = ( +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/cobweb, +/obj/item/book{ + desc = "An undeniably handy book."; + icon_state = "bookknock"; + name = "A Simpleton's Guide to Safe-cracking with Stethoscopes" + }, +/obj/item/stack/spacecash/c500{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/stack/spacecash/c1000, +/obj/item/gun/ballistic/automatic/pistol/deagle/gold, +/obj/machinery/status_display/ai/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/structure/safe/vault, +/obj/machinery/light/small/red/directional/west, +/turf/open/floor/iron/dark, +/area/station/command/vault) "vgc" = ( /obj/structure/sign/warning/vacuum/directional/east, /obj/effect/decal/cleanable/blood/old, @@ -66626,6 +66696,15 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"vgZ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/primary/aft) "vha" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -66652,23 +66731,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/science) -"vhi" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) -"vhn" = ( -/obj/effect/mapping_helpers/broken_floor, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/structure/noticeboard/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/wood, -/area/station/commons/vacant_room/commissary) "vhp" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ @@ -66691,26 +66753,23 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/service/chapel/funeral) -"vhL" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/machinery/light/directional/north, -/turf/open/floor/carpet/royalblue, -/area/station/service/chapel/office) "vhZ" = ( /obj/effect/spawner/structure/electrified_grille, /turf/open/floor/plating/rust, /area/station/security/prison) -"vim" = ( -/obj/effect/turf_decal/siding/thinplating/light/corner{ +"vij" = ( +/obj/effect/turf_decal/tile/blue, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/effect/turf_decal/box, +/obj/machinery/vitals_reader/directional/east, +/obj/structure/bed/medical{ dir = 4 }, -/obj/machinery/airalarm/directional/north, -/obj/machinery/light/directional/north, /turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) +/area/station/medical/treatment_center) "viq" = ( /obj/structure/lattice/catwalk, /obj/structure/railing, @@ -66940,13 +66999,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/fore) -"vly" = ( -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, -/obj/machinery/portable_atmospherics/scrubber, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "vlJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -66959,11 +67011,6 @@ }, /turf/open/floor/iron/dark, /area/station/security/detectives_office) -"vlM" = ( -/obj/structure/transit_tube/crossing, -/obj/structure/lattice/catwalk, -/turf/open/space/basic, -/area/space/nearstation) "vme" = ( /obj/effect/turf_decal/tile/green/opposingcorners, /obj/effect/turf_decal/tile/blue{ @@ -67003,6 +67050,21 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"vmV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted, +/obj/effect/turf_decal/tile/purple, +/obj/machinery/camera/autoname/directional/east, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/science/auxlab) "vmZ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -67163,11 +67225,6 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/medical/storage) -"vpP" = ( -/obj/item/radio/intercom/directional/south, -/obj/effect/turf_decal/tile/green/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/virology) "vpV" = ( /obj/structure/chair{ dir = 4 @@ -67203,23 +67260,15 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"vqg" = ( -/obj/item/kirbyplants/organic/plant5, -/obj/structure/noticeboard/directional/east, -/obj/item/paper/monitorkey, -/obj/machinery/camera/directional/east{ - c_tag = "Chief Engineer's Office"; - name = "engineering camera"; - network = list("ss13","engine") - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) -"vqj" = ( -/obj/machinery/atmospherics/components/unary/airlock_pump{ - dir = 4 +"vqf" = ( +/obj/machinery/module_duplicator, +/obj/machinery/newscaster/directional/north, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 1 }, -/turf/open/floor/plating, -/area/station/maintenance/disposal/incinerator) +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance/office) "vqr" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -67281,18 +67330,6 @@ /obj/item/fishing_rod, /turf/open/floor/grass, /area/station/service/hydroponics/garden) -"vrf" = ( -/obj/structure/table/wood, -/obj/item/storage/fancy/cigarettes/cigars/havana{ - pixel_y = 5 - }, -/obj/machinery/light/directional/east, -/obj/item/lighter, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/captain) "vrq" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/disposalpipe/segment{ @@ -67310,20 +67347,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) -"vrD" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/command/bridge) "vrN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -67393,14 +67416,6 @@ /obj/effect/turf_decal/tile/yellow/half/contrasted, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/lobby) -"vte" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) "vtf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -67442,14 +67457,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) -"vuk" = ( -/obj/machinery/light/small/directional/south, -/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ - dir = 8 - }, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/exit/departure_lounge) "vum" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -67466,6 +67473,18 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"vun" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "vuu" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -67482,6 +67501,13 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/port) +"vuD" = ( +/obj/effect/turf_decal/tile/blue/opposingcorners, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "vuF" = ( /obj/machinery/light_switch/directional/west, /obj/machinery/disposal/bin/tagger, @@ -67499,15 +67525,6 @@ }, /turf/open/floor/engine, /area/station/science/xenobiology) -"vuM" = ( -/obj/machinery/airalarm/directional/west, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/bot, -/obj/machinery/computer/prisoner/gulag_teleporter_computer{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/processing) "vuQ" = ( /turf/closed/wall/r_wall, /area/station/hallway/primary/starboard) @@ -67518,6 +67535,16 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) +"vvb" = ( +/obj/machinery/conveyor{ + dir = 8; + id = "mining" + }, +/obj/machinery/bouldertech/refinery{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/cargo/miningoffice) "vvd" = ( /obj/machinery/mech_bay_recharge_port, /obj/machinery/airalarm/directional/west, @@ -67687,14 +67714,6 @@ dir = 4 }, /area/station/hallway/primary/fore) -"vxf" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) "vxr" = ( /obj/machinery/computer/atmos_control/mix_tank{ dir = 1 @@ -67786,37 +67805,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/lab) -"vyE" = ( -/obj/structure/table, -/obj/machinery/light_switch/directional/north{ - pixel_x = -8 - }, -/obj/item/clothing/gloves/color/orange, -/obj/item/reagent_containers/spray/cleaner{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/flashlight{ - pixel_y = 4 - }, -/obj/item/flashlight{ - pixel_y = 4 - }, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/machinery/button/door/directional/north{ - id = "custodialwagon"; - name = "Custodial Bay Toggle"; - pixel_x = 8; - req_access = list("janitor") - }, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/janitor) "vyN" = ( /turf/open/floor/engine, /area/station/engineering/supermatter/engine) @@ -67858,6 +67846,14 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) +"vzu" = ( +/obj/effect/turf_decal/stripes/end{ + dir = 4 + }, +/obj/item/radio/intercom/directional/east, +/obj/machinery/status_display/ai/directional/south, +/turf/open/floor/engine, +/area/station/tcommsat/computer) "vzy" = ( /turf/closed/wall, /area/station/service/janitor) @@ -67870,18 +67866,14 @@ }, /turf/open/floor/circuit/telecomms, /area/station/science/xenobiology) -"vzV" = ( -/obj/structure/sign/poster/official/anniversary_vintage_reprint/directional/north, -/obj/machinery/camera/directional/north{ - c_tag = "Chemistry North"; - network = list("ss13","medbay") - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) +"vzR" = ( +/obj/machinery/light/small/directional/south, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "vzZ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -67942,10 +67934,37 @@ }, /turf/open/floor/iron/dark, /area/station/service/kitchen) +"vAG" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/decal/cleanable/dirt, +/obj/effect/landmark/event_spawn, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/hidden/layer4{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/bridge_pipe/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "vBi" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"vBl" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/binary/valve/digital{ + name = "Waste Release" + }, +/obj/machinery/atmospherics/pipe/bridge_pipe/green/visible{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/directional/west, +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "vBt" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -67959,18 +67978,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/engineering/supermatter/room) -"vBv" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted, -/obj/machinery/light/directional/north, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/siding/purple/end{ - dir = 1 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/science/auxlab) "vBE" = ( /obj/structure/chair{ dir = 4 @@ -68050,22 +68057,20 @@ /obj/machinery/portable_atmospherics/canister/nitrous_oxide, /turf/open/floor/iron/dark, /area/station/science/ordnance/storage) -"vCk" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/north, -/obj/machinery/firealarm/directional/north, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/armory) "vCn" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"vCp" = ( +/obj/effect/turf_decal/box/corners{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "vCA" = ( /obj/effect/landmark/start/hangover, /obj/structure/disposalpipe/segment{ @@ -68112,14 +68117,16 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"vCV" = ( -/obj/structure/cable, +"vDc" = ( +/obj/machinery/firealarm/directional/east, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/obj/effect/turf_decal/delivery, +/obj/machinery/byteforge, +/obj/machinery/camera/autoname/directional/north, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ - dir = 1 - }, +/obj/machinery/light/small/directional/east, /turf/open/floor/iron/dark, -/area/station/maintenance/disposal/incinerator) +/area/station/cargo/bitrunning/den) "vDk" = ( /turf/closed/wall, /area/station/engineering/supermatter/room) @@ -68127,6 +68134,16 @@ /obj/machinery/smartfridge/drinks, /turf/open/floor/wood, /area/station/hallway/secondary/service) +"vDK" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "vDO" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/command/glass{ @@ -68166,21 +68183,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"vEt" = ( -/obj/structure/table, -/obj/item/hfr_box/core, -/obj/item/hfr_box/corner, -/obj/item/hfr_box/corner, -/obj/item/hfr_box/corner, -/obj/item/hfr_box/corner, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "vEv" = ( /obj/structure/railing, /obj/effect/turf_decal/siding/thinplating/dark/end{ @@ -68234,24 +68236,18 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/virology) -"vFk" = ( -/obj/machinery/ai_slipper{ - uses = 10 - }, -/obj/effect/turf_decal/box, -/obj/machinery/flasher/directional/north{ - id = "AI"; - name = "Meatbag Pacifier"; - pixel_x = 26 - }, -/obj/machinery/camera/directional/south{ - c_tag = "AI Chamber Core"; - name = "core camera"; - network = list("aicore") +"vFe" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/light/small/directional/west, +/turf/open/floor/grass, +/area/station/science/auxlab) +"vFp" = ( +/obj/structure/railing/corner, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 }, -/obj/machinery/light/floor, -/turf/open/floor/engine, -/area/station/ai/satellite/chamber) +/turf/open/floor/iron/dark, +/area/station/service/chapel) "vFv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -68314,6 +68310,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) +"vGd" = ( +/obj/structure/table, +/obj/item/storage/toolbox/mechanical, +/obj/item/clothing/head/utility/welding, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) "vGn" = ( /obj/machinery/door/poddoor/preopen{ id = "prisonblast"; @@ -68365,14 +68370,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"vGY" = ( -/obj/machinery/light/directional/east, -/obj/machinery/newscaster/directional/north, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/science/lobby) "vHa" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -68466,6 +68463,25 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"vHW" = ( +/obj/machinery/computer/records/security{ + dir = 4 + }, +/obj/machinery/requests_console/directional/north{ + department = "Security"; + name = "Security Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/supplies, +/obj/effect/turf_decal/tile/red/anticorner/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/security/checkpoint/science) "vHZ" = ( /obj/effect/turf_decal/box/white{ color = "#9FED58" @@ -68520,6 +68536,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/science/server) +"vIV" = ( +/obj/structure/table, +/obj/machinery/camera/directional/west{ + c_tag = "ai_upload"; + name = "upload camera"; + network = list("aiupload") + }, +/obj/item/radio/intercom/directional/west, +/obj/effect/spawner/round_default_module, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/light/cold/dim/directional/west, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "vJc" = ( /turf/closed/wall/r_wall, /area/station/science/xenobiology) @@ -68545,15 +68577,6 @@ }, /turf/open/floor/iron, /area/station/security/checkpoint/customs) -"vJk" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security/glass{ - name = "Security Checkpoint" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/general, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) "vJm" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/light/directional/south, @@ -68597,22 +68620,17 @@ }, /turf/open/floor/iron/dark, /area/station/security/armory) -"vJB" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/command/bridge) "vJH" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /mob/living/basic/bot/secbot/beepsky, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"vJM" = ( +/obj/machinery/holopad, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "vJN" = ( /obj/machinery/hydroponics/constructable, /obj/structure/railing{ @@ -68624,6 +68642,14 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"vJO" = ( +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/turf/open/floor/iron/stairs/left{ + dir = 4 + }, +/area/station/service/chapel/monastery) "vJP" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -68633,30 +68659,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"vJT" = ( -/obj/machinery/vending/wardrobe/bar_wardrobe, -/obj/effect/turf_decal/delivery, -/obj/effect/decal/cleanable/cobweb, -/obj/item/radio/intercom/directional/west, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/service/bar/backroom) -"vJZ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "vKa" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -68699,6 +68701,14 @@ /obj/machinery/airalarm/directional/east, /turf/open/floor/plating, /area/station/maintenance/central) +"vKC" = ( +/obj/structure/rack, +/obj/effect/spawner/random/techstorage/security_all, +/obj/effect/turf_decal/bot, +/obj/structure/extinguisher_cabinet/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "vKE" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -68781,6 +68791,14 @@ /obj/structure/cable, /turf/open/floor/iron/solarpanel/airless, /area/station/solars/starboard/fore) +"vLU" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/machinery/portable_atmospherics/canister, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "vMb" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -68801,23 +68819,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/warden) -"vMk" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible/layer2, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/engineering/hallway) "vMo" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -68895,27 +68896,21 @@ /obj/structure/sign/warning/docking, /turf/closed/wall/rust, /area/station/cargo/miningdock) -"vNB" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +"vNo" = ( /obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/simple/dark/visible, -/obj/effect/turf_decal/stripes/line{ +/obj/structure/chair/sofa/right/maroon{ dir = 4 }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ +/obj/structure/railing{ dir = 8 }, -/obj/effect/turf_decal/tile/red{ +/obj/machinery/airalarm/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 8 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/duct, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) +/turf/open/floor/iron/dark, +/area/station/service/bar/atrium) "vNE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -68949,6 +68944,10 @@ dir = 8 }, /area/station/hallway/primary/port) +"vNU" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/showroomfloor, +/area/station/security/execution/transfer) "vOl" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -68977,6 +68976,22 @@ }, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) +"vOJ" = ( +/obj/structure/table/glass, +/obj/item/crowbar{ + pixel_x = -8; + pixel_y = 11 + }, +/obj/item/emergency_bed{ + pixel_x = 7; + pixel_y = 10 + }, +/obj/item/emergency_bed, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/medical/office) "vOL" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 8 @@ -69032,6 +69047,11 @@ "vOX" = ( /turf/closed/wall/rust, /area/station/maintenance/starboard) +"vPc" = ( +/obj/effect/turf_decal/sand/plating, +/obj/structure/transit_tube, +/turf/open/floor/plating/airless, +/area/space/nearstation) "vPd" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -69040,17 +69060,16 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"vPz" = ( -/obj/item/kirbyplants/random, -/obj/machinery/status_display/ai/directional/east, +"vPo" = ( /obj/effect/decal/cleanable/dirt, -/obj/machinery/camera/motion/directional/south{ - c_tag = "ai_upload Consoles"; - network = list("aiupload"); - name = "upload camera" +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 }, +/obj/structure/closet/secure_closet/medical2, /turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) +/area/station/medical/virology) "vPB" = ( /obj/structure/flora/grass/jungle/b/style_random, /obj/structure/flora/bush/grassy/style_random, @@ -69063,6 +69082,13 @@ /obj/structure/window/spawner/directional/east, /turf/open/floor/grass, /area/station/hallway/secondary/exit/departure_lounge) +"vPD" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/holopad, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "vPG" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -69107,6 +69133,15 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/commons/locker) +"vQc" = ( +/obj/structure/chair/sofa/left/brown{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/sign/poster/contraband/the_big_gas_giant_truth/directional/west, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/dark, +/area/station/cargo/miningoffice) "vQf" = ( /obj/effect/turf_decal/sand/plating, /obj/machinery/atmospherics/components/unary/passive_vent/layer2{ @@ -69150,11 +69185,6 @@ /obj/structure/sign/departments/medbay/alt, /turf/closed/wall, /area/station/maintenance/port/greater) -"vRh" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "vRk" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -69165,21 +69195,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/maintenance/department/crew_quarters/bar) -"vRl" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/siding/red{ - dir = 9 - }, -/obj/effect/landmark/start/depsec/supply, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/checkpoint/supply) "vRo" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -69232,11 +69247,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron/dark, /area/station/command/bridge) -"vRQ" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "vRU" = ( /obj/structure/closet/secure_closet/medical3, /obj/item/storage/belt/medical, @@ -69315,6 +69325,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/cargo/miningdock) +"vSU" = ( +/obj/effect/spawner/random/structure/girder, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/station/maintenance/central) +"vSW" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/camera/autoname/directional/east, +/obj/machinery/light/small/directional/east, +/turf/open/floor/engine, +/area/station/science/auxlab) "vTs" = ( /obj/machinery/hydroponics/constructable, /obj/item/seeds/apple, @@ -69399,37 +69422,6 @@ /obj/effect/mapping_helpers/airlock/access/all/supply/general, /turf/open/floor/plating, /area/station/maintenance/department/cargo) -"vUr" = ( -/obj/structure/table/glass, -/obj/item/crowbar{ - pixel_x = -8; - pixel_y = 11 - }, -/obj/machinery/light/directional/east, -/obj/item/emergency_bed{ - pixel_x = 7; - pixel_y = 10 - }, -/obj/item/emergency_bed, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/office) -"vUs" = ( -/obj/structure/bed, -/obj/machinery/iv_drip, -/obj/effect/turf_decal/bot_white/right, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/item/bedsheet/medical, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/obj/machinery/light/small/directional/east, -/turf/open/floor/iron/dark, -/area/station/medical/medbay/central) "vUt" = ( /obj/structure/cable, /obj/effect/turf_decal/bot, @@ -69438,15 +69430,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/office) -"vUu" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/extinguisher_cabinet/directional/east, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark/corner, -/area/station/hallway/primary/port) "vUC" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -69502,12 +69485,13 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) -"vVt" = ( -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, +"vVv" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, -/area/station/security/brig) +/area/station/hallway/secondary/exit/departure_lounge) "vVw" = ( /turf/closed/wall/r_wall/rust, /area/station/science/lab) @@ -69539,6 +69523,16 @@ "vVT" = ( /turf/closed/wall/r_wall/rust, /area/station/engineering/storage/tech) +"vVW" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/status_display/ai/directional/south, +/obj/machinery/firealarm/directional/east, +/turf/open/floor/engine, +/area/station/ai/satellite/chamber) "vWv" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -69555,23 +69549,6 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/office) -"vWC" = ( -/obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ - dir = 5 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/corner, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/dark, -/area/station/engineering/atmos) "vWE" = ( /obj/effect/decal/cleanable/food/flour, /obj/effect/landmark/start/cook, @@ -69627,11 +69604,6 @@ /obj/structure/sign/poster/official/random/directional/north, /turf/open/floor/iron/dark, /area/station/medical/surgery/theatre) -"vXn" = ( -/obj/structure/closet/secure_closet/brig, -/obj/effect/turf_decal/tile/red/anticorner/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) "vXp" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/conveyor{ @@ -69642,6 +69614,19 @@ /obj/structure/sign/poster/contraband/random/directional/south, /turf/open/floor/plating, /area/station/cargo/storage) +"vXz" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/machinery/atmospherics/components/unary/passive_vent{ + dir = 4; + name = "killroom vent" + }, +/turf/open/floor/circuit/telecomms, +/area/station/science/xenobiology) "vXC" = ( /obj/machinery/door/airlock/atmos{ name = "Atmospherics Connector" @@ -69718,6 +69703,25 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) +"vYQ" = ( +/obj/structure/table, +/obj/item/storage/toolbox/mechanical{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/storage/toolbox/mechanical, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/sign/warning/fire/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/commons/storage/primary) "vZa" = ( /obj/machinery/computer/shuttle/mining, /obj/effect/turf_decal/tile/brown/half/contrasted{ @@ -69827,6 +69831,16 @@ dir = 8 }, /area/station/service/chapel) +"waw" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/extinguisher_cabinet/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark/corner, +/area/station/hallway/primary/port) "waI" = ( /obj/effect/turf_decal/siding/green{ dir = 8 @@ -69873,6 +69887,47 @@ /obj/effect/mapping_helpers/airlock/access/any/command/maintenance, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) +"wbF" = ( +/obj/structure/table, +/obj/machinery/camera/directional/east{ + c_tag = "Garden" + }, +/obj/effect/turf_decal/siding/green{ + dir = 8 + }, +/obj/item/shovel/spade, +/obj/item/hatchet, +/obj/item/cultivator, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/item/shovel/spade, +/obj/item/cultivator, +/obj/item/hatchet, +/obj/item/plant_analyzer, +/obj/item/reagent_containers/cup/bottle/nutrient/rh{ + pixel_x = 2; + pixel_y = 1 + }, +/obj/item/reagent_containers/cup/bottle/nutrient/ez, +/obj/machinery/light/warm/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) +"wbG" = ( +/obj/effect/landmark/start/cyborg, +/obj/machinery/airalarm/directional/north, +/obj/machinery/camera/motion/directional/west{ + c_tag = "ai_upload Foyer"; + name = "upload camera"; + network = list("aiupload") + }, +/obj/machinery/computer/security/telescreen/aiupload/directional/west, +/obj/machinery/recharge_station, +/turf/open/floor/circuit/green{ + luminosity = 2 + }, +/area/station/ai/upload/chamber) "wbH" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/stripes/line{ @@ -69982,22 +70037,6 @@ /obj/structure/sign/warning/secure_area, /turf/closed/wall/r_wall, /area/station/maintenance/department/bridge) -"wcX" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/obj/effect/turf_decal/siding/yellow/corner{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/box, -/turf/open/floor/iron, -/area/station/engineering/lobby) "wdf" = ( /obj/effect/turf_decal/bot, /obj/structure/extinguisher_cabinet/directional/west, @@ -70007,6 +70046,12 @@ }, /turf/open/floor/iron/dark, /area/station/science/lobby) +"wdm" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple/anticorner/contrasted, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance/office) "wdu" = ( /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/office) @@ -70022,17 +70067,11 @@ /obj/effect/turf_decal/tile/yellow, /turf/open/floor/iron, /area/station/cargo/warehouse) -"wdD" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark/corner{ - dir = 4 - }, -/area/station/hallway/primary/fore) +"wdC" = ( +/obj/structure/closet/secure_closet/brig, +/obj/effect/turf_decal/tile/red/half/contrasted, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "wdL" = ( /obj/effect/turf_decal/delivery, /turf/open/floor/iron, @@ -70129,18 +70168,6 @@ /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/iron, /area/station/command/teleporter) -"wfD" = ( -/obj/machinery/flasher/directional/west{ - id = "AI"; - name = "Meatbag Pacifier" - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 10 - }, -/obj/structure/cable, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "wgq" = ( /obj/structure/table, /obj/item/storage/toolbox/mechanical, @@ -70156,14 +70183,13 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"wgr" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/chair/office{ - dir = 1 +"wgy" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 4 }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) +/obj/machinery/light/warm/directional/north, +/turf/open/floor/wood, +/area/station/service/bar/atrium) "wgA" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -70273,13 +70299,6 @@ /obj/effect/spawner/structure/window/reinforced/tinted, /turf/open/floor/plating, /area/station/commons/locker) -"win" = ( -/obj/effect/turf_decal/siding/thinplating/light/corner{ - dir = 1 - }, -/obj/machinery/light/directional/north, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) "wiw" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -70291,27 +70310,13 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/dark, /area/station/service/bar/atrium) -"wiz" = ( -/obj/structure/cable, -/obj/structure/chair/sofa/left/maroon{ - dir = 8 - }, -/obj/machinery/light/directional/south, -/obj/structure/railing{ - dir = 4 - }, -/obj/machinery/power/apc/auto_name/directional/south, +"wiK" = ( /obj/effect/decal/cleanable/dirt, -/obj/effect/landmark/start/hangover, -/obj/machinery/camera/directional/south{ - c_tag = "Atrium Port"; - name = "service camera" - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/structure/cable, +/obj/item/kirbyplants/photosynthetic, /turf/open/floor/iron/dark, -/area/station/service/bar/atrium) +/area/station/ai/upload/chamber) "wiM" = ( /obj/structure/cable, /obj/structure/table, @@ -70322,19 +70327,19 @@ }, /turf/open/floor/iron/dark, /area/station/command/bridge) -"wiR" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/binary/valve/digital{ - name = "Waste Release" - }, -/obj/machinery/atmospherics/pipe/bridge_pipe/green/visible{ - dir = 8 +"wjc" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/light_switch/directional/west, +/obj/structure/disposalpipe/trunk{ + dir = 1 }, +/obj/machinery/firealarm/directional/south, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 + dir = 1 }, +/obj/machinery/disposal/bin/tagger, /turf/open/floor/iron/dark, -/area/station/engineering/atmos) +/area/station/medical/office) "wjm" = ( /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 @@ -70360,21 +70365,6 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/ai/satellite/chamber) -"wjz" = ( -/obj/machinery/light/directional/south, -/obj/machinery/camera/directional/south{ - c_tag = "Aft Hallway Engineering Doors"; - name = "aft camera" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/hallway/primary/aft) "wjD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -70385,16 +70375,6 @@ dir = 8 }, /area/station/hallway/primary/central/fore) -"wjG" = ( -/obj/effect/turf_decal/siding/white{ - dir = 8 - }, -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "wjK" = ( /turf/closed/wall, /area/station/maintenance/department/security) @@ -70416,20 +70396,19 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos) -"wkB" = ( -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 +"wky" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "N2O to Pure" }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red{ +/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/visible{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ +/obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 1 }, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +/turf/open/floor/iron/dark, +/area/station/engineering/atmos) "wkC" = ( /obj/machinery/airalarm/directional/north, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -70466,10 +70445,26 @@ /obj/effect/mapping_helpers/airlock/access/all/security/general, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) +"wlb" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/obj/effect/turf_decal/box, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "wlm" = ( /obj/structure/bookcase/random/fiction, /turf/open/floor/iron/dark, /area/station/maintenance/starboard/fore) +"wlu" = ( +/obj/structure/flora/bush/sparsegrass/style_random, +/obj/structure/flora/bush/sparsegrass/style_random, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/grass, +/area/station/service/chapel) "wlz" = ( /obj/structure/railing{ dir = 8 @@ -70510,15 +70505,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"wmj" = ( -/obj/machinery/vending/wardrobe/sec_wardrobe, -/obj/effect/turf_decal/bot, -/obj/structure/reagent_dispensers/wall/peppertank/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/lockers) "wmn" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -70581,11 +70567,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"wmM" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/light/directional/east, -/turf/open/floor/carpet/green, -/area/station/service/lawoffice) "wnc" = ( /obj/structure/dresser, /obj/machinery/light/small/directional/north, @@ -70605,6 +70586,20 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/storage) +"wnv" = ( +/obj/effect/landmark/start/chief_engineer, +/obj/structure/cable, +/obj/effect/turf_decal/siding/yellow{ + dir = 5 + }, +/obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red/opposingcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "wny" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/turf_decal/delivery, @@ -70678,6 +70673,12 @@ /obj/structure/sign/poster/contraband/rebels_unite/directional/south, /turf/open/floor/plating/airless, /area/station/maintenance/space_hut/plasmaman) +"woi" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) "woj" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -70711,36 +70712,34 @@ }, /turf/open/floor/engine/hull/reinforced, /area/space/nearstation) -"woB" = ( -/obj/machinery/airalarm/directional/west, -/obj/machinery/power/apc/auto_name/directional/south, -/obj/structure/cable, +"woJ" = ( +/obj/structure/closet/firecloset, +/obj/effect/turf_decal/delivery, /obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue{ dir = 8 }, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ +/turf/open/floor/iron/dark, +/area/station/hallway/primary/aft) +"woL" = ( +/obj/machinery/holopad, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/white, +/area/station/security/prison/mess) +"woT" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ dir = 8 }, -/obj/machinery/vending/coffee, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/break_room) -"woG" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/north, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/security/prison) -"woJ" = ( -/obj/structure/closet/firecloset, -/obj/effect/turf_decal/delivery, +/obj/machinery/portable_atmospherics/canister/anesthetic_mix, +/obj/effect/turf_decal/bot, /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 8 }, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/cable, /turf/open/floor/iron/dark, -/area/station/hallway/primary/aft) +/area/station/medical/cryo) "woV" = ( /obj/effect/turf_decal/bot, /obj/structure/rack, @@ -70885,6 +70884,14 @@ /obj/effect/turf_decal/trimline/purple/filled/line, /turf/open/floor/plating/plasma/rust, /area/station/maintenance/space_hut/plasmaman) +"wqP" = ( +/obj/machinery/firealarm/directional/east, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron/dark, +/area/station/command/bridge) "wqQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/siding/purple, @@ -70946,6 +70953,10 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"wrA" = ( +/obj/machinery/light/dim/directional/north, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "wrI" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/public/glass{ @@ -70993,19 +71004,20 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"wsy" = ( -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ - dir = 4 +"wst" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 8 }, -/obj/structure/table, -/obj/item/storage/box/disks{ - pixel_y = 5 +/obj/effect/turf_decal/box/red, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/cable, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 }, -/obj/item/clothing/gloves/latex, -/obj/item/clothing/gloves/latex, -/turf/open/floor/iron/dark, -/area/station/science/research) +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/showroomfloor, +/area/station/science/ordnance) "wsD" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/neutral{ @@ -71040,6 +71052,14 @@ }, /turf/open/floor/iron, /area/station/command/bridge) +"wtM" = ( +/obj/effect/turf_decal/bot, +/obj/structure/closet/secure_closet/hydroponics, +/obj/structure/sign/poster/contraband/kudzu/directional/east, +/obj/effect/turf_decal/tile/green/fourcorners, +/obj/machinery/light/small/directional/south, +/turf/open/floor/iron/checker, +/area/station/service/hydroponics) "wtP" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/blue, @@ -71095,6 +71115,11 @@ /obj/structure/sign/warning/deathsposal, /turf/closed/wall, /area/station/maintenance/starboard/aft) +"wux" = ( +/turf/open/floor/iron/stairs/left{ + dir = 8 + }, +/area/station/hallway/primary/fore) "wuE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -71105,6 +71130,22 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"wuG" = ( +/obj/structure/table/wood/fancy/black, +/obj/item/food/grown/flower/poppy/lily{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/lily{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/poppy/lily, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/mapping_helpers/apc/cell_5k, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/service/chapel/monastery) "wuI" = ( /obj/structure/sign/poster/contraband/missing_gloves, /turf/closed/wall/rust, @@ -71119,20 +71160,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos) -"wuT" = ( -/obj/machinery/door/airlock/external{ - name = "Prison External Airlock" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 4 - }, -/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, -/turf/open/floor/iron/dark, -/area/station/maintenance/port/lesser) "wvj" = ( /obj/structure/rack, /obj/effect/spawner/random/techstorage/command_all, @@ -71140,6 +71167,21 @@ luminosity = 2 }, /area/station/engineering/storage/tech) +"wvw" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/light/small/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/mapping_helpers/broken_floor, +/obj/effect/turf_decal/bot, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 4 + }, +/turf/open/floor/plating, +/area/station/maintenance/solars/port/aft) "wwf" = ( /obj/machinery/disposal/bin/tagger, /obj/structure/disposalpipe/trunk, @@ -71148,20 +71190,15 @@ /obj/machinery/newscaster/directional/north, /turf/open/floor/wood, /area/station/service/library) -"wwj" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/machinery/turretid{ - control_area = "/area/station/ai/upload/chamber"; - name = "AI Upload Turret Control"; - pixel_x = -30 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 1 +"wwg" = ( +/obj/machinery/light/small/directional/north, +/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only{ + dir = 8 }, -/obj/machinery/light/small/directional/east, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) +/obj/effect/turf_decal/bot, +/obj/structure/sign/warning/vacuum/external/directional/north, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "wwv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -71231,6 +71268,21 @@ "wxq" = ( /turf/closed/wall/rust, /area/station/service/hydroponics) +"wxr" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/siding/blue{ + dir = 4 + }, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 4 + }, +/obj/machinery/duct, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) "wxx" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -71248,6 +71300,25 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/carpet/green, /area/ruin/space/has_grav/abandoned_warehouse) +"wxH" = ( +/obj/structure/table/wood, +/obj/item/flashlight/lamp/green{ + pixel_x = -5; + pixel_y = 7 + }, +/obj/item/pen{ + pixel_x = 8; + pixel_y = 5 + }, +/obj/item/pen/red{ + pixel_x = 5; + pixel_y = 1 + }, +/obj/item/toy/figure/detective, +/obj/item/radio/intercom/directional/east, +/obj/machinery/light/warm/directional/east, +/turf/open/floor/carpet/green, +/area/station/security/detectives_office) "wxI" = ( /obj/item/kirbyplants/organic/plant5, /obj/effect/decal/cleanable/dirt, @@ -71260,15 +71331,14 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"wyh" = ( -/obj/machinery/light/small/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/blue/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron, -/area/station/hallway/secondary/exit/departure_lounge) +"wxX" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Security Maintenance" + }, +/obj/effect/mapping_helpers/airlock/access/all/security/brig, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/maintenance/aft) "wyv" = ( /obj/structure/table, /obj/item/clipboard{ @@ -71306,6 +71376,13 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"wyS" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/machinery/light/floor, +/turf/open/floor/iron, +/area/station/cargo/storage) "wyX" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -71349,20 +71426,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"wzk" = ( -/obj/machinery/modular_computer/preset/command{ - dir = 4 - }, -/obj/machinery/requests_console/directional/west{ - department = "Head of Personnel's Desk"; - name = "Head of Personnel's Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/mapping_helpers/requests_console/information, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/machinery/light/directional/west, -/turf/open/floor/carpet/blue, -/area/station/command/heads_quarters/hop) "wzp" = ( /obj/machinery/hydroponics/constructable, /obj/structure/railing/corner{ @@ -71382,11 +71445,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) -"wzy" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/structure/sign/warning/vacuum/external, -/turf/open/floor/plating, -/area/station/hallway/secondary/exit/departure_lounge) "wzA" = ( /obj/effect/turf_decal/delivery, /obj/structure/closet/emcloset, @@ -71431,21 +71489,17 @@ }, /turf/open/floor/iron/dark, /area/station/medical/office) -"wzY" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 +"wAb" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/obj/effect/decal/cleanable/blood/old, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) +/obj/machinery/camera/autoname/directional/west, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/virology) "wAn" = ( /obj/effect/turf_decal/tile/blue/opposingcorners, /obj/effect/turf_decal/tile/red/opposingcorners{ @@ -71501,6 +71555,17 @@ /obj/machinery/telecomms/broadcaster/preset_left, /turf/open/floor/circuit/green/telecomms/mainframe, /area/station/tcommsat/server) +"wBb" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark/corner{ + dir = 4 + }, +/area/station/hallway/primary/fore) "wBf" = ( /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/iron, @@ -71549,6 +71614,11 @@ }, /turf/open/floor/iron, /area/station/cargo/miningoffice) +"wCN" = ( +/obj/structure/flora/bush/ferny/style_random, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/grass, +/area/station/science/auxlab) "wCR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -71567,13 +71637,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"wDh" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "wDs" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -71597,12 +71660,6 @@ /obj/structure/flora/rock/pile/style_random, /turf/open/misc/asteroid/lowpressure, /area/space/nearstation) -"wEj" = ( -/obj/machinery/light/directional/north, -/obj/machinery/atmospherics/components/tank/air, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/maintenance/department/chapel/monastery) "wEt" = ( /turf/closed/wall/r_wall/rust, /area/station/service/bar/atrium) @@ -71642,34 +71699,6 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) -"wEJ" = ( -/obj/effect/mapping_helpers/broken_floor, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/bot, -/obj/structure/rack, -/obj/item/wrench, -/obj/item/storage/toolbox/emergency{ - pixel_y = 4 - }, -/obj/item/hand_labeler, -/obj/item/stack/package_wrap, -/obj/machinery/button/door/directional/east{ - id = "commissaryshutter"; - name = "Commissary Shutter Toggle"; - pixel_x = 26; - pixel_y = 7 - }, -/obj/machinery/button/door/directional/east{ - id = "commissarydoor"; - name = "Commissary Door Lock"; - normaldoorcontrol = 1; - pixel_x = 26; - pixel_y = -2; - specialfunctions = 4 - }, -/obj/machinery/light/small/directional/north, -/turf/open/floor/wood, -/area/station/commons/vacant_room/commissary) "wEM" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -71684,6 +71713,13 @@ /obj/machinery/atmospherics/components/tank/air/layer4, /turf/open/floor/plating, /area/station/maintenance/solars/port/fore) +"wEW" = ( +/obj/machinery/biogenerator, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/security/prison/garden) "wFa" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -71707,31 +71743,6 @@ "wFl" = ( /turf/closed/wall/r_wall, /area/station/command/heads_quarters/captain/private) -"wFm" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/siding/blue{ - dir = 4 - }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 4 - }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) -"wFs" = ( -/obj/machinery/recharge_station, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 - }, -/obj/machinery/light/directional/west, -/obj/structure/extinguisher_cabinet/directional/west, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/paramedic) "wFu" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -71761,14 +71772,6 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"wFJ" = ( -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/brig) "wFL" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -71872,17 +71875,6 @@ /obj/machinery/electrolyzer, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) -"wGQ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/kirbyplants, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/corner, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/command/bridge) "wGS" = ( /obj/structure/tank_dispenser, /obj/effect/turf_decal/tile/purple/half/contrasted{ @@ -71909,19 +71901,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/security/brig) -"wHp" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "wHr" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, @@ -71998,19 +71977,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/showroomfloor, /area/station/security/office) -"wHW" = ( -/obj/structure/closet/secure_closet/security/engine, -/obj/effect/turf_decal/delivery, -/obj/machinery/light/directional/south, -/obj/structure/reagent_dispensers/wall/peppertank/directional/south, -/obj/item/crowbar, -/obj/item/book/manual/wiki/security_space_law, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/checkpoint/engineering) "wIo" = ( /obj/machinery/atmospherics/components/unary/passive_vent{ dir = 1; @@ -72018,12 +71984,6 @@ }, /turf/open/floor/circuit/green/telecomms/mainframe, /area/station/science/server) -"wIt" = ( -/obj/item/nullrod, -/obj/structure/table/wood, -/obj/machinery/light/very_dim/directional/north, -/turf/open/floor/cult, -/area/station/service/chapel/office) "wIu" = ( /obj/item/kirbyplants/organic/plant16, /obj/machinery/duct, @@ -72107,6 +72067,22 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/treatment_center) +"wJz" = ( +/obj/effect/turf_decal/stripes/line, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/simple/dark/visible{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/bridge_pipe/cyan/hidden/layer4, +/obj/machinery/atmospherics/pipe/bridge_pipe/supply/hidden/layer4{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "wJD" = ( /obj/effect/turf_decal/bot, /obj/machinery/portable_atmospherics/canister/nitrogen, @@ -72124,17 +72100,6 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/cmo) -"wKb" = ( -/obj/structure/railing/corner, -/obj/item/kirbyplants/random, -/obj/machinery/light/directional/west, -/turf/open/floor/iron/dark, -/area/station/service/chapel) -"wKG" = ( -/obj/structure/closet/secure_closet/brig, -/obj/effect/turf_decal/tile/red/half/contrasted, -/turf/open/floor/iron/showroomfloor, -/area/station/security/execution/transfer) "wKN" = ( /obj/structure/barricade/wooden, /obj/structure/barricade/wooden/crude, @@ -72143,16 +72108,6 @@ dir = 1 }, /area/station/service/chapel) -"wLp" = ( -/obj/effect/landmark/event_spawn, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 1 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/cargo/sorting) "wLu" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/engineering{ @@ -72180,9 +72135,6 @@ /obj/machinery/duct, /turf/open/floor/plating, /area/station/engineering/atmos) -"wLX" = ( -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "wMe" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -72192,18 +72144,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"wMl" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/obj/machinery/duct, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/medbay/central) "wMt" = ( /obj/machinery/light/small/directional/east, /obj/effect/turf_decal/tile/blue/half/contrasted{ @@ -72290,20 +72230,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) -"wNQ" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/airalarm/directional/south, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow, -/obj/effect/turf_decal/tile/yellow/half/contrasted, -/obj/machinery/disposal/bin/tagger, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/pharmacy) "wNT" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -72321,6 +72247,16 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/medical) +"wOe" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/landmark/event_spawn, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/engineering/lobby) "wOk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, @@ -72341,6 +72277,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) +"wOl" = ( +/obj/effect/turf_decal/siding/white{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "wOy" = ( /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 1 @@ -72407,6 +72349,19 @@ /obj/item/gun/ballistic/automatic/battle_rifle, /turf/open/floor/iron/dark, /area/station/security/armory) +"wPS" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/railing/corner{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/light/floor, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "wPU" = ( /obj/structure/sign/warning/electric_shock/directional/east, /obj/structure/closet/radiation, @@ -72469,19 +72424,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, /turf/open/floor/iron/dark, /area/station/service/chapel/storage) -"wRj" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/highsecurity{ - name = "AI Upload" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/effect/mapping_helpers/airlock/access/all/command/ai_upload, -/turf/open/floor/engine, -/area/station/ai/upload/chamber) "wRn" = ( /obj/effect/turf_decal/box/corners, /turf/open/floor/engine, @@ -72588,20 +72530,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/engineering/atmos) -"wSM" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red/opposingcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, -/turf/open/floor/iron, -/area/station/hallway/secondary/entry) "wSU" = ( /obj/structure/table/glass, /obj/machinery/requests_console/directional/north{ @@ -72629,6 +72557,13 @@ }, /turf/open/floor/engine, /area/station/ai/satellite/interior) +"wTa" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/decal/cleanable/blood/old, +/obj/effect/mapping_helpers/burnt_floor, +/obj/structure/sign/poster/contraband/random/directional/west, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "wTe" = ( /obj/machinery/door/firedoor, /obj/effect/decal/cleanable/dirt, @@ -72647,6 +72582,18 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/department/bridge) +"wTo" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 4 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/duct, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/prison/garden) "wTx" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/decal/cleanable/dirt, @@ -72673,16 +72620,6 @@ "wTU" = ( /turf/open/floor/carpet/orange, /area/station/command/heads_quarters/qm) -"wUC" = ( -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/machinery/camera/autoname/directional/west, -/turf/open/floor/iron, -/area/station/engineering/main) "wUU" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -72724,12 +72661,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/medical/break_room) -"wVE" = ( -/obj/machinery/light/directional/south, -/obj/effect/turf_decal/tile/red/half/contrasted, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron, -/area/station/security/brig) "wVG" = ( /obj/effect/decal/cleanable/greenglow, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -72798,6 +72729,20 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"wWy" = ( +/obj/structure/table, +/obj/item/paper_bin{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/pen, +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/status_display/evac/directional/south, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/hallway/primary/starboard) "wWB" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -72818,6 +72763,9 @@ }, /turf/open/floor/iron/dark, /area/station/science/lobby) +"wWJ" = ( +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "wWQ" = ( /obj/effect/turf_decal/siding/wood/end{ dir = 4 @@ -72838,6 +72786,13 @@ /obj/structure/sign/warning/radiation, /turf/closed/wall, /area/station/maintenance/disposal/incinerator) +"wWY" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "wXa" = ( /turf/open/floor/iron/dark, /area/station/service/chapel) @@ -72872,6 +72827,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"wXP" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/maintenance/disposal/incinerator) "wXT" = ( /obj/structure/cable, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -72906,6 +72867,17 @@ /obj/effect/spawner/random/armory/shotgun, /turf/open/floor/iron/showroomfloor, /area/station/security/armory) +"wYq" = ( +/obj/machinery/door/airlock/maintenance, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental, +/obj/effect/mapping_helpers/airlock/access/any/service/janitor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/maintenance/starboard/aft) "wYx" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -72965,6 +72937,17 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/space/nearstation) +"xae" = ( +/obj/structure/table, +/obj/structure/bedsheetbin, +/obj/machinery/status_display/evac/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ + dir = 1 + }, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/dark, +/area/station/commons/toilet/restrooms) "xaf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -73041,6 +73024,20 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/showroomfloor, /area/station/science/auxlab) +"xbj" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/light_switch/directional/east, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/camera/motion/directional/south{ + c_tag = "Vault"; + name = "vault camera"; + network = list("vault") + }, +/obj/machinery/light/small/red/directional/south, +/turf/open/floor/engine, +/area/station/command/vault) "xbk" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber{ dir = 1 @@ -73087,20 +73084,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"xcf" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 10 - }, -/obj/machinery/light/small/directional/south, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/obj/machinery/atmospherics/components/unary/passive_vent{ - dir = 4; - name = "killroom vent" - }, -/turf/open/floor/circuit/telecomms, -/area/station/science/xenobiology) "xcj" = ( /obj/effect/turf_decal/siding/green, /obj/machinery/door/window/right/directional/south, @@ -73125,6 +73108,18 @@ }, /turf/open/floor/iron/dark, /area/station/security/courtroom) +"xcu" = ( +/obj/machinery/door/airlock/external{ + name = "Prison External Airlock" + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "kilo-maint-1" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/maintenance, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "xcE" = ( /obj/structure/chair/office/light{ dir = 1; @@ -73209,15 +73204,14 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel) -"xdY" = ( -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 10 +"xea" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 8 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/hallway/primary/fore) +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "xef" = ( /obj/structure/disposalpipe/junction/flip{ dir = 4 @@ -73287,6 +73281,13 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/department/electrical) +"xeL" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "xeP" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -73318,6 +73319,22 @@ /obj/effect/spawner/structure/window/reinforced/tinted, /turf/open/floor/plating, /area/station/maintenance/department/security) +"xfB" = ( +/obj/structure/closet/bombcloset, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/science/lobby) +"xfL" = ( +/obj/effect/turf_decal/box/corners{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "xfU" = ( /obj/item/circuitboard/computer/solar_control, /turf/open/misc/asteroid/airless, @@ -73331,19 +73348,12 @@ /obj/machinery/airalarm/directional/west, /turf/open/floor/iron/showroomfloor, /area/station/medical/psychology) -"xgs" = ( -/obj/effect/spawner/structure/window/reinforced, -/turf/open/floor/plating, -/area/station/security/processing) -"xgz" = ( -/obj/effect/landmark/event_spawn, -/obj/structure/cable, -/obj/structure/disposalpipe/segment{ - dir = 10 +"xgi" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 5 }, -/obj/machinery/duct, -/turf/open/floor/iron/showroomfloor, -/area/station/science/xenobiology) +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "xgC" = ( /obj/machinery/status_display/ai/directional/north, /obj/effect/turf_decal/tile/neutral{ @@ -73356,6 +73366,28 @@ dir = 1 }, /area/station/hallway/primary/central/fore) +"xgF" = ( +/obj/structure/rack, +/obj/effect/turf_decal/bot, +/obj/item/storage/medkit/o2, +/obj/item/tank/internals/emergency_oxygen, +/obj/machinery/light/small/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/item/tank/internals/emergency_oxygen, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/entry) +"xgH" = ( +/obj/machinery/airalarm/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/end{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "xgV" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/yellow/half/contrasted, @@ -73397,14 +73429,27 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"xhY" = ( -/obj/machinery/light/directional/north, -/obj/machinery/newscaster/directional/north, +"xhT" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 1 + dir = 4 }, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/chemistry) +/turf/open/floor/iron, +/area/station/hallway/primary/fore) +"xii" = ( +/obj/machinery/door/poddoor/preopen{ + id = "brigfrontdoor"; + name = "Front Security Blast Door" + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/caution/stand_clear, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/security/brig) "xin" = ( /obj/structure/table, /obj/effect/turf_decal/bot, @@ -73437,6 +73482,14 @@ }, /turf/open/floor/iron/dark, /area/station/science/genetics) +"xir" = ( +/obj/machinery/status_display/evac/directional/south, +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/machinery/light/warm/dim/directional/south, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "xiA" = ( /obj/machinery/power/apc/auto_name/directional/east, /obj/structure/cable, @@ -73447,8 +73500,15 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/sorting) -"xiC" = ( -/obj/structure/disposalpipe/segment, +"xiP" = ( +/obj/machinery/newscaster/directional/north, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/chemistry) +"xiT" = ( +/obj/effect/landmark/start/medical_doctor, /obj/effect/turf_decal/tile/blue/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/medical/medbay/central) @@ -73485,16 +73545,6 @@ /obj/machinery/newscaster/directional/east, /turf/closed/wall/r_wall, /area/station/commons/fitness/recreation) -"xjv" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/caution/stand_clear, -/obj/machinery/door/airlock/public/glass{ - name = "Chapel Hallway" - }, -/turf/open/floor/iron/dark, -/area/station/hallway/primary/fore) "xjB" = ( /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ dir = 4 @@ -73558,10 +73608,18 @@ /obj/effect/landmark/navigate_destination/minisat_access_ai, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) -"xjT" = ( -/obj/effect/turf_decal/siding/purple/corner, -/turf/closed/wall/r_wall, -/area/station/science/research) +"xjS" = ( +/obj/structure/bodycontainer/morgue{ + dir = 8 + }, +/obj/effect/turf_decal/bot_white, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/medical/morgue) "xke" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 @@ -73603,63 +73661,6 @@ }, /turf/open/floor/plating, /area/station/security/prison) -"xkP" = ( -/obj/structure/table/wood, -/obj/item/storage/photo_album{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/effect/decal/cleanable/dirt, -/obj/item/taperecorder{ - pixel_x = 5; - pixel_y = 4 - }, -/obj/item/camera, -/obj/item/pen{ - pixel_x = -7; - pixel_y = -5 - }, -/obj/machinery/light/small/directional/west, -/obj/machinery/camera/directional/west{ - c_tag = "Library Backroom"; - name = "library camera" - }, -/obj/structure/sign/painting/large/library_private{ - dir = 8; - pixel_x = -29 - }, -/obj/effect/turf_decal/tile/neutral/opposingcorners, -/turf/open/floor/iron/dark, -/area/station/service/library) -"xkQ" = ( -/obj/item/storage/medkit/regular{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/storage/medkit/brute{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/storage/medkit/brute, -/obj/item/storage/medkit/brute{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/structure/table/glass, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/door/window/left/directional/east{ - name = "First-Aid Supplies"; - req_access = list("medical") - }, -/obj/machinery/light/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/medical/storage) "xle" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -73739,6 +73740,24 @@ /obj/effect/mapping_helpers/airlock/access/all/science/research, /turf/open/floor/iron/dark, /area/station/science/auxlab) +"xmm" = ( +/obj/structure/table, +/obj/item/paper_bin{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/pen, +/obj/item/stamp/denied{ + pixel_x = 8; + pixel_y = 6 + }, +/obj/item/stamp/granted{ + pixel_x = 8; + pixel_y = 2 + }, +/obj/machinery/firealarm/directional/south, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "xmn" = ( /obj/structure/chair/pew/right{ dir = 8 @@ -73764,10 +73783,6 @@ /obj/structure/cable/layer3, /turf/open/floor/engine, /area/station/ai/satellite/chamber) -"xmD" = ( -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/showroomfloor, -/area/station/security/warden) "xmI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/trunk{ @@ -73824,6 +73839,18 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) +"xnB" = ( +/obj/machinery/portable_atmospherics/pump, +/obj/machinery/airalarm/directional/south, +/obj/effect/turf_decal/box, +/obj/machinery/atmospherics/components/unary/portables_connector/visible/layer2{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/opposingcorners{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/engineering/hallway) "xnC" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -73968,11 +73995,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"xpJ" = ( -/obj/machinery/telecomms/server/presets/supply, -/obj/machinery/light/directional/east, -/turf/open/floor/circuit/green/telecomms/mainframe, -/area/station/tcommsat/server) "xpM" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/landmark/generic_maintenance_landmark, @@ -74043,6 +74065,30 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/brig) +"xrx" = ( +/obj/structure/table/glass, +/obj/item/folder/white, +/obj/item/folder/white, +/obj/item/crowbar/red, +/obj/item/wrench, +/obj/item/storage/box/syringes{ + pixel_x = 4; + pixel_y = 6 + }, +/obj/item/storage/box/beakers{ + pixel_y = 2 + }, +/obj/machinery/airalarm/directional/east, +/obj/structure/noticeboard/directional/south, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/item/storage/pill_bottle/sepsisillin{ + pixel_x = -5; + pixel_y = -2 + }, +/turf/open/floor/iron/dark, +/area/station/medical/virology) "xrA" = ( /obj/machinery/door/airlock/external{ name = "External Airlock" @@ -74178,12 +74224,6 @@ }, /turf/closed/wall, /area/station/maintenance/starboard) -"xun" = ( -/obj/structure/table, -/obj/item/clothing/suit/costume/justice, -/obj/effect/decal/cleanable/blood/old, -/turf/open/floor/plating, -/area/station/maintenance/aft) "xuA" = ( /obj/machinery/vending/hydroseeds{ slogan_delay = 700 @@ -74225,6 +74265,23 @@ /obj/item/radio/intercom/directional/east, /turf/closed/wall, /area/station/maintenance/disposal) +"xvc" = ( +/obj/structure/window/spawner/directional/east, +/obj/structure/window/spawner/directional/south, +/obj/structure/window/spawner/directional/west, +/obj/structure/flora/bush/large/style_random{ + pixel_y = 0 + }, +/obj/structure/flora/bush/fullgrass/style_random, +/turf/open/floor/grass, +/area/station/medical/medbay/central) +"xvg" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/service/chapel) "xvj" = ( /obj/structure/flora/bush/stalky/style_random, /obj/structure/flora/bush/flowers_br/style_random, @@ -74271,14 +74328,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/science/server) -"xvy" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 1 - }, -/obj/structure/cable, -/obj/machinery/airalarm/directional/north, -/turf/open/floor/iron, -/area/station/security/prison/garden) "xvF" = ( /obj/machinery/firealarm/directional/north, /obj/machinery/door/poddoor/preopen{ @@ -74342,14 +74391,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) -"xwE" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/status_display/evac/directional/north, -/turf/open/floor/engine, -/area/station/engineering/supermatter/room) "xwJ" = ( /obj/structure/table, /obj/item/stack/sheet/iron/fifty, @@ -74384,22 +74425,19 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/office) -"xwU" = ( -/obj/structure/table, -/obj/item/storage/box/bodybags{ - pixel_x = 4; - pixel_y = 6 +"xwW" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 }, -/obj/item/storage/box/bodybags{ - pixel_y = 2 +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 8 }, -/obj/machinery/light/small/directional/east, -/obj/machinery/light_switch/directional/east, -/obj/effect/turf_decal/tile/neutral{ +/obj/effect/turf_decal/tile/purple{ dir = 1 }, -/turf/open/floor/iron/dark, -/area/station/medical/morgue) +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron/showroomfloor, +/area/station/science) "xxk" = ( /turf/closed/wall/r_wall, /area/station/command/heads_quarters/cmo) @@ -74496,25 +74534,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"xzA" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - dir = 8 - }, -/obj/machinery/portable_atmospherics/canister/anesthetic_mix, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/medical/cryo) -"xzC" = ( -/obj/effect/turf_decal/siding/wood, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/turf/open/floor/carpet/royalblue, -/area/station/service/chapel/office) "xzI" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/spawner/random/structure/closet_maintenance, @@ -74535,6 +74554,11 @@ }, /turf/open/floor/iron/dark, /area/station/medical/pharmacy) +"xzU" = ( +/obj/structure/transit_tube/horizontal, +/obj/structure/lattice, +/turf/open/space/basic, +/area/space/nearstation) "xAw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -74565,26 +74589,23 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/starboard) -"xAQ" = ( -/obj/machinery/atmospherics/components/unary/thermomachine/freezer, -/obj/structure/sign/warning/cold_temp/directional/north, -/obj/item/radio/intercom/directional/east, -/turf/open/floor/iron/showroomfloor, -/area/station/medical/cryo) "xAW" = ( /obj/structure/reagent_dispensers/watertank, /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/engineering/atmos) -"xBd" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 9 +"xBb" = ( +/obj/machinery/light_switch/directional/south, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/blue{ + dir = 8 }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) +/obj/effect/turf_decal/tile/blue/half/contrasted, +/obj/structure/table/glass, +/obj/item/mod/module/thermal_regulator, +/obj/item/mod/module/plasma_stabilizer, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/break_room) "xBi" = ( /obj/structure/cable, /obj/effect/decal/cleanable/dirt, @@ -74614,9 +74635,36 @@ /obj/structure/flora/bush/sparsegrass/style_random, /turf/open/floor/grass, /area/station/security/prison/garden) +"xBF" = ( +/obj/effect/spawner/random/structure/chair_flipped, +/turf/open/floor/plating, +/area/station/maintenance/port/lesser) "xBI" = ( /turf/closed/wall/rust, /area/station/maintenance/port/fore) +"xBT" = ( +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/structure/sink/directional/south, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/tile/yellow/half/contrasted{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/machinery/atmospherics/components/binary/pump/on/layer4{ + dir = 4; + name = "Airlock Pump"; + target_pressure = 300; + hide = 1 + }, +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 6 + }, +/turf/open/floor/iron, +/area/station/maintenance/disposal/incinerator) "xCa" = ( /obj/effect/turf_decal/siding/wood/corner, /obj/structure/disposalpipe/segment, @@ -74690,21 +74738,6 @@ }, /turf/open/floor/iron, /area/station/engineering/main) -"xCy" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/tile/yellow/half/contrasted{ - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/maintenance/disposal/incinerator) "xCz" = ( /obj/structure/rack, /obj/effect/spawner/random/techstorage/tcomms_all, @@ -74726,6 +74759,34 @@ }, /turf/open/floor/iron/dark, /area/station/security/courtroom) +"xCE" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/primary/fore) +"xCL" = ( +/obj/item/reagent_containers/cup/glass/flask/gold{ + pixel_x = 3; + pixel_y = 8 + }, +/obj/item/reagent_containers/cup/glass/bottle/rum{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass{ + pixel_x = 7; + pixel_y = 4 + }, +/obj/item/reagent_containers/cup/glass/drinkingglass/shotglass, +/obj/structure/table/wood, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/carpet/royalblack, +/area/station/command/heads_quarters/captain) "xCP" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/landmark/event_spawn, @@ -74827,6 +74888,20 @@ /obj/machinery/duct, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"xDw" = ( +/obj/machinery/chem_master, +/obj/effect/turf_decal/bot, +/obj/machinery/light_switch/directional/south, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/item/paper/crumpled{ + default_raw_text = "This isn't funny, I'm trapped on the least fun room on the station."; + name = "poorly written complaint" + }, +/turf/open/floor/iron/dark, +/area/station/science/xenobiology) "xDE" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 10 @@ -74837,12 +74912,6 @@ }, /turf/open/floor/iron/white, /area/station/security/prison/mess) -"xDG" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/item/radio/intercom/directional/north, -/turf/open/floor/iron, -/area/station/security/prison) "xDM" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -75069,18 +75138,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"xIm" = ( -/obj/machinery/door/airlock/engineering{ - name = "Emergency Storage" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/unres/delayed{ - dir = 8 - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, -/turf/open/floor/iron/dark, -/area/station/hallway/secondary/entry) "xIq" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -75179,18 +75236,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"xJn" = ( -/obj/machinery/computer/records/security{ - dir = 1 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/directional/south, -/obj/machinery/airalarm/directional/south, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/checkpoint/medical) "xJs" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible{ @@ -75221,14 +75266,6 @@ /obj/structure/table, /turf/open/floor/iron/dark, /area/station/science/research) -"xJK" = ( -/obj/item/clothing/head/helmet/toggleable/justice/escape{ - name = "justice helmet" - }, -/obj/structure/sign/poster/official/the_owl/directional/north, -/obj/effect/mapping_helpers/broken_floor, -/turf/open/floor/plating, -/area/station/maintenance/aft) "xJN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -75242,12 +75279,24 @@ /obj/machinery/duct, /turf/open/floor/iron/showroomfloor, /area/station/medical/pharmacy) +"xKc" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/plating, +/area/station/hallway/primary/aft) "xKn" = ( /obj/effect/decal/cleanable/blood/old, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/chair/stool/bar/directional/west, /turf/open/floor/carpet/green, /area/station/maintenance/port/greater) +"xKq" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "xKr" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/tile/blue{ @@ -75275,16 +75324,6 @@ "xKP" = ( /turf/closed/wall/r_wall, /area/station/maintenance/solars/port/aft) -"xKT" = ( -/obj/structure/chair/sofa/bench/right{ - dir = 4 - }, -/obj/effect/landmark/start/assistant, -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/turf/open/floor/wood, -/area/station/service/chapel/monastery) "xKW" = ( /obj/machinery/door/airlock/maintenance{ name = "Mech Bay Maintenance" @@ -75324,6 +75363,15 @@ /obj/effect/landmark/navigate_destination/bar, /turf/open/floor/iron/dark, /area/station/hallway/secondary/service) +"xLA" = ( +/obj/machinery/shower/directional/west, +/obj/effect/turf_decal/box, +/obj/structure/fluff/shower_drain, +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/main) "xLC" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -75404,18 +75452,10 @@ }, /turf/open/floor/iron/dark, /area/station/security/medical) -"xMN" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/washing_machine, -/obj/machinery/light/directional/east, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/machinery/light_switch/directional/east, -/obj/effect/turf_decal/tile/neutral/opposingcorners{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue/opposingcorners, -/turf/open/floor/iron/showroomfloor, -/area/station/commons/toilet/restrooms) +"xMZ" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron, +/area/station/hallway/secondary/entry) "xNc" = ( /turf/closed/wall/r_wall/rust, /area/station/command/heads_quarters/cmo) @@ -75508,6 +75548,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer1, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"xOl" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/tile/red/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/caution/stand_clear, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/obj/structure/cable, +/turf/open/floor/iron, +/area/station/security/processing) "xOq" = ( /obj/item/kirbyplants/organic/plant16, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -75572,12 +75623,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/cargo/miningdock) -"xQq" = ( -/obj/machinery/rnd/production/techfab/department/medical, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/stripes/box, -/turf/open/floor/iron/dark, -/area/station/medical/storage) "xQL" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, @@ -75600,26 +75645,27 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/engine, /area/station/ai/satellite/atmos) -"xRm" = ( -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/command{ - name = "Chief Engineer's Office" +"xRc" = ( +/obj/structure/filingcabinet/chestdrawer, +/obj/effect/turf_decal/bot, +/obj/machinery/firealarm/directional/south, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 }, -/obj/structure/disposalpipe/segment{ +/obj/effect/turf_decal/siding/blue{ dir = 4 }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/access/all/engineering/ce, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/ce) -"xRv" = ( -/obj/machinery/recharge_station, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted, +/obj/machinery/light/dim/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/medbay/central) +"xRy" = ( +/obj/machinery/atmospherics/pipe/smart/simple/cyan/hidden{ + dir = 10 + }, +/turf/closed/wall/r_wall, +/area/station/maintenance/disposal/incinerator) "xRB" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/blue/half/contrasted, @@ -75707,22 +75753,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) -"xSO" = ( -/obj/structure/table, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/glass/fifty, -/obj/item/storage/belt/utility, -/obj/item/clothing/head/utility/welding, -/obj/item/clothing/glasses/welding, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/airalarm/directional/west, -/obj/structure/extinguisher_cabinet/directional/north, -/obj/machinery/light/directional/north, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/iron/dark, -/area/station/science/robotics/lab) "xST" = ( /obj/item/kirbyplants/organic/plant10, /obj/effect/turf_decal/tile/blue, @@ -75744,18 +75774,6 @@ /obj/effect/turf_decal/tile/red/opposingcorners, /turf/open/floor/iron/showroomfloor, /area/station/service/bar/atrium) -"xTt" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/disposal/bin/tagger, -/obj/machinery/light/directional/south, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/commons/locker) "xTx" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/old, @@ -75817,41 +75835,6 @@ /obj/effect/mapping_helpers/airlock/access/all/command/captain, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/captain) -"xUB" = ( -/obj/item/canvas/nineteen_nineteen, -/obj/item/canvas/nineteen_nineteen, -/obj/item/canvas/nineteen_nineteen, -/obj/item/canvas/nineteen_nineteen, -/obj/structure/table/rolling, -/obj/item/storage/crayons{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/effect/spawner/random/contraband/prison, -/obj/item/storage/crayons{ - pixel_x = -6; - pixel_y = 1 - }, -/obj/item/paper_bin{ - pixel_x = 11; - pixel_y = 4 - }, -/obj/item/pen{ - pixel_x = 13; - pixel_y = 9 - }, -/obj/item/pen{ - pixel_x = 11; - pixel_y = 6 - }, -/obj/item/pen{ - pixel_x = 11; - pixel_y = 4 - }, -/obj/machinery/light/small/directional/south, -/obj/structure/cable, -/turf/open/floor/iron/grimy, -/area/station/security/prison) "xUM" = ( /obj/machinery/door/poddoor/preopen{ id = "sci_experimentor"; @@ -75867,19 +75850,6 @@ /obj/effect/decal/cleanable/blood/old, /turf/open/floor/engine, /area/station/science/auxlab) -"xUP" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/warning/vacuum/external/directional/east, -/obj/structure/disposalpipe/segment, -/obj/structure/cable, -/obj/effect/mapping_helpers/broken_floor, -/obj/machinery/atmospherics/components/unary/airlock_pump, -/obj/machinery/duct, -/turf/open/floor/plating, -/area/station/science) "xUQ" = ( /obj/structure/closet/cardboard, /obj/structure/grille/broken, @@ -75975,24 +75945,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) -"xWm" = ( -/obj/item/kirbyplants/random, -/obj/machinery/status_display/ai/directional/east, -/obj/machinery/camera/directional/north{ - c_tag = "ai_upload Turrets"; - name = "upload camera"; - network = list("aiupload") - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) -"xWq" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/obj/effect/turf_decal/box, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/hallway/primary/starboard) "xWw" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -76071,6 +76023,18 @@ }, /turf/open/floor/engine, /area/station/science/auxlab) +"xXQ" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "atmos_end_big_lad" + }, +/obj/machinery/door/airlock/external{ + name = "Atmospherics External Airlock" + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/atmos, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/plating, +/area/station/maintenance/disposal/incinerator) "xYa" = ( /obj/effect/turf_decal/bot, /obj/machinery/status_display/shuttle{ @@ -76109,6 +76073,13 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/service/bar/backroom) +"xYC" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/showroomfloor, +/area/station/security/warden) "xYE" = ( /obj/effect/turf_decal/delivery, /obj/structure/sign/warning/secure_area/directional/west, @@ -76133,6 +76104,27 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"xZb" = ( +/obj/structure/railing{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/obj/effect/turf_decal/siding/wideplating/dark/corner, +/turf/open/floor/iron/dark, +/area/station/service/chapel) +"xZc" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/ai/upload/chamber) "xZi" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -76350,13 +76342,29 @@ /obj/structure/window/spawner/directional/east, /turf/open/floor/grass, /area/station/hallway/secondary/exit/departure_lounge) -"ycd" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/computer/upload/ai/no_lock{ +"ybX" = ( +/obj/effect/turf_decal/delivery, +/obj/structure/closet/crate/silvercrate, +/obj/item/screwdriver/power, +/obj/effect/turf_decal/tile/neutral/anticorner/contrasted{ dir = 1 }, /turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) +/area/station/command/vault) +"ych" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/iron/showroomfloor, +/area/station/medical/office) "ycp" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, @@ -76386,14 +76394,6 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"ycF" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, -/area/station/security/brig) "ycL" = ( /obj/structure/disposalpipe/trunk, /obj/machinery/disposal/bin/tagger, @@ -76408,6 +76408,14 @@ }, /turf/open/floor/iron, /area/station/security/brig) +"ydk" = ( +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/tile/purple, +/obj/effect/turf_decal/tile/purple/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/starboard) "ydn" = ( /obj/structure/flora/bush/grassy/style_random, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -76425,6 +76433,17 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/storage_shared) +"ydI" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/processing) "ydV" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -76559,6 +76578,18 @@ }, /turf/open/floor/iron, /area/station/command/bridge) +"ygo" = ( +/obj/structure/table, +/obj/item/food/ready_donk/donkrange_chicken, +/obj/item/food/ready_donk/donkhiladas{ + pixel_y = 5 + }, +/obj/item/food/ready_donk/country_chicken{ + pixel_y = 10 + }, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron/dark, +/area/station/cargo/miningoffice) "ygt" = ( /obj/structure/chair{ dir = 8 @@ -76618,10 +76649,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/hallway/primary/starboard) -"yhf" = ( -/obj/structure/sign/warning, -/turf/closed/wall/r_wall, -/area/station/maintenance/port/lesser) "yhh" = ( /obj/effect/decal/cleanable/dirt, /obj/item/shard, @@ -76643,6 +76670,40 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/showroomfloor, /area/station/security/office) +"yhz" = ( +/obj/structure/table, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = 10 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = -2 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = 4 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = -8 + }, +/obj/item/clothing/under/rank/prisoner{ + pixel_x = 8; + pixel_y = 5 + }, +/obj/machinery/camera/directional/north{ + c_tag = "Prison Visitation"; + network = list("ss13","prison") + }, +/obj/effect/turf_decal/tile/red/half/contrasted, +/turf/open/floor/iron, +/area/station/security/execution/transfer) +"yhD" = ( +/obj/structure/flora/bush/lavendergrass/style_random, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/grass, +/area/station/service/chapel) "yhI" = ( /obj/effect/turf_decal/stripes/white/line{ dir = 8 @@ -76657,17 +76718,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/medical/morgue) -"yhR" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/light/directional/west, -/obj/item/radio/intercom/directional/west, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark/corner{ - dir = 1 - }, -/area/station/hallway/primary/starboard) "yhU" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -76689,6 +76739,38 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/engineering/atmos) +"yif" = ( +/obj/machinery/camera/directional/south{ + c_tag = "Aft Hallway Engineering Doors"; + name = "aft camera" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow/half/contrasted, +/obj/effect/turf_decal/tile/neutral, +/turf/open/floor/iron, +/area/station/hallway/primary/aft) +"yil" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/obj/structure/filingcabinet/employment, +/obj/item/radio/intercom/directional/south, +/obj/machinery/button/door/directional/east{ + id = "lawyer_shutters"; + name = "Law Office Shutters Toggle"; + req_access = list("lawyer") + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/neutral/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/small/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/lawoffice) "yip" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -76699,6 +76781,12 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/primary/central/fore) +"yit" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer4, +/turf/open/floor/iron, +/area/station/hallway/secondary/exit/departure_lounge) "yiF" = ( /obj/structure/table, /obj/item/paper_bin{ @@ -76823,13 +76911,6 @@ /obj/machinery/camera/autoname/directional/west, /turf/open/floor/iron/showroomfloor, /area/station/science/xenobiology) -"ykv" = ( -/obj/structure/railing/corner{ - dir = 8 - }, -/obj/machinery/light/directional/west, -/turf/open/floor/iron/dark, -/area/station/service/chapel/monastery) "ykB" = ( /obj/effect/decal/cleanable/blood/old, /obj/effect/decal/cleanable/dirt, @@ -76861,6 +76942,14 @@ /obj/effect/turf_decal/caution/stand_clear, /turf/open/floor/iron/dark, /area/station/hallway/primary/starboard) +"ykR" = ( +/obj/structure/chair, +/obj/effect/turf_decal/bot, +/obj/machinery/firealarm/directional/north, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/half/contrasted, +/turf/open/floor/iron/dark, +/area/station/hallway/secondary/exit/departure_lounge) "yld" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/red{ @@ -76898,43 +76987,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/department/crew_quarters/bar) -"ylT" = ( -/obj/structure/table, -/obj/machinery/cell_charger, -/obj/item/assembly/prox_sensor{ - desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/assembly/prox_sensor{ - desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/assembly/prox_sensor{ - desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/assembly/prox_sensor{ - desc = "Used for scanning and alerting when someone enters a certain proximity. This one is slightly shifted to the left."; - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/stock_parts/power_store/cell/high, -/obj/item/stock_parts/power_store/cell/high, -/obj/item/stock_parts/power_store/cell/high, -/obj/item/crowbar/red, -/obj/item/toy/figure/roboticist{ - pixel_x = 6 - }, -/obj/machinery/light/directional/north, -/obj/machinery/light_switch/directional/north, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/science/robotics/lab) "yma" = ( /turf/closed/wall/r_wall, /area/station/maintenance/disposal/incinerator) @@ -81980,7 +82032,7 @@ huo pbx gSc isS -xUB +oxG sAv aeU aeU @@ -82494,7 +82546,7 @@ dWG cuS kvU mTM -mRm +tSE sAv aUz aeU @@ -84046,7 +84098,7 @@ aeU aeU myp cqQ -vcY +wEW haP xhI aJM @@ -84292,7 +84344,7 @@ eJN nKO sPK sAv -xDG +pim pTC xlA mWy @@ -84302,7 +84354,7 @@ myp jYh jYh myp -xvy +cgr tAM tYe ucS @@ -84549,7 +84601,7 @@ hMC yeK sPK sAv -woG +hhi bMw xlA vhZ @@ -85074,10 +85126,10 @@ coy aeU myp agx -bHM -pOO -oxC -umA +jon +wTo +cqD +jjF myp aeu aeu @@ -85333,7 +85385,7 @@ myp jYh myp jFC -rec +ndC sYe myp aeU @@ -85577,7 +85629,7 @@ hMC hMC sPK twX -rpB +nFl kYP pKm uzK @@ -85590,9 +85642,9 @@ aeU aeU jYh tdY -ptQ -tgh -myp +lbE +dFa +jYh aeU aeU aeu @@ -85741,7 +85793,7 @@ aeU aeu sxi sxi -urM +tVL sxi tfI aeu @@ -85834,7 +85886,7 @@ sPK sPK sPK twX -miK +erk lPU tns twX @@ -85846,8 +85898,8 @@ mWy aeU aUz umr -myp jYh +myp jYh myp aeU @@ -86254,9 +86306,9 @@ aeU aeu aeu tfI -wIt +nrm iJV -nfD +jhY tfI sxi sxi @@ -86517,7 +86569,7 @@ sxi fXL rTk pah -kKa +ajR jBl rOz hMN @@ -86610,7 +86662,7 @@ lyk dHe xlA mqC -goj +lyk sAv xky sAv @@ -86779,7 +86831,7 @@ dCd hMN rQG lJR -onr +dLD jgN rOz xdc @@ -86867,10 +86919,10 @@ oCE dWG dWG saW -oWG -lmr -bKj -bEv +oXh +dFC +rjk +uWP gOG tPH aeU @@ -87025,9 +87077,9 @@ aeU aeu aeu sxi -teL +kAr itF -agc +nDk qEV fBt wFu @@ -87287,17 +87339,17 @@ kmt iKT svZ rTk -vhL +gMB ciR -xzC +iUc hMN aoX alr wcs uHL hMN -nSZ -kcy +iLd +fXP cTz weG wPq @@ -87550,7 +87602,7 @@ riW hMN vvL vvL -okr +vvL vvL hMN hMN @@ -87889,7 +87941,7 @@ wDz wDz wDz wDz -yhf +nct dWG fQO dWG @@ -88049,11 +88101,11 @@ aeu aeu aeu hmh -wEj +qel shE hJC dJi -qhZ +gIv vmZ vmZ bnn @@ -88143,11 +88195,11 @@ drG pqD kNP iza -duU -pqD -hxL +kFB +bcm wDz -wKG +mMy +iOR vmq pfO sJD @@ -88311,17 +88363,17 @@ aXf iqQ snn wXa -vmZ +dKg eXY gnh joC ehp -jIa +sZX tfW iMx gnh slr -rqG +wPS cop tiv rfg @@ -88399,12 +88451,12 @@ pqD szX iza kWA -svC -fbz -qBz -kWA +wTa +nCL +jqI wDz -ocq +wdC +iOR kPd tpd sJD @@ -88659,13 +88711,13 @@ rFD kAb wHA kWA -oIU wDz -vXn +wdC +iOR kPd hCK mMy -sIF +eUU rdo ygt ygt @@ -88820,7 +88872,7 @@ aeu aeu aeu hmh -pkg +brG kUh jAy dJi @@ -88915,9 +88967,9 @@ pqD pqD gzA rrc -rHs -bDp +oLq wDz +wdC ugy kPd mvF @@ -89077,7 +89129,7 @@ aeu aeu aeu ixB -okc +gve mLd cdi dJi @@ -89093,10 +89145,10 @@ cHg was nKj vrs -wKb +iol tiv -jju -xKT +nme +fXK xuR vSG kpS @@ -89173,14 +89225,14 @@ iza pqD qiM pqD -iza wDz -bYK -dnF +mMy +avl +bIx mvF vAa qxD -qVF +woL cXv nbO mes @@ -89344,7 +89396,7 @@ vmZ ewC qER xmn -dKm +buf ggj aSu kVd @@ -89430,17 +89482,17 @@ jes pqD yji wlP -pqD wDz -bWP -iOR +yhz +qhT +vNU mvF agy iJy awv fov -moc -pKQ +gRw +bHT rxW tPH aeU @@ -89594,7 +89646,7 @@ ixB ixB snn snn -tmj +gIv wXa prn vmZ @@ -89688,9 +89740,9 @@ qWB wRQ fiH wDz -wDz -nqR -qhT +lUG +iOR +iOR rUh mMy tPH @@ -89915,7 +89967,7 @@ mlm hBJ fvh lXK -tGg +gfi tnP wHF maA @@ -90108,7 +90160,7 @@ aeu rNx sjG tiv -srs +vak wXa nKj vmZ @@ -90121,9 +90173,9 @@ upK ocE vrs nKj -cln +jIi tiv -win +kVJ piu piu vSG @@ -90187,12 +90239,12 @@ oVz dBg oVz vjh -nRC +tnl qSJ qSJ uOo qSJ -qug +mSe iza juF iza @@ -90365,16 +90417,16 @@ gnh gnh eAk jrh -sPx +wXa uEC hUM fFa frh -ccd -slr -wXa -tnA -lHk +nPx +iCf +tDT +vFp +xZb ejt pGD pMe @@ -90422,7 +90474,7 @@ tgp leD xle mtV -ozd +mtZ nJF rZW nJF @@ -90432,7 +90484,7 @@ aux nJF rZW rZW -hjt +vHr hBJ cYy vjh @@ -90444,22 +90496,22 @@ vBi vjh vBi vmu -keO +kuE kSR thW cKR hyQ -iIP +hgS dfS wyY nhZ wgK nCL +gTg +xBF +gzZ nCL -nCL -nCL -nCL -nKy +efO wDz jQn cyT @@ -90627,14 +90679,14 @@ tiv vUi jpa cne -rsX +mcu mlD lCC mlD -rsX +bNg gJc sQq -qbP +gMh tiv sQJ dvs @@ -90712,10 +90764,10 @@ iza pqD qsy tKt -iMA -gmo -iMA -iMA +ryn +ryn +ryn +ryn ryn rQZ qQc @@ -90877,24 +90929,24 @@ aeu aeu lUq bnB -lqR +iYD rnj ecE -nGE +tvD wXa vmZ tqB -rJN +wlu dRh xlF vZn -dRh +fCf rGK vrs wXa -aoi +iXh tiv -piu +wrA smI piu vSG @@ -90970,8 +91022,8 @@ pqD bbC wgK iMA -xad -adf +gmo +iMA iza llh wDz @@ -91135,19 +91187,19 @@ aeu lUq uDe mjn -seK +bGK pOV -laH +iLD ocE -vmZ +dKg wXv -rcB +qBB lCC hZd rsX -mlD +yhD iYr -vrs +oeL lQw iLD mHf @@ -91193,7 +91245,7 @@ ycx sfw yhU mtV -vzV +hEq rZW rZW ikO @@ -91203,7 +91255,7 @@ nJF rZW nJF wmq -vHr +iIS mtV vjh sXT @@ -91232,13 +91284,13 @@ cnQ sav mMy jri -mCu +fAW bIq wjK dbu kES -xfx dbu +xfx dbu wjK cmU @@ -91391,23 +91443,23 @@ aeU bVv bbO dIR -riX -rBV +cRV +jRG pOV -ogM +qei hUM fFa jdL -hVT +mcu mlD -dRh +mdy vZn -qFs +nEB tUO pGD hUM -eIi -mHf +rbQ +tiv piu qDG piu @@ -91482,7 +91534,7 @@ bJv bJv pqD srR -qkA +kMR iza aeu acW @@ -91494,8 +91546,8 @@ kra iln wAt tlV -ezY -qCi +hyd +pTd aOf kiO acm @@ -91662,10 +91714,10 @@ siH ing eGC xRM -uAH +vJO lOG cRv -vim +uOp piu piu vSG @@ -91707,7 +91759,7 @@ vjh pVz gLH mtV -xhY +xiP rZW nJF cSJ @@ -91717,7 +91769,7 @@ rZW rZW nJF rZW -rgY +eqi mtV vjh dhe @@ -91750,7 +91802,7 @@ cTY oig wXt wAt -bLN +rKZ rkV aLg aOf @@ -91763,7 +91815,7 @@ aeU aeu aeu krY -fCN +wvw uUS aeu aeu @@ -91905,12 +91957,12 @@ aeU aeU qOd dVk -luk +btQ ecE nUf -eTS wXa -vmZ +wXa +sjS wXa siH bzu @@ -91920,7 +91972,7 @@ siH piu nbZ piu -ykv +uRK sVD cvi mYv @@ -92004,7 +92056,7 @@ cnQ mxf jri hqP -hQa +uId xrS wAt vPG @@ -92166,16 +92218,16 @@ aBB pOV qeO qeO -oxD -vmZ -vmZ +xvg +dKg +sjS kXV -cOR -aGK -buS +iVK +sBd +pzW dIy jlY -xBd +gHg ggb ikg hda @@ -92208,7 +92260,7 @@ aeu aeu aeU fzd -dLr +tWx fzd aeu aeu @@ -92228,7 +92280,7 @@ tlU gqR wYR xFE -tWn +wYR juL wYR aAP @@ -92527,11 +92579,11 @@ vvn tSN aiL bvR -ubC +ndq qRD fhL kYI -rBd +kwt nMD eGW mui @@ -92679,19 +92731,19 @@ lUq bbO bbO rNx -mZS -bAs +gIv +jEG bPZ -foB +fMA ing -fty +pIm tOH -eqD +qYe ebU -tIA +wuG yhr -csL -gPC +kbR +pJn cGP vSG vSG @@ -92736,7 +92788,7 @@ hBm kFw ukc esG -rFL +vGd nrS nsl mxh @@ -92773,7 +92825,7 @@ xad cnQ aeu mxf -krk +lax sqs mZF lGV @@ -92781,10 +92833,10 @@ jtl bUl pZM qnc -qKI +hXg mNm wNf -ubC +ndq olQ tSP cIg @@ -92993,7 +93045,7 @@ pmA tYd kCm eWP -cGo +pbt fKA tdd xlr @@ -93034,8 +93086,8 @@ ceh hHm aor akV -iQn -eJm +sog +fWg irO aiL aiL @@ -93194,7 +93246,7 @@ aeu aeu aeu gnh -oye +dgp pXi sYM rNx @@ -93217,7 +93269,7 @@ aaa aaa aaa acm -aaa +acm aaa aaa aaa @@ -93253,7 +93305,7 @@ eWP sbZ qxQ pDT -szL +mwU hBJ dBg qXJ @@ -93528,12 +93580,12 @@ iCb rGx vjh rdM -dna +gpX trf oLa xnJ trf -hHt +ggD tsn pqD nWR @@ -93714,24 +93766,24 @@ aeU aeU aaa aaa -rBj -cUc -vlM +rSk +vEC gNI -opV -aaa -acm -aaa -aaa -aaa +gNI +gNI +gNI +vEC +gNI +gNI +gNI +gNI +oFk aaa acm aaa aaa -aaa -aaa nqX -aaa +acm aaa aaa aaa @@ -93749,7 +93801,7 @@ xuZ tfY qzJ crH -nki +xBI uHY dGD bsa @@ -93765,7 +93817,7 @@ pmq mBX qlg uvT -sNR +cSj gey jEQ neJ @@ -93785,12 +93837,12 @@ dLL nVj vjh tSk -meO +idf uFp qSC lch fOv -ceM +fpn djZ pqD iza @@ -93974,22 +94026,22 @@ aaa aaa aaa aaa -acm -acm -cev -acm aaa aaa aaa aaa -acm aaa aaa aaa aaa -nqX +aaa +cev +acm aaa aaa +nqX +acm +aaa aaa aaa acm @@ -94006,7 +94058,7 @@ xuZ pCu pCu pCu -cXZ +pVz xzc ukc pVz @@ -94234,14 +94286,14 @@ aaa aaa aaa aaa -aba -acm -acm -acm -acm -acK -acm -acm +aaa +aaa +aaa +aaa +aaa +aaa +aaa +acn acm acm gNE @@ -94263,8 +94315,8 @@ xuZ pCu pCu pCu -cXZ -cXZ +pVz +pVz pVz pVz xBI @@ -94279,7 +94331,7 @@ jiN aHQ esG cgH -xkQ +fnf eUs vRU vRN @@ -94323,7 +94375,7 @@ iaY nhz qRS gPA -bvk +pqU hkA tzY vpV @@ -94492,17 +94544,17 @@ aaa aaa aaa aaa -bRh aaa aaa aaa -acm aaa aaa aaa aaa -nqX +cev aaa +nqX +acm aaa aaa aaa @@ -94565,7 +94617,7 @@ sOy pjC pqD kzl -bXS +tVD iTq kTK wDz @@ -94750,16 +94802,16 @@ aaa aaa aaa aaa -bRh aaa aaa -acm aaa aaa aaa aaa -nqX aaa +cev +nqX +acm aaa aaa aaa @@ -94800,7 +94852,7 @@ kSx ruU pyr tAx -pTc +ipn pyr vjh wFG @@ -94834,7 +94886,7 @@ pon vMb rbz iFU -urx +tfS nAa ijl gWb @@ -94845,7 +94897,7 @@ sxn dUE hwJ rqs -ilv +lwj oKW vWY frP @@ -95008,16 +95060,16 @@ aaa aaa aaa aaa -mzI aaa -acm aaa aaa aaa aaa -nqX aaa aaa +jak +acm +aaa aaa aeu aeu @@ -95038,10 +95090,10 @@ ejc jcj qVD eUN -oxp +kHN noO xfZ -tyA +lWV xxk buJ kAR @@ -95069,17 +95121,17 @@ nEf iCb oqA vjh -ukV -peJ +rQO +loJ rfX jXX mzB ova -pJK +uYZ weW iza ckJ -wuT +ujt iza pqD hCr @@ -95266,15 +95318,15 @@ aaa aaa aaa aaa -bRh -acm aaa aaa aaa aaa -nqX aaa aaa +nqX +vks +aaa aeu aeu cIV @@ -95308,9 +95360,9 @@ lUM qGK tOR pXf -xQq +dSz rHc -sxM +lmN sJM sJM rSi @@ -95318,7 +95370,7 @@ sJM sJM vXf pwF -ufU +kFY vjh mtV mtV @@ -95333,7 +95385,7 @@ mle hQg hPq dny -joS +mHh pqD fNy bMY @@ -95345,7 +95397,7 @@ cXh cXh wlS pns -wFJ +nzW nzW kZP ylu @@ -95524,13 +95576,13 @@ aaa aaa aaa aaa -acn -acm -acm -acm -acm +aaa +aaa +aaa +aaa +aaa gNE -acm +thg iAo anH aeu @@ -95559,7 +95611,7 @@ xmI xxk wSU hEI -kbH +qAr wKa tZc qGK @@ -95571,18 +95623,18 @@ qyo gbf nBZ gbd -woB +eyJ sJM -ibL +oRX cdD rnJ xLd -ubJ +lEz nWW -ngT +wAb nKC nWW -ngT +wAb xcR xDc xDc @@ -95618,7 +95670,7 @@ gFD oKW mlv ycp -etB +fPk pya lsS afL @@ -95782,12 +95834,12 @@ aaa aaa aaa aaa -mzI aaa aaa aaa -nqX aaa +nqX +mBp aaa aeu aeu @@ -95805,9 +95857,9 @@ cIV nPS brA ixi -jJJ +hNG pVP -ami +jnl eUN vsJ xFt @@ -95834,7 +95886,7 @@ qTY pwF kJH srx -ubJ +lEz mtr ecF nKC @@ -96040,11 +96092,11 @@ aaa aaa aaa aaa -bRh aaa aaa -nqX aaa +nqX +mBp aaa aeu aeu @@ -96062,16 +96114,16 @@ xOg hRg eRC ixi -oaU +jJJ ocS eyj psv hOY pqS kUp -iMr +gBk ccY -qqB +diP hsW imP edU @@ -96079,18 +96131,18 @@ ccY uMz nKV nKV -sbJ +kqL wue vFv gbf wVD wGE -ouD +xBb rSi rzB jMa suQ -raS +bTF fYw eoG cOY @@ -96102,7 +96154,7 @@ uNO uNO bDV sDD -hOH +lXj uNO osy pqD @@ -96114,7 +96166,7 @@ cXh skC cXh cXh -vVt +lCU uBM jgv rjg @@ -96298,10 +96350,10 @@ aaa aaa aaa aaa -bRh aaa -nqX aaa +nqX +mBp aaa aeu aeu @@ -96328,32 +96380,32 @@ lCa qez eUN ccY -clH +aFh huR huR bXe gZu jhv -uZT -fph -pMj -dKa +xiT +eAQ +jXU +lXT wCR sJM ybO oKJ -fth +czR sJM hpv htS rKk hpv fYw -srg +dcA jOY hYD vFc -cOt +lbz nsA lOZ iun @@ -96375,7 +96427,7 @@ crk jmw kxG rEi -flt +hFf fPQ kTe ijl @@ -96388,7 +96440,7 @@ dKK xpa oKW afL -dMH +cuB crh vwH ijd @@ -96556,9 +96608,9 @@ aaa aaa aaa aaa -mzI -nqX aaa +nqX +mBp aaa aeu aeu @@ -96576,7 +96628,7 @@ cIV ike rKa xOg -mCk +rJt ocS hms rxl @@ -96591,11 +96643,11 @@ kaT ekB ayC jhv -uCa -jjm -qCu -aOS -hDX +vuD +ixE +xvc +uCJ +oSd sJM rSi cuU @@ -96610,7 +96662,7 @@ dEF dEF dEF hQI -vpP +oPS dEF osy uNO @@ -96814,8 +96866,8 @@ aaa aaa aaa aaa -tlJ -acm +gNE +thg iAo anH aeu @@ -96833,8 +96885,8 @@ cIV dGo hgW cIU -rMJ -oRz +lvg +qnA uMG fWK fWL @@ -96848,11 +96900,11 @@ gPS buq gZu mEt -anI -son -xiC +dUm +mGi +uqS eOi -wMl +fTa lHX nBD qYv @@ -96864,10 +96916,10 @@ omi eBH tXm vvf -geO +pBV jdR hQI -qlw +bTC dEF tYi nXC @@ -96889,7 +96941,7 @@ xex mfd ciY tyk -mlj +hXa nvK yjW vMg @@ -97072,7 +97124,7 @@ aaa aaa aaa nqX -cev +mBp aaa aeu aeu @@ -97093,8 +97145,8 @@ qLO qEs ocS uMG -sFu -jIg +iQD +hHf pdI cUk fcv @@ -97106,9 +97158,9 @@ wJy lQK dea tpy -utF -wFm -dzQ +ujj +jCF +wxr qrS cdd xev @@ -97118,19 +97170,19 @@ blU gPa eHO rvR -aDo +xRc dEF -fjn +lsk hGm dEF iju unz hkp -xMN +sCx uQE qHu mqU -qEJ +xae rhN esq pqD @@ -97146,12 +97198,12 @@ wDs lmF pAQ hCd -xmD +jdd jck -dzy +nXg sCJ ocz -dZA +fhc lYX ijl abj @@ -97160,7 +97212,7 @@ nKr lvP cFO lZr -jPG +ikI sYu mIR kom @@ -97329,8 +97381,8 @@ aaa aaa aaa nqX +xzU aaa -vks aeu aeu cIV @@ -97350,10 +97402,10 @@ lCV mCk ocS hms -xAQ -eWH -okQ -xzA +sRO +klw +woT +oYE kkV ugq iCV @@ -97372,10 +97424,10 @@ owm jrs cMT fVF -vUs -lBB +oAF +kGm sRZ -cjS +rzZ dEF dEF iYf @@ -97404,7 +97456,7 @@ jmw vMg tVS esK -ctv +xYC eqK vMg jJs @@ -97586,8 +97638,8 @@ aaa aaa aaa nqX +xzU aaa -iTZ aeu aeu cIV @@ -97604,7 +97656,7 @@ cIV fSE sSP xOg -mCk +ugP bsy dkh dkh @@ -97613,10 +97665,10 @@ dkh jUU dkh ccY -aHC +uLb uko gsr -knL +vij qbk mEt rZE @@ -97660,24 +97712,24 @@ omQ jmw fEe clN -dup +gfy oFo hJp lGs xex -dSv -gjg +nGx +oXt oKW aHD lzg icQ aka -psn +fLE azo alv cez sVP -qWZ +rTL anG aeu aeu @@ -97843,8 +97895,8 @@ aaa aaa aaa nqX +xzU aaa -iTZ aeu aeu cIV @@ -97866,7 +97918,7 @@ ocS qLB phH vJg -uJs +qvs pri eMB eer @@ -97880,24 +97932,24 @@ rUP sSJ lfQ mHm -rVp +nMX rFt iIF xJN fPc -ijC +lwz pIT ofg rot -bAQ +kzZ nvH -gSC +hLs gjx jHS pOb veV hkp -lqM +faC rNo vPX dQC @@ -97917,7 +97969,7 @@ yld jmw vMg cSX -mpd +nzR lAk eKT abB @@ -97934,8 +97986,8 @@ cHL cbp alQ amr -vaP -amX +gSm +aka aeu aeu aeu @@ -98100,8 +98152,8 @@ aaa aaa aaa gNE -aaa -duV +thg +iAo anH aeu aeu @@ -98125,19 +98177,19 @@ xjI yhI eby rhs -gtK +olF eer bKU uUm oWQ -fxx +wjc aAu cTI gAO rFt lJL ozt -xJn +cxa rFt itr yaZ @@ -98145,8 +98197,8 @@ tVu aTe pcG mnG -uEX -nCU +qfv +aqu llY fGQ qZS @@ -98154,11 +98206,11 @@ egU fpx obU dEF -kzz +gCq iYa bCO uHv -nQX +qzs rhN hfF vea @@ -98357,11 +98409,11 @@ aaa aaa aaa nqX +xzU aaa -iTZ aaa -aaa -acm +anH +aeU aeu aeu aeu @@ -98375,8 +98427,8 @@ qlC pWo efG qOz -sRL -cVn +pNh +got jUU jKQ gGR @@ -98403,13 +98455,13 @@ jMU jJd ofg iYA -hih +nBl llY -lyr -oYu -aLC +vPo +iWV +hct jVf -uYJ +xrx hkp fzk nKh @@ -98437,7 +98489,7 @@ vMg lGs sJW dZA -wVE +lYX oKW cIk yhw @@ -98614,24 +98666,24 @@ aaa aaa aaa nqX -aaa uHc aaa aaa -acm -acm -acm -acm +kiO +cry +cry +cry +cry aCs cmU cmU cmU cmU -urA -duq +uni +hKW dDO efG -rdI +wux hWD tmd dkh @@ -98639,10 +98691,10 @@ uRf vxZ tLh lrc -lHy +iPG eer eer -rRZ +ych pbk qpP wzW @@ -98657,7 +98709,7 @@ mJk vbT etY nri -wNQ +ooo ofg xvW chM @@ -98693,14 +98745,14 @@ bJm qbY qbv mdp -ycF +qtS oNH kJq jUJ rFh iYq aka -vCk +cYK cup pDd cxP @@ -98872,20 +98924,20 @@ aaa aaa nqX aaa -aaa rSk -ucP -ucP +txd vEC -vEC -ucP -eFE -rvD -rvD +txd +txd +txd +txd eFE +vPc +vPc +vPc rvD -aIH -uBH +cQN +dTG ooR eTt hxf @@ -98907,7 +98959,7 @@ eYH xEm gxN tCd -uHg +cel cel vsR kHG @@ -98919,7 +98971,7 @@ eDr pBd jHo rBS -wFs +ewZ oJH xvW gbh @@ -98944,10 +98996,10 @@ icp ydh uXL aPF -tiY +gyR nzW wPK -eDx +ylu srf veu dZA @@ -98957,12 +99009,12 @@ yaE jtL sQI iPa -alP +fJy oMr cdF ceE wPO -scR +bgj aka aeu aeu @@ -99141,30 +99193,30 @@ nJV ccW ccW ccW -gLp -tFK +nnd +lvZ fSS mBW uOB uOB -glY +dqJ dkh -uTT -pYb -klN +sNc +xjS +fjG phL rvW -fXZ +gcV dkh pFY ciA krM qaR -jam +pSV fgy qht msL -jhr +qDC dQG phY uik @@ -99173,7 +99225,7 @@ erH gaE uNK qCj -lia +iqC xoK evD gZI @@ -99182,17 +99234,17 @@ sij als vnL rhN -cVI +cXz nKh nkd tDs -xTt +hRe rhN czz amn pqD ckJ -siS +aKU wDz kTK wDz @@ -99392,7 +99444,7 @@ aaa aaa aaa aaa -acm +cry acm cmU aUz @@ -99411,10 +99463,10 @@ dkh jUU dJU xef -xwU +mIi dkh fyw -vUr +vOJ xxz eer fXm @@ -99426,20 +99478,20 @@ jJW vss aeH xzS -ouK +nfF fKf fyZ qNL xvW -jDU +dlO ear qip -aDD +eHw xvW tqG cZT rhN -hXY +nSa nKh iku ikE @@ -99450,7 +99502,7 @@ rhN pqD llI ncj -cUY +xcu fGz dPR wDz @@ -99471,8 +99523,8 @@ bHv qVs nrF wSq -wmj -ojW +nce +onu sLf kbx btg @@ -99649,7 +99701,7 @@ aaa aaa aaa aaa -cmU +aeU vku aeu aeu @@ -99659,8 +99711,8 @@ aeu efG xLX efG -ogg -eFR +jzx +bBb fVJ uGc bMe @@ -99674,12 +99726,12 @@ uVJ rKT aPT aAu -mlz +hvL gOh nes tjv mYV -aBN +cGd fdY ofg aDL @@ -99720,9 +99772,9 @@ jgy fGC iVV mRp -gJL -odc -eoM +pgq +lec +xii ksM rFK qVs @@ -99917,9 +99969,9 @@ efG dHc qlC tbn -lLp -aWI eFR +aWI +fZj eFR nCA cXZ @@ -99992,7 +100044,7 @@ fQM csM rhd hmO -xgs +rnt kem acm qJs @@ -100174,19 +100226,19 @@ efG ofr efG xIS -eFR -aWI -eFR -eFR +tRk +syU +xhT +tuV nax cXZ -krg +jHW idh sby oAN gQd gQd -pAq +lBu nki uWO wWp @@ -100208,7 +100260,7 @@ qox sJY hdj qCX -rVA +hNh pZO eQE otS @@ -100239,19 +100291,19 @@ sFF war rhd lbD -hCl +leR kbV vvB wHQ fDi bpg -gBi +hGG oZE oZE -vuM -brK -xgs -xgs +tKh +fSm +rnt +rnt qkf aaa aaa @@ -100431,12 +100483,12 @@ loI uDt qlC fSY -eFR -xdY -vxf -jTt +oSq +vun +grZ +xCE oLs -euc +qzq hHL dUQ aSX @@ -100449,7 +100501,7 @@ uQI bHB gBJ jLK -fjX +fUj vpY lrI ryW @@ -100506,7 +100558,7 @@ rhd oZE awi sMe -lRJ +eOe fJs lIr rRb @@ -100688,18 +100740,18 @@ efG uDt qlC sLD +bBb eFR -dfV -not -fcM -dxB -xjv +qqS +woi +tIJ +aaV nvE +lSd mog mog mog -mog -mog +lSd bSM rER vzZ @@ -100716,11 +100768,11 @@ rky rky ksr rbk -azg +rky gtq oZX igR -vUu +waw pJz fvX tFb @@ -100757,16 +100809,16 @@ ttn psP igC hmO -ntR +rms iYS -xgs +rnt fTQ bum hKc -jvl -xgs -xgs -xgs +faY +rnt +rnt +rnt aaa aaa aaa @@ -100947,10 +100999,10 @@ qlC qlC uqN vTD -wdD -kEz +wBb +sQd kMs -fmQ +nkQ huc vxb kEz @@ -101014,13 +101066,13 @@ nVK fWX boE kgE -qwi -iZv +mOC +lwM gpy -fiR -uap -lnU -riB +ydI +oYp +dUg +aCN hmO acm qJs @@ -101208,13 +101260,13 @@ bJM qlC efG qlC -uZq +wOl pkE nEV pkE afB nEV -wjG +nEV ovv xjO yeo @@ -101225,10 +101277,10 @@ fIx raY jzh wxj -otF +rjT diZ bDF -dtD +aUE plX fFW qhC @@ -101267,20 +101319,20 @@ gXJ vgU coz gVv -ePe +vgZ wmr lyd tjE gmG gmG gmG -sow +bya mbP sgA -mGG -xgs -xgs -xgs +puL +rnt +rnt +rnt aaa aaa aaa @@ -101523,18 +101575,18 @@ sMa nVK nXW ekM -vJk -mCm +dkE +xKc gmG gmG pDM -xJK -qlD -gtU -nZE -tOB +boj +fiC +wxX +qrK +kba sgA -lzs +xOl gOq lIr fdC @@ -101739,10 +101791,10 @@ wqF usV aPR gnN -ixN +rIt fST hee -oTt +iZN plX ttt kQH @@ -101780,20 +101832,20 @@ ayY nVK wWm kIz -wgr -nFe -cmc -nPT -gmn -nPT -xun +oOo +fmK +bLu +pXR +nFw +pGc +ffT tjE tOa xPc -cRG -rIq -xgs -xgs +tHf +cWp +rnt +rnt qkf aaa aaa @@ -102018,8 +102070,8 @@ gFO cvm aeG xCq -izM -rJJ +sip +oQh rFx qhf hKB @@ -102248,14 +102300,14 @@ nIx maW sLb lbO -jcN +rVu oKV avy iFI -pQd +lzA lbO mHe -xkP +gsR dcr jCm cYZ @@ -102268,11 +102320,11 @@ oUc qHV xHn jCm -qnL +gID spE -wmM +nxX ojs -iIX +yil rhT jQE uVx @@ -102567,12 +102619,12 @@ qmZ iMX iMX rTi -aaa -aaa -aaa -aaa +acm +acm +acm +acm kCk -qqJ +eLf krr ntH gcy @@ -102800,12 +102852,12 @@ gHD jqb vCG rbH -ppy +wxH gdP uNI bnY aIl -suo +jVW lYT gmG gmG @@ -102826,15 +102878,15 @@ gTD rTi acm fsu -bzb +acK kCk kCk -dyC -qAx -wzY -tZM -qoi -xCy +xBT +mUV +dXh +qTO +anw +ndj ouE tnC jCP @@ -103009,9 +103061,9 @@ qfu qlC qlC hTK -ufc -fwM -spV +jig +wbF +uOg waI rEV eBx @@ -103085,13 +103137,13 @@ acm osW cWN mLN -bzy -vhi +nqn +asc mcs eNV fmA fEb -jyS +teg brE wzr lNp @@ -103276,10 +103328,10 @@ wkC veW sLb lxP -vJT -dZe +gxS +qUm fcX -hAF +rGr lxP pwx ebK @@ -103295,7 +103347,7 @@ sbX qwx wGp jCm -jiT +poC mNT uXe xMC @@ -103340,15 +103392,15 @@ uhp rTi acm osW -vqj -bjJ -isO -ePm +mkj +oOW +jdi +aRy bfA fdp mLN mLN -nmo +uYx gxi qmR tAN @@ -103599,13 +103651,13 @@ tvH osW uvO bjJ -nqn -nDX -baS -vCV -dgd -ekp -dtb +gwr +vJM +wJz +wXP +xXQ +lvw +uYx xWT yma yma @@ -103786,7 +103838,7 @@ tPD tPD uAS efG -aDO +nbU cQV lnD lxP @@ -103807,7 +103859,7 @@ vcL cXT niy nOp -lre +qXw vne jPB vCO @@ -103856,18 +103908,18 @@ wRU acF aWF kCk -eyx -sQt +ioW +cRQ bfA drk mLN -ckg -dtb +pBh +xRy laf -pOG +rgl wRr nim -cMV +dJt yma aaa aaa @@ -104006,7 +104058,7 @@ ceG ceF xbZ twg -dHw +iGQ caN ceG ceF @@ -104037,13 +104089,13 @@ sME dja tPD uxN -ifu -hjD +vfW +rmt nSq tPD tPD efG -tCY +qoN pVd gwV okN @@ -104097,7 +104149,7 @@ mRV rrO cfL ajo -wiR +vBl hmK mJP fVl @@ -104107,18 +104159,18 @@ kbw qoo sfV cEu -dCX -eBi -vWC -hxi -vJZ -vNB -eEz -bwc +eQW +jKU +fRC +lqO +kSJ +gVy +mSs +vAG clL qOG mLN -acK +ckg yma tur yma @@ -104260,7 +104312,7 @@ asZ asZ aAg ceF -ciT +gJw sVj pfM pYO @@ -104282,7 +104334,7 @@ aeS cHu cHu iyr -bWI +iID pnK bbl naY @@ -104297,9 +104349,9 @@ rLy nIq qlk mfD -gkb +iTF pvb -jdY +gIe wor yip nwE @@ -104308,14 +104360,14 @@ tmV jlw fOd fBF -sJd +bOm ovw kqG eCd hwZ wqd ugG -fWB +hob rNX eJc sVh @@ -104366,13 +104418,13 @@ bSw kHq bWx nGM -cNP +ohe lsf vQt mLN -cud -fLu -oGO +vLU +mlK +eEU mLN mLN acK @@ -104522,7 +104574,7 @@ auf xQU nAe fpk -peu +pZF chO iyi ceF @@ -104550,7 +104602,7 @@ aDQ aDQ acW uxN -jqt +dVn skB taM hjE @@ -104596,7 +104648,7 @@ mbp fNe bVR uPM -suo +jVW iHp vWH fZk @@ -104610,7 +104662,7 @@ pDM gmG qDI cfL -irG +gYq utk cLu vbx @@ -104623,12 +104675,12 @@ aAQ ouX nDi fqO -unr +sND sni xsn bjJ mSm -vEt +bCe gsB oOW aaa @@ -104765,13 +104817,13 @@ aeu asZ asZ wBs -raM +dfc qUO alz ceg bGE cdf -pIy +eCH asZ adg azv @@ -104811,7 +104863,7 @@ vKc xRE pDF jQW -qXc +xbj itn cXH gIz @@ -104833,21 +104885,21 @@ wzG xBi luK eCd -fNG +fAk aPK lRd woj qmx twV -mAf +lON nby mwy -wzk +lln hxQ lrO cVH pxE -olN +slM ufE tmZ efe @@ -104882,7 +104934,7 @@ pED cSU dxq jkB -hkW +kCR kCk kCk kCk @@ -105037,7 +105089,7 @@ tqQ adg eXj yiF -iGD +gdf lTc mMj uba @@ -105065,9 +105117,9 @@ bjR aDQ uxN itn -jRQ +oIf wFL -eBZ +ybX uxN uxN qNr @@ -105092,7 +105144,7 @@ bnl sVh xkj sdd -mRo +eMg fqP qPX lLv @@ -105129,7 +105181,7 @@ bDP inE fwf hNy -pkr +uNy nxY wuK aLP @@ -105290,7 +105342,7 @@ xIq adg byD cgJ -kBU +nMh alV uIs afw @@ -105332,7 +105384,7 @@ hdy vuj maW wjD -fNT +oPN ijQ dCM vSi @@ -105340,7 +105392,7 @@ lMf fCs lMf qJf -hja +sZC wEt add rnG @@ -105539,7 +105591,7 @@ aay iBD cdY jIX -vFk +bST ceq atB xmB @@ -105592,7 +105644,7 @@ tEE mfJ bqd sHm -ddp +wgy ddp vRO rsb @@ -105613,7 +105665,7 @@ kYG oBy ncT cEr -dFA +tVo nGJ soC cHI @@ -105624,7 +105676,7 @@ dJg hyl rsw hAJ -hJD +vKC vbP rIk mBm @@ -105842,14 +105894,14 @@ dwc qNr qNr jYx -uwT +sMk eLp ukU dYy fzL yej ijQ -naG +rPR hJQ gpS bMN @@ -105881,14 +105933,14 @@ tmZ aQs rsw hAJ -kIf +hEh rIk seU htZ bQE uUk giA -nfN +bZS mjh xhH uUO @@ -106065,7 +106117,7 @@ myg adg qfq apx -irE +bnS ill aSa gMy @@ -106129,10 +106181,10 @@ oBe cEr wgS nTr -oJq +gKS qNJ bgl -dbi +pCp rqi kNK hyl @@ -106142,7 +106194,7 @@ xCz rIk rIk viM -ifv +owl dHq nbH nwh @@ -106307,13 +106359,13 @@ aeu asZ aAg roV -kbf +qrj lLi abD abM poZ pKt -egV +vVW asZ alV azv @@ -106348,7 +106400,7 @@ tPD qZx klm pac -bNv +stU bxx oTw oZP @@ -106368,7 +106420,7 @@ clQ piB xSU kju -wiz +vbV mZW kLy dyK @@ -106399,7 +106451,7 @@ gBT hIS rIk iqd -mGz +etH cfL cfL mjh @@ -106643,8 +106695,8 @@ wtz gHE bdo wHR -vrD -smq +pNG +hyp pik dfi vfb @@ -106869,13 +106921,13 @@ rWg rsT orT aIq -suF +dVE tGZ wLu mlw eDT vJH -bji +myt ijQ iRt hVU @@ -106895,14 +106947,14 @@ izt xjr hGf wZW -jNe +fLi roT dOg eyU ipC -ipC -ipC -wGQ +say +qiT +kXA eyU tWL xEV @@ -107090,7 +107142,7 @@ rZN ihg xYa mAj -mPo +sZR pwa ihg ihg @@ -107116,7 +107168,7 @@ qlC yaN efG tPD -sLU +gZO uWU nYp mLJ @@ -107129,7 +107181,7 @@ nms uMf rLh vqw -oqu +tyY eAn tjA xoT @@ -107154,12 +107206,12 @@ hGf bXj fqG tZk -lGF +wqP xPO vRP bqy qoT -vJB +dqq dLh tWL tmZ @@ -107396,7 +107448,7 @@ gTz jDy tDW faZ -cYG +vNo mZW sIf dyK @@ -107424,10 +107476,10 @@ hyl qvG kTq kxH -rAr +mgU rFg xYE -eqV +jdB qHe csW olP @@ -107890,14 +107942,14 @@ lZi lZi lae kLR -xpJ +tRq hQP tdt fzJ shk orT aQB -mfU +vzu vqw vqw imo @@ -107905,7 +107957,7 @@ tEE fzL nTG ijQ -mfT +iNH qOB vYI lYE @@ -107964,7 +108016,7 @@ jYc tNB cUA tTp -gZm +wky aeL eQf fOH @@ -108162,7 +108214,7 @@ wqE wuh tzl ijQ -gHi +hpD jLq shP evs @@ -108183,7 +108235,7 @@ mzp cqO bZt cEr -nHv +bYT efP exz wfk @@ -108210,7 +108262,7 @@ giU nny hkK wRU -bcQ +nwu nhO dof pvu @@ -108416,7 +108468,7 @@ gba cDH maW cWl -dqa +srB dCM ijQ wEI @@ -108424,7 +108476,7 @@ lJd vbo lJd hNL -fUP +slz wEt wqu tXg @@ -108454,14 +108506,14 @@ kTq rDe whm iSG -pcO -fkB +hZU +cAk qsb kDy cfL kUB gTc -gtR +eva pNZ mSM kZU @@ -108649,9 +108701,9 @@ kjS cJk oQg ajZ -brW -dLy -oRA +vFe +wCN +iqO uvq iUT lRk @@ -108680,7 +108732,7 @@ sde hMf pvf bln -qUj +pZd bDr dvk niQ @@ -108696,12 +108748,12 @@ scg vuF eib eDg -eRD +fmv bBS pOd pjm kOJ -lfU +orI iuP nnV mmF @@ -108716,15 +108768,15 @@ qsb qsb wpI mjh -mCE +dYH nZF eQj wbH sIx vaz -oYd +tMo wRU -vly +oiR jbg iNm mZb @@ -108740,7 +108792,7 @@ tKi vfO xLM vVP -dlS +hEV hEV gva aeu @@ -108901,7 +108953,7 @@ aeu aeu aeu oQg -rmU +icm dDH sCu oQg @@ -108924,7 +108976,7 @@ gyx vXa exN byy -jKt +gAd sBz hEa sIq @@ -108968,7 +109020,7 @@ sPP tGQ mMz rkT -uSk +eUy hrY luO lig @@ -108997,7 +109049,7 @@ uAE bEa vZy qaZ -hEV +dlS hEV gva aeu @@ -109206,7 +109258,7 @@ ygj wMt gRP lpt -lTZ +sXo bvA ssr vSa @@ -109222,7 +109274,7 @@ tJX hyl nbR uPM -suo +jVW iHp nVK nVK @@ -109418,7 +109470,7 @@ gNz owq vOL rMA -klB +jHP nmu jRy rMA @@ -109435,7 +109487,7 @@ wpf aVn fvU vOM -gxe +pWx sAZ mLb puB @@ -109449,7 +109501,7 @@ qBC fxP nSh pFS -elD +nbG vAw bXz ekL @@ -109463,7 +109515,7 @@ dvk wFl dKJ wFl -swA +xCL lBj qaF xUk @@ -109485,7 +109537,7 @@ irM dEl prP rFi -sSg +goz aWD aWD rWT @@ -109728,7 +109780,7 @@ bJR dmD tqz fxd -civ +vSU rNZ fLy oYi @@ -109737,7 +109789,7 @@ nfa nJA ekM ekM -rtT +byd exH ekM ofq @@ -109745,7 +109797,7 @@ jhg xoh qRv mGj -mSu +aWw ohj fvQ mVo @@ -109982,11 +110034,11 @@ lrk cau otP otP -bTe +iQh jlh lme -soL -tRL +urv +sOJ hfp mXI fNe @@ -110012,7 +110064,7 @@ nOg wcg bVp tgd -wHW +ccw jBs sXQ fVB @@ -110186,7 +110238,7 @@ aeu aeu aeu oQg -mDQ +dNT xXG xXG xXG @@ -110194,7 +110246,7 @@ kuc nqs xnO qDA -bht +aCg iUT swW lda @@ -110219,7 +110271,7 @@ ftG wRs pLB hxw -irL +fyh bXz hdm dMp @@ -110238,7 +110290,7 @@ yeA fiQ jfV rxt -vrf +sBb fJv jlh aVx @@ -110249,21 +110301,21 @@ ixo gWw jhz scX -deG +bKH gkr pmu wRE gng -xsJ +pRc jhg xAw bSX ddI jMo -hpz +rTm hTB tGz -vMk +dgG ipf jYd usp @@ -110445,7 +110497,7 @@ aeu oQg lBN vAm -huj +vae tIM vIK nqs @@ -110466,7 +110518,7 @@ iJf npi vvd luQ -uLA +hQd lby gsu sIq @@ -110481,7 +110533,7 @@ bXz bXz wIL ffv -maX +dKB dyX xPk laE @@ -110700,9 +110752,9 @@ aeU aeu aeu gNz -nwW +otn fiW -iyI +vSW fiW ksN nqs @@ -110733,7 +110785,7 @@ nol lox ocZ oFC -lbj +eBm ipo wxq wxq @@ -110747,13 +110799,13 @@ kbc niQ ejQ yaG -kEm +lIA cTh -gjc -sHh -kvo -wfD -pmI +xgH +vIV +nLt +hhJ +wiK yaG nuL aGZ @@ -110778,7 +110830,7 @@ cjl rlx uFb vZu -mDM +xnB fSt icw tKS @@ -110791,7 +110843,7 @@ dQK ekm ekm oUj -mBL +hTo pSW ocx gHd @@ -110976,7 +111028,7 @@ hqS bot ttB lRk -kll +feW ejU jLT pqm @@ -110993,24 +111045,24 @@ xTI uGD ejP fUZ -hJm +aqx qSa eeb gTA jHT -mzs +qQR xPk ycr niQ dch slo dRY -lzv -reT -vRQ -vRh -bNM -ioK +guq +vDK +xea +fRm +uLL +avx slo jTk aGZ @@ -111020,14 +111072,14 @@ nNM nam bax bax -eoT +tIX qgE rOS eQq scX xsJ lRl -wjz +yif xOI xOI qkm @@ -111221,7 +111273,7 @@ otr aDF tAI ctx -ipQ +eDR oQg vhf lVe @@ -111262,12 +111314,12 @@ niQ kPt slo cfK -vcm -hHP +kHt +anE ftl uiE crR -xRv +pMz slo oUA skM @@ -111286,7 +111338,7 @@ rXv nVK pRw rwD -ixj +iXo ggF ils qsA @@ -111294,7 +111346,7 @@ aYW hmr hnD kUS -kyT +qtO fqN smn ceW @@ -111472,7 +111524,7 @@ aeu aeu aeu gNz -vBv +gML ake reJ ckp @@ -111494,7 +111546,7 @@ uFl qZl wwv jMZ -eYw +mxT lby dEc umq @@ -111518,13 +111570,13 @@ urE niQ cSq slo -fjk -wLX -reT -lLl -wDh -ssg -ycd +prc +xgi +nyV +drX +mCG +xZc +avx slo iqk skM @@ -111547,11 +111599,11 @@ pwo hZT lNK lNK -rvU +fwd pLu -lbq +nlT kUS -qOi +gso feD ydw mmE @@ -111559,19 +111611,19 @@ dLN oDG tCp qOq -cqc +qZC lOX lcn jIt fXQ okp -cgb +aiR sCV fgH kZY dES uTY -bKM +ofu ekm aeu aeu @@ -111732,10 +111784,10 @@ oQg ugA cJh otr -lGS +pTU cJv ahJ -pLV +vmV oQg mbW lVe @@ -111769,19 +111821,19 @@ wGA xvj aMe oNk -kbT +txB sWI jCZ niQ abJ yaG -xWm +hLY eON -jRF -dsC -aAq -nDK -vPz +dqk +mMu +xeL +gGD +gTT yaG acm skM @@ -111794,17 +111846,17 @@ vwe ffC dmp sZB -kvJ +vYQ bax oCt fmn aek xOI -hON -wcX -mez -die -hwN +sYx +mKe +nyA +lJM +pOZ oBP ocQ gTr @@ -111828,7 +111880,7 @@ rea tiz jnL tiz -tiz +dqv ekm aeu aeu @@ -112003,12 +112055,12 @@ vHh lRk enP lZi -xSO +eQh vTu dRR tmf qpw -lvV +mIC bBF jdE sIq @@ -112036,7 +112088,7 @@ tXg tXg arl arl -wRj +fQY arl arl arl @@ -112058,10 +112110,10 @@ kYU cuV xOI lrU -sHk -tlu -ojj -kGc +cSZ +sgK +meR +wOe mPP vww vHH @@ -112080,7 +112132,7 @@ qnH vME lOX paN -hNj +dOA piD uyK uyK @@ -112239,23 +112291,23 @@ aca aji tfi udA -xUP +ksu lRZ iJu uFM qAa -oTE -iJQ +xwW +sjK kri hBD eeQ -eMX +orW lVe -pPj +uSd eIM -fRS -gfD -ggI +sPg +hTn +gOP dGP xBs qCI @@ -112283,7 +112335,7 @@ sMi eYd fmV pqu -pjX +wtM sWI mza tnc @@ -112291,8 +112343,8 @@ vwQ tXg rQs tXg -eIP -wwj +wbG +tMC gii arl arl @@ -112303,7 +112355,7 @@ xuM qzv qur iNi -jmE +wWy ioQ mQR elz @@ -112315,7 +112367,7 @@ sXJ alL jjs nPw -piN +hrg xDj eHc jzk @@ -112506,10 +112558,10 @@ joF olL soI pAx -umG +oJW jcp ikH -kat +ceY qNb jKj iUT @@ -112550,7 +112602,7 @@ tXg tXg arl arl -ipA +nqF arl jMF xuM @@ -112572,7 +112624,7 @@ qZX qZX qZX sxF -xRm +tMB sxF fvC vfd @@ -112770,11 +112822,11 @@ uRT nhy vEW lZi -cPu +vHW fJq vnH eJq -mVL +hbI dBy rlT vnZ @@ -112802,7 +112854,7 @@ nYP jiS eeo dke -yhR +lHt xWK nwI fIR @@ -112829,14 +112881,14 @@ qZX kDg chH ctg -dUY +prC cYk pIO evb tNW oCo wnl -wUC +mVr eOW aGR eEZ @@ -113025,7 +113077,7 @@ dxu vuI cIY cbL -qWJ +aBk lZi uDT nyN @@ -113083,15 +113135,15 @@ cuV oQk aAU jjs -cwj +tmx cLx jNM -ueJ +oxJ tav snU oDh dJw -jgt +rAt kmo kmo dGX @@ -113108,7 +113160,7 @@ mno idk lOX obh -hNj +dOA piD uyK uyK @@ -113271,7 +113323,7 @@ qmW cKd wpw ieb -xcf +vXz wpw vhx nQx @@ -113340,15 +113392,15 @@ cuV cuV bhJ jjs -tzH +cxB sXC sdp -hRa +onn cEa snU uso -afH -gII +udd +pLy sCr hur gKR @@ -113370,7 +113422,7 @@ rea vRo vRo vRo -vRo +mQs ekm aeu aeu @@ -113541,18 +113593,18 @@ uRT dBV vEW lZi -aYX +epi uJP moG bBF -ylT +nuy lWg gpP bLf mii vqN bBF -oAO +veY maj aUn ayh @@ -113563,7 +113615,7 @@ lBk wyF prq vao -qpJ +ydk fqq tho vTx @@ -113573,10 +113625,10 @@ lrR fkw hDK ege -fak +eKa ykP xcV -gUK +eOV kUN kvL bCz @@ -113586,7 +113638,7 @@ xNe gtC xNe qEf -qIv +jsR ykP vWF sqK @@ -113598,36 +113650,36 @@ cuV pgG qZX xjl -vqg -gYS -eYE -dxU -pqF -dpR -eto +clM +syA +wnv +tuS +bfh +iiG +kAg uMk -pGE -doU -kaM +ulU +xLA +jLs tyI bjb rea mSC naQ ksp -oAk +hyk lOX lcn jIt lcn lOX -xwE +dSo oow rYh hVH dES rKV -gyM +cpW twN aeu aeu @@ -113779,7 +113831,7 @@ kMx gqn elS wuc -jJD +lrM oVc mwp lth @@ -113800,7 +113852,7 @@ mTk lZi lko oHP -xjT +dAx bBF gkv lYg @@ -114038,7 +114090,7 @@ gqn vJc lev gSQ -riN +enU bEd pig prr @@ -114050,15 +114102,15 @@ ppB syu gqk okt -qVc +xDw uRT rAO hZP lZi -tFq +hAT kwV edj -wsy +hUn xJB sJU mDj @@ -114088,22 +114140,22 @@ oNO tQI xlf liB -cBc +sHo hKV cIW liB -lpf +kvE cox eLU pRu rEE -upa +jDv xNe ghO -jJB -tuO +kCi +lIf vzy -vyE +kfx toE bXf aVC @@ -114122,8 +114174,8 @@ kmo vKj uvm amq -lDb hjj +raT bGj vOq rKn @@ -114132,7 +114184,7 @@ rfm pbL pbL ifB -cfs +umC qai rIm lRS @@ -114323,7 +114375,7 @@ tfV lJU qDZ gGf -teE +oTQ hsg nxj dLA @@ -114333,10 +114385,10 @@ kzK mhP hhE pwy -mGS +dLg lDu vMo -pqh +iAf vOX vOO gsO @@ -114350,8 +114402,8 @@ jEy jOi hJa sAR -wLp -mPZ +klA +dhG gjP iCK pFw @@ -114362,7 +114414,7 @@ jGW lIR jHm mbY -oAS +aqE vzy kSp cuV @@ -114374,8 +114426,8 @@ cAq pQC wHr qZX -kHs -rbp +spU +tCo nan uvm vLG @@ -114555,14 +114607,14 @@ vse jmM mQe jec -oJm +gbx kkP nKQ -oJm +gbx mQe dJk nqE -xgz +uYg xHR nPl uYb @@ -114578,11 +114630,11 @@ hZr uYP tNe dkM -eog +ufm iUm eog eog -riC +eDw meP rhK uAh @@ -114603,7 +114655,7 @@ xoO fGI rfF sKy -vRl +rUZ nnA qAn qJi @@ -114821,7 +114873,7 @@ ecf mPE oiu peF -mAH +okE lev cHZ jdp @@ -114833,7 +114885,7 @@ dJm bUx cKY mDj -vGY +bwF eHI eHI eHI @@ -114842,7 +114894,7 @@ fsj plp aYz lZn -gwO +tlt geE wqQ rwR @@ -114853,10 +114905,10 @@ nOJ bls hnl iuE -peY +sUj dcq ulm -rpD +tUA jhD liB nqg @@ -114868,7 +114920,7 @@ ebF xiA tmq rmK -cHG +omr xNe eUk kTk @@ -114882,7 +114934,7 @@ vxN cuV mZz fuB -qED +hIp jTm gDM jxy @@ -115066,11 +115118,11 @@ gqn vJc lev nXp -opE +jdu kDe dmA hvl -mOt +lvR wpw gcp puJ @@ -115106,13 +115158,13 @@ qzA knu lNP oLH -tPN -uoA +loE +mUH rZV erl pnl idM -aau +dRG aEC vWA dyj @@ -115127,7 +115179,7 @@ xNe fKx uIx ihU -dpT +jzu vWv oGV vzy @@ -115139,7 +115191,7 @@ vzy fuB cuV cuV -ehc +wYq cuV fuB cuV @@ -115321,7 +115373,7 @@ vPW xRZ uwY vJc -qld +oxn ulZ iSS uLt @@ -115346,7 +115398,7 @@ xvr kkT gtW sBl -ltQ +swC imS imS imS @@ -115379,12 +115431,12 @@ aII sAr wyv cON -rup +dBX xNe ihU gtC ihU -kWm +aHM bHl rVj oSI @@ -115396,7 +115448,7 @@ nPV jSJ cvM rgv -aqe +iJL xGa uAb keE @@ -115610,7 +115662,7 @@ hhc imS cka jeU -plp +rxb lKk aND aND @@ -115622,14 +115674,14 @@ frO frO dJS vOX -jGg +ftJ iNL aru wNb atu tXr jfc -bfI +thT vUH gnB qxC @@ -115647,17 +115699,17 @@ bHl nHy asU bHl -xWq bHl bHl -gRq -nyw -eCf -fgu -djQ -atc -gBp -tXB +bHl +lNd +oHB +bly +sjp +ceO +naO +uNR +pxl hGX mkk iVL @@ -115860,7 +115912,7 @@ roF pMT gtW hTb -bQn +pPM vTX mtq tCh @@ -115885,13 +115937,13 @@ wwC kZw gHI cTX -lJD +wyS eKk lJD eKk lJD oQL -lJD +wyS fLo nFy hfr @@ -115905,16 +115957,16 @@ sNa fur gel gCl -iyM -rDj -tZa +qca +fgx +hMY mkk -exV -ozV -fFs +deF +xgF +jhE rCi lxo -dZR +pEp itC rCi iVL @@ -115945,7 +115997,7 @@ cAp auR mCV rNR -pKB +hxF xmQ gny aeU @@ -116136,7 +116188,7 @@ uhx oWm wtt vOX -kXp +mPB qFV nTz dXJ @@ -116162,7 +116214,7 @@ cQR cQR pIl pIl -mbE +gIJ eHY nXu eLm @@ -116171,7 +116223,7 @@ eLm eLm eLm mkk -xIm +jNq cvM mkk rCi @@ -116370,15 +116422,15 @@ syF mKC lDu wZZ -ubU +blm wIo gtW iAC fOu iRR -hZi +fqu rwy -feZ +obv wzI uZR pmD @@ -116393,7 +116445,7 @@ frO frO wtt rZV -pKv +kgU wTU cRO sGF @@ -116410,7 +116462,7 @@ iko euX tOy akD -shW +kWN bAS vUp byb @@ -116418,8 +116470,8 @@ ozz uAc cQR lPb -eZL -hIm +sCy +iNz cPZ vJh iWL @@ -116427,9 +116479,9 @@ hSy ssi qjq eLm -fiZ -wHp -wSM +uRv +nYg +pjx tbE rFN gJj @@ -116637,13 +116689,13 @@ aeJ egr fxQ wzI -giG -hpu +xfB +afY bWZ frO tbu eLV -lPE +iLI nkV xio frO @@ -116668,7 +116720,7 @@ nFy tbS pHE bmX -dwh +clo cQR jBC pdZ @@ -116676,7 +116728,7 @@ ecl wJn lFB cQc -wyh +vzR nXu ttZ lmi @@ -116684,8 +116736,8 @@ aXA gNM pvF tCQ -slC -lhx +wWJ +hYr owX lAB lAB @@ -116932,8 +116984,8 @@ hDa tNa cQR rDh -lux -hEQ +lza +wWY beo qtA mrI @@ -117158,25 +117210,25 @@ cqr fyq tXS vls -gBd +dOa xxN lDu nIg dME -oJa +ixR boO oiK mvm ncQ clV jwu -mIe +fmY qWh gSp xVK kxu npx -rqp +iXL lgC pWC jnp @@ -117190,7 +117242,7 @@ cQR eMo pIl wRO -mbE +gIJ iSw nXu eLm @@ -117198,7 +117250,7 @@ dkL tCQ eLm eLm -dnl +obO plx wGe izU @@ -117430,12 +117482,12 @@ dME tiK dWM oAJ -sPV +bGa eiI dok kZq nFT -tsD +gQM tOy pLe yaF @@ -117447,7 +117499,7 @@ wpn cLG jwn lFC -oPf +lja btb apw hBf @@ -117457,7 +117509,7 @@ rCi qUb hUL pBR -pZj +gIC nuh lAB bPX @@ -117660,20 +117712,20 @@ ucG mrt khy lDu -dIv +vqf wdu blZ jiO tQM gao -tNC +ugx sXG rtw hRG ceT sgn uuW -uuW +hzx oLH wCY cTN @@ -117682,7 +117734,7 @@ qTJ chT oCC wrY -tVt +tBy dME hxP ntn @@ -117696,24 +117748,24 @@ diU tOy xcT tDG -kTD +gns cQR gMA qih tNQ -dNx +fhq xhJ sRb -mdl -phX -phX -phX -brz -lxL -fSC -mjU -kPE -pBR +xfL +tej +uQJ +tej +uqi +jEW +rgW +iLN +oiU +uHU tla lAB lAB @@ -117957,19 +118009,19 @@ cQb cQR jgI cQR -hEH +pRL oSX kdO -oXm -gLK +qFl +pIf chg vPB noE -phX -dsh +iLF +wlb blx hev -cyc +xMZ kwM xbx tbE @@ -118176,7 +118228,7 @@ gyd awX iSC wxl -nSf +wdm oRK wua bwA @@ -118186,7 +118238,7 @@ dty fIu jYp qew -sVI +msy dry lDu pkM @@ -118218,12 +118270,12 @@ vOW aoE sMh iCW -gLK +pIf chg vHF miM -jYb -sIr +iLF +uRL rCi rCi rCi @@ -118449,13 +118501,13 @@ lDu lDu xZL rZV -ngV +jPA iRq aLI dVB -kyj +vQc qAR -snL +vDc eXQ kwl hOc @@ -118474,15 +118526,15 @@ lcg hyt dPK wTe -acZ -seN +mHu +gLG chg ybS noE -tej +nDu lFC wdZ -utU +rYA mnK oIu eQg @@ -118698,7 +118750,7 @@ kFN uYh hvz clw -gfT +bYg oay dAQ eGD @@ -118726,21 +118778,21 @@ jAF xqQ jAF acm -qBs -wEJ -fol +kjG +eOA +oYK oMU baG oaF -gLK +pIf chg vHF noE -dSt +vVv +tej tej tej -vte -ruc +dSt fQb lSl xGN @@ -118963,7 +119015,7 @@ cGr oLH kxT rZV -rBq +vvb udJ gmb fxY @@ -118985,23 +119037,23 @@ aaa acK uJV jUi -kMi -vhn +hxZ +tQP seO mHu -hVF +ukz chg tdn xWJ -cua -tej -dSt -heX -iyk -qYt -cpi -nlM -bzS +qmM +yit +egH +cmR +dka +dIB +hIy +cRM +qUB rCi bUO bUN @@ -119220,7 +119272,7 @@ uwq oLH wCY rZV -lSg +trx tIe twd dVB @@ -119245,20 +119297,20 @@ vOW vOW sMh vOW -eWd -evu -seN -iVy -gLK -rZO -rJw -jMl -bpU +cir +slX +hMU +vcx +uOl +rya +oSW +ajb +ajb ajb pcC uAe -udg -cdt +fBw +xir mkk cic cic @@ -119464,8 +119516,8 @@ lDu lDu wHb lDu -ldc -bcE +qlI +wst ocv fHZ bIm @@ -119475,7 +119527,7 @@ aMb fqb lDu lDu -mrt +lGT vOX qAR uoL @@ -119499,22 +119551,22 @@ aaa acK wJe rMI -iSU +iMS jij wJe -otA -hRO +gIW +pqp xnU mPS ijL -nFg -dOF +vCp +mDY cKz dfY vkd cHO vOW -qaa +hhy bPK cVr bYe @@ -119756,20 +119808,20 @@ aaa acm vOW dSw -qiL -msZ +cZN +lWF gHA -wkB -uEa +bfQ +yit vHF aiB vHF -dSt -dOF +vVv +aFj pfH -nHC -pGg -apl +hKF +cHv +xmm sMh cdl gpH @@ -119989,10 +120041,10 @@ roI oPX lDu wCY -kLm +fkP kCd gKI -ces +ygo bBK dVB jJp @@ -120013,19 +120065,19 @@ aaa acm wJe hQs -gsl -bHL +eZi +vPD wJe -wkB -hxx +fhC +egH wTS epv wTS -tej -dOF +iLF +mDY ycy bzc -rcY +mDY hsV vOW vOW @@ -120246,7 +120298,7 @@ fBm vFW oLH wCY -cxR +mxo xzI gKI gQt @@ -120269,23 +120321,23 @@ aaa aaa acm sMh -iZX -lkp +ykR +eZi nqx awt emV -fyM -fyM -fyM +iLF +iKV +jWN esX -fyM +xKq qjK dmf vGa wSz -pnd +req vOW -kpq +dqh tBn dhY qYN @@ -120480,7 +120532,7 @@ aeu aeu aeu uVQ -hjb +ccS uVQ aeu aeu @@ -120496,15 +120548,15 @@ sJS rZV mtc oLH -oWj +bMD lkB iaX eWS eWS sRz xZL +mxo aVG -kCd qAR eSY kzr @@ -120527,13 +120579,13 @@ aaa acm wJe twj -unb +rNc wGh wJe cdu dyu lFC -rzJ +lFC lFC cOf pID @@ -120541,9 +120593,9 @@ dfY khK qbO aYd -vOW +sMh nho -hKP +tbz sMh qeH ffa @@ -120753,7 +120805,7 @@ xeP vOX mtc lDu -doB +ocd fJo lkB xye @@ -120798,7 +120850,7 @@ sMh vCM iOW iOW -vOW +sMh sMh acJ vOW @@ -121011,15 +121063,15 @@ rZV mtc lDu lOh -eIb +hRJ seG cMD drm oLH wCY -kCd -rZV -fyS +sZK +ddQ +erp fsV tRg nuS @@ -121273,10 +121325,10 @@ sJS lDu yad lDu -wCY +tqt rZV rZV -ssw +eeJ enQ vSM gAF @@ -121298,19 +121350,19 @@ aaa qJs acm qJs +vOW +wwg iOW -noj -iOW -vuk -iOW +kSe +sMh tlA oVP juq +vOW +wwg iOW -noj -iOW -vuk -iOW +kSe +sMh acm aaa acm @@ -121533,11 +121585,11 @@ daG wCY sjd fej -mME +syl xCP -pra +tNf rEu -gEs +kPI xPT aeo acm @@ -121790,9 +121842,9 @@ exg kMP ddQ dez -aPr -rYa -pIb +tjd +eRl +uBd mVu kPI mjW @@ -121814,7 +121866,7 @@ aaa aaa iOW wkT -wzy +sMh emb iOW qJs @@ -121822,7 +121874,7 @@ acm qJs iOW emb -wzy +sMh lPK iOW acm @@ -122044,11 +122096,11 @@ sJS rZV smo vOX -fIl +rfK rZV -hLH -dOu -olq +aLJ +uSu +ist kYE gjv oiE diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 4945b2afa4d0..6ac29f31bbcd 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -322,6 +322,12 @@ /obj/structure/window/spawner/directional/north, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"agL" = ( +/obj/effect/turf_decal/stripes/white/line{ + dir = 8 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "agN" = ( /obj/structure/table/glass, /obj/item/radio/intercom/directional/west, @@ -2218,10 +2224,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/ai/upload/chamber) -"aOc" = ( -/obj/machinery/holopad/secure, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "aOf" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -2318,17 +2320,6 @@ /obj/item/radio/intercom/directional/west, /turf/open/floor/wood, /area/station/command/heads_quarters/captain/private) -"aPv" = ( -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) -"aPw" = ( -/obj/machinery/ai_slipper{ - uses = 8 - }, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "aPx" = ( /obj/effect/turf_decal/bot, /obj/effect/spawner/random/structure/closet_empty/crate, @@ -2692,21 +2683,6 @@ /obj/item/clothing/gloves/color/fyellow, /turf/open/floor/plating, /area/station/maintenance/fore) -"aWC" = ( -/obj/structure/cable, -/obj/structure/rack, -/obj/item/storage/box/beakers{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/item/storage/box/syringes, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 1 - }, -/obj/item/storage/pill_bottle/sepsisillin, -/turf/open/floor/iron/white, -/area/station/medical/virology) "aWH" = ( /obj/machinery/telecomms/bus/preset_one, /turf/open/floor/circuit/green/telecomms/mainframe, @@ -5402,26 +5378,6 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/carpet, /area/station/command/corporate_showroom) -"bRq" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell{ - pixel_x = -6; - pixel_y = 10 - }, -/obj/item/food/grown/harebell{ - pixel_x = 8; - pixel_y = 12 - }, -/obj/item/food/grown/harebell{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/harebell{ - pixel_y = 7 - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "bRF" = ( /obj/machinery/light/small/directional/north, /turf/open/floor/iron, @@ -5839,6 +5795,12 @@ "bYz" = ( /turf/open/floor/circuit, /area/station/maintenance/port/aft) +"bYG" = ( +/obj/machinery/flasher/directional/south{ + id = "AI" + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "bYQ" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/bot, @@ -6372,6 +6334,25 @@ /obj/effect/turf_decal/tile/yellow/half/contrasted, /turf/open/floor/iron, /area/station/engineering/break_room) +"ckr" = ( +/obj/machinery/light_switch/directional/west, +/obj/effect/decal/cleanable/cobweb, +/obj/structure/table/wood, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/item/food/grown/flower/poppy{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 7; + pixel_y = 7 + }, +/obj/item/flashlight/flare/candle{ + pixel_x = 12; + pixel_y = 4 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "cky" = ( /obj/machinery/power/shieldwallgen, /obj/machinery/light/cold/directional/east, @@ -8505,15 +8486,6 @@ /obj/machinery/duct, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"cXc" = ( -/obj/structure/table, -/obj/item/food/grown/poppy/lily, -/obj/effect/turf_decal/stripes/line{ - dir = 9 - }, -/obj/machinery/light/directional/south, -/turf/open/floor/iron, -/area/station/service/hydroponics) "cXg" = ( /obj/machinery/recharge_station, /obj/machinery/light/small/directional/west, @@ -14134,15 +14106,6 @@ /obj/structure/cable, /turf/open/floor/wood, /area/station/command/corporate_showroom) -"eQg" = ( -/obj/machinery/flasher/directional/south{ - id = "AI" - }, -/obj/machinery/porta_turret/ai{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "eQr" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -16054,6 +16017,11 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos) +"fzc" = ( +/obj/machinery/ai_law_rack/base/core, +/obj/effect/turf_decal/stripes/white/full, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "fzd" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -18133,13 +18101,6 @@ }, /turf/open/floor/iron, /area/station/engineering/main) -"goo" = ( -/obj/machinery/porta_turret/ai{ - dir = 1 - }, -/obj/machinery/firealarm/directional/south, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "goF" = ( /obj/machinery/atmospherics/pipe/layer_manifold/cyan/hidden{ dir = 8 @@ -18831,22 +18792,6 @@ /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, /turf/closed/wall/r_wall, /area/station/engineering/atmos) -"gAU" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/ai_module/reset, -/obj/item/assembly/flash/handheld, -/obj/item/assembly/flash/handheld, -/obj/item/flashlight{ - pixel_x = 1; - pixel_y = 5 - }, -/obj/item/flashlight{ - pixel_x = 1; - pixel_y = 5 - }, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "gBb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -19019,17 +18964,6 @@ "gEg" = ( /turf/closed/wall, /area/station/security/execution/education) -"gEk" = ( -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/structure/window/reinforced/spawner/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/chapel/funeral) "gEl" = ( /obj/structure/bed, /obj/item/bedsheet/red, @@ -19075,29 +19009,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"gEX" = ( -/obj/structure/sign/plaques/kiddie/badger{ - pixel_y = 32 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/machinery/light/small/directional/north, -/obj/structure/table/wood, -/turf/open/floor/carpet, -/area/station/service/chapel/funeral) "gEZ" = ( /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/west, @@ -21016,22 +20927,6 @@ }, /turf/open/floor/iron, /area/station/security/execution/transfer) -"hlB" = ( -/obj/machinery/flasher/directional/north{ - id = "AI" - }, -/obj/effect/spawner/random/aimodule/harmful, -/obj/structure/table/wood/fancy/red, -/obj/machinery/door/window/brigdoor/left/directional/west{ - name = "High-Risk Modules"; - req_access = list("captain") - }, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/obj/structure/window/reinforced/spawner/directional/south, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "hlD" = ( /turf/open/floor/carpet, /area/station/command/heads_quarters/captain/private) @@ -21167,6 +21062,17 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) +"hoM" = ( +/obj/machinery/hydroponics/soil{ + pixel_y = 8 + }, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/machinery/light/small/directional/north, +/turf/open/floor/cult, +/area/station/service/chapel/funeral) "hoN" = ( /obj/structure/table/reinforced, /obj/machinery/door/firedoor, @@ -25906,6 +25812,14 @@ }, /turf/open/floor/iron/dark, /area/station/science/ordnance) +"iTg" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/white/line{ + dir = 1 + }, +/obj/machinery/holopad/secure, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "iTC" = ( /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ dir = 10 @@ -33132,25 +33046,6 @@ dir = 8 }, /area/station/medical/morgue) -"lmx" = ( -/obj/machinery/light_switch/directional/west, -/obj/effect/decal/cleanable/cobweb, -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/item/food/grown/poppy{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/poppy{ - pixel_x = 7; - pixel_y = 7 - }, -/obj/item/flashlight/flare/candle{ - pixel_x = 12; - pixel_y = 4 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "lmy" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -34274,6 +34169,16 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/fore) +"lLh" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/white/corner{ + dir = 1 + }, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "lLk" = ( /obj/effect/landmark/event_spawn, /turf/open/floor/iron, @@ -34499,6 +34404,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"lOQ" = ( +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/structure/table/wood, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/structure/window/reinforced/spawner/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/chapel/funeral) "lOU" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -36715,18 +36631,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/command/bridge) -"mBW" = ( -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/item/food/grown/poppy{ - pixel_x = -8; - pixel_y = 7 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "mCj" = ( /obj/machinery/camera{ c_tag = "Xenobiology Lab - Pen #7"; @@ -36914,6 +36818,10 @@ /obj/machinery/light/directional/north, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"mEP" = ( +/obj/machinery/firealarm/directional/south, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "mET" = ( /obj/item/wrench, /obj/effect/decal/cleanable/dirt, @@ -38021,6 +37929,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/hidden/layer5, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"mXy" = ( +/obj/structure/table, +/obj/item/food/grown/flower/poppy/lily, +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/obj/machinery/light/directional/south, +/turf/open/floor/iron, +/area/station/service/hydroponics) "mXz" = ( /obj/effect/landmark/generic_maintenance_landmark, /obj/effect/turf_decal/stripes/line, @@ -39158,25 +39075,6 @@ /obj/structure/sign/warning/pods/directional/north, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) -"nra" = ( -/obj/machinery/newscaster/directional/north, -/obj/machinery/camera/directional/north{ - c_tag = "Chapel - Fore" - }, -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/item/food/grown/poppy{ - pixel_y = 11 - }, -/obj/item/book/bible{ - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = -11; - pixel_y = 2 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "nrm" = ( /obj/effect/turf_decal/stripes/white/line{ dir = 1 @@ -39577,17 +39475,6 @@ /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible/layer5, /turf/closed/wall, /area/station/engineering/atmos/pumproom) -"nwL" = ( -/obj/machinery/door/window/right/directional/south{ - name = "Upload Console Window"; - req_access = list("ai_upload") - }, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/computer/upload/ai/no_lock, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "nwT" = ( /obj/structure/disposalpipe/sorting/mail/flip{ dir = 8 @@ -41718,6 +41605,26 @@ }, /turf/open/space/basic, /area/space) +"olR" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell{ + pixel_x = -6; + pixel_y = 10 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 8; + pixel_y = 12 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/harebell{ + pixel_y = 7 + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "omc" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -45583,6 +45490,25 @@ /obj/machinery/newscaster/directional/north, /turf/open/floor/iron/dark, /area/station/medical/break_room) +"pCq" = ( +/obj/machinery/newscaster/directional/north, +/obj/machinery/camera/directional/north{ + c_tag = "Chapel - Fore" + }, +/obj/structure/table/wood, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/item/food/grown/flower/poppy{ + pixel_y = 11 + }, +/obj/item/book/bible{ + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = -11; + pixel_y = 2 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "pCt" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -49186,6 +49112,21 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"qOJ" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/assembly/flash/handheld, +/obj/item/assembly/flash/handheld, +/obj/item/flashlight{ + pixel_x = 1; + pixel_y = 5 + }, +/obj/item/flashlight{ + pixel_x = 1; + pixel_y = 5 + }, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "qOM" = ( /obj/machinery/vending/hydronutrients, /obj/effect/turf_decal/trimline/green/filled/line{ @@ -49945,6 +49886,12 @@ /obj/structure/sign/clock/directional/south, /turf/open/floor/iron/dark, /area/station/medical/storage) +"rav" = ( +/obj/effect/turf_decal/stripes/white/line{ + dir = 4 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "raz" = ( /obj/structure/cable, /turf/open/floor/plating/airless, @@ -54361,6 +54308,23 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"sCK" = ( +/obj/structure/table, +/obj/machinery/status_display/ai/directional/west, +/obj/machinery/flasher/directional/south{ + id = "AI" + }, +/obj/item/ai_module/law/supplied/freeform, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 4; + pixel_x = 2 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 5; + pixel_x = -2 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "sCM" = ( /obj/effect/spawner/random/structure/closet_maintenance, /obj/item/assembly/prox_sensor{ @@ -55967,25 +55931,6 @@ /obj/machinery/light/directional/east, /turf/open/floor/iron, /area/station/security/courtroom) -"tcx" = ( -/obj/structure/table, -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 1 - }, -/obj/item/aicard, -/obj/item/pai_card, -/obj/item/circuitboard/aicore, -/obj/machinery/keycard_auth/wall_mounted/directional/north{ - pixel_x = -5 - }, -/obj/machinery/button/door/directional/north{ - id = "xeno_blastdoor"; - name = "Xenobiology Containment Control"; - pixel_x = 8; - req_access = list("rd") - }, -/turf/open/floor/iron/white, -/area/station/command/heads_quarters/rd) "tcC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -56779,6 +56724,28 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation) +"trA" = ( +/obj/structure/table, +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 1 + }, +/obj/item/aicard, +/obj/item/pai_card, +/obj/item/circuitboard/aicore, +/obj/machinery/keycard_auth/wall_mounted/directional/north{ + pixel_x = -5 + }, +/obj/machinery/button/door/directional/north{ + id = "xeno_blastdoor"; + name = "Xenobiology Containment Control"; + pixel_x = 8; + req_access = list("rd") + }, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/iron/white, +/area/station/command/heads_quarters/rd) "trC" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -57514,6 +57481,19 @@ }, /turf/open/floor/plating, /area/station/cargo/sorting) +"tGu" = ( +/obj/machinery/flasher/directional/north{ + id = "AI" + }, +/obj/effect/spawner/random/aimodule/harmful, +/obj/structure/table/wood/fancy/red, +/obj/machinery/door/window/brigdoor/left/directional/west{ + name = "High-Risk Modules"; + req_access = list("captain") + }, +/obj/structure/window/reinforced/spawner/directional/south, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "tGy" = ( /obj/machinery/firealarm/directional/west, /obj/effect/turf_decal/tile/green/fourcorners, @@ -58959,6 +58939,29 @@ /obj/machinery/portable_atmospherics/canister, /turf/open/floor/iron/dark, /area/station/engineering/atmospherics_engine) +"ucA" = ( +/obj/structure/sign/plaques/kiddie/badger{ + pixel_y = 32 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/machinery/light/small/directional/north, +/obj/structure/table/wood, +/turf/open/floor/carpet, +/area/station/service/chapel/funeral) "ude" = ( /obj/machinery/holopad, /obj/effect/turf_decal/box/white{ @@ -62115,17 +62118,6 @@ /obj/item/poster/traitor, /turf/open/floor/iron/dark, /area/station/command/vault) -"viF" = ( -/obj/machinery/door/window/left/directional/south{ - name = "Cyborg Upload Console Window"; - req_access = list("ai_upload") - }, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/computer/upload/borg/no_lock, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "viH" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/spawner/structure/window/reinforced, @@ -63153,19 +63145,6 @@ /obj/item/storage/fancy/candle_box, /turf/open/floor/iron/white, /area/station/medical/abandoned) -"vyv" = ( -/obj/structure/table, -/obj/machinery/status_display/ai/directional/west, -/obj/machinery/flasher/directional/south{ - id = "AI" - }, -/obj/item/ai_module/reset{ - pixel_x = 2; - pixel_y = 8 - }, -/obj/item/ai_module/supplied/freeform, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "vyy" = ( /obj/machinery/washing_machine, /obj/effect/decal/cleanable/dirt, @@ -63745,6 +63724,16 @@ /obj/effect/mapping_helpers/airlock/access/all/medical/paramedic, /turf/open/floor/iron/dark, /area/station/medical/paramedic) +"vIw" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/white/corner{ + dir = 4 + }, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/turf/open/floor/iron/dark/smooth_large, +/area/station/ai/upload/chamber) "vIz" = ( /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/visible{ dir = 8 @@ -64963,6 +64952,18 @@ /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron/white, /area/station/medical/storage) +"waP" = ( +/obj/structure/table/wood, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/item/food/grown/flower/poppy{ + pixel_x = -8; + pixel_y = 7 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "wbF" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -65493,17 +65494,6 @@ /obj/structure/chair/stool/directional/south, /turf/open/floor/wood, /area/station/commons/lounge) -"wlj" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/machinery/light/small/directional/north, -/turf/open/floor/cult, -/area/station/service/chapel/funeral) "wll" = ( /obj/structure/table/wood, /obj/item/radio/intercom/directional/east, @@ -66671,6 +66661,21 @@ /obj/structure/cable, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) +"wEr" = ( +/obj/structure/cable, +/obj/structure/rack, +/obj/item/storage/box/beakers{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 1 + }, +/obj/item/storage/pill_bottle/sepsisillin, +/turf/open/floor/iron/white, +/area/station/medical/virology) "wEz" = ( /obj/item/stack/sheet/cardboard, /obj/effect/spawner/random/trash/janitor_supplies, @@ -86446,7 +86451,7 @@ jUb jUb aaa xjH -aWC +wEr sxU rie rie @@ -91357,7 +91362,7 @@ eEn gGh gGh gYU -wlj +hoM rIG aaa aaa @@ -92381,7 +92386,7 @@ cvy xvf qKi vQg -gEX +ucA dLy qyb dLy @@ -92899,7 +92904,7 @@ gYU pIs gYU kho -gEk +lOQ gYU aaa aaa @@ -94175,7 +94180,7 @@ bsZ rJI taX tSw -lmx +ckr gmX lHK woz @@ -94432,7 +94437,7 @@ tSw tSw xtz tSw -nra +pCq iJj uZD hKv @@ -94689,7 +94694,7 @@ xaj tSw xtz tSw -mBW +waP iJj jAm lFZ @@ -95203,7 +95208,7 @@ kNO hav xGX tSw -bRq +olR iZi kVN hKv @@ -95894,7 +95899,7 @@ aJS akF pQG wtB -vyv +sCK aJS aaa ihq @@ -96406,10 +96411,10 @@ aaa aJS oOl rxx -viF -aPv +rav +vIw oWF -eQg +bYG aJS mSI aUx @@ -96663,8 +96668,8 @@ aaa aJS dWf nhP -aOc -aPw +fzc +iTg lSy dqn wpJ @@ -96920,10 +96925,10 @@ aaa aJS bpu wzH -nwL -aPv +agL +lLh iIQ -goo +mEP aJS bYv aUx @@ -97433,7 +97438,7 @@ qWF aaa aJS aJS -hlB +tGu vrJ fEO cFr @@ -105453,7 +105458,7 @@ jvL vfh kHN tAg -tcx +trA nhr vYE wnY @@ -107756,7 +107761,7 @@ uHo beO lXr cZm -cXc +mXy tUn kmN bGL @@ -109777,7 +109782,7 @@ psZ psZ psZ qXB -gAU +qOJ cDO oUK eRR diff --git a/_maps/map_files/NebulaStation/NebulaStation.dmm b/_maps/map_files/NebulaStation/NebulaStation.dmm index 2dbf08716f73..af0422102f70 100644 --- a/_maps/map_files/NebulaStation/NebulaStation.dmm +++ b/_maps/map_files/NebulaStation/NebulaStation.dmm @@ -1952,6 +1952,10 @@ /obj/effect/decal/cleanable/glass, /turf/open/floor/plating, /area/station/maintenance/port) +"aoU" = ( +/obj/item/food/grown/flower/harebell, +/turf/open/misc/asteroid/airless, +/area/space/nearstation) "aoW" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -8612,27 +8616,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white, /area/station/medical/psychology) -"bpZ" = ( -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 5 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark/corner{ - dir = 8 - }, -/obj/structure/table/wood/fancy/royalblack, -/obj/item/food/grown/poppy{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/poppy{ - pixel_x = 2; - pixel_y = 3 - }, -/obj/item/food/grown/poppy{ - pixel_y = 11 - }, -/turf/open/floor/iron/dark/diagonal, -/area/station/service/chapel) "bqa" = ( /obj/machinery/portable_atmospherics/canister/nitrogen, /obj/effect/turf_decal/bot, @@ -13474,16 +13457,6 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/engine/n2o, /area/station/engineering/atmos) -"bZk" = ( -/obj/structure/table/glass, -/obj/item/stack/spacecash/c10, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/structure/sign/poster/contraband/random/directional/south, -/turf/open/floor/plating, -/area/station/maintenance/starboard/fore) "bZp" = ( /obj/effect/spawner/random/engineering/atmospherics_portable, /obj/effect/mapping_helpers/broken_floor, @@ -18560,6 +18533,23 @@ }, /turf/open/floor/plating, /area/station/cargo/sorting) +"cMJ" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 10 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark/corner{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/red/filled/corner{ + dir = 4 + }, +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell, +/obj/machinery/camera/autoname/directional/west{ + network = list("prison") + }, +/turf/open/floor/iron/dark/textured, +/area/station/security/prison) "cMN" = ( /obj/effect/turf_decal/siding/dark{ dir = 1 @@ -19869,19 +19859,6 @@ /obj/item/toy/seashell, /turf/open/floor/bamboo, /area/station/ai/satellite/exterior) -"cXw" = ( -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 9 - }, -/obj/effect/turf_decal/trimline/red/filled/corner, -/obj/effect/turf_decal/siding/thinplating_new/dark/corner, -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/structure/sign/poster/official/bless_this_spess/directional/west, -/turf/open/floor/iron/dark/textured, -/area/station/security/prison) "cXz" = ( /turf/open/floor/glass/reinforced/plasma/airless, /area/space/nearstation) @@ -20241,33 +20218,6 @@ /obj/structure/window/reinforced/spawner/directional/south, /turf/open/floor/iron/dark/textured, /area/station/command/heads_quarters/rd) -"dac" = ( -/obj/structure/sign/plaques/kiddie/badger{ - pixel_y = 32 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 1 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark, -/obj/structure/table/wood/fancy, -/obj/machinery/light/directional/north, -/turf/open/floor/iron/dark/herringbone, -/area/station/service/chapel/funeral) "dae" = ( /obj/effect/turf_decal/siding/wood{ dir = 9 @@ -21125,6 +21075,26 @@ /obj/item/food/cornchips/random, /turf/open/floor/iron/dark, /area/station/hallway/primary/port) +"dgB" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 9 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark/corner, +/obj/structure/table/wood/fancy/royalblack, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 6; + pixel_y = 6 + }, +/obj/item/food/grown/flower/harebell{ + pixel_x = 3; + pixel_y = 3 + }, +/turf/open/floor/iron/dark/diagonal, +/area/station/service/chapel) "dgG" = ( /turf/open/floor/plating, /area/station/maintenance/port) @@ -25746,15 +25716,6 @@ /obj/machinery/light/small/dim/directional/north, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room/upper) -"dQv" = ( -/obj/structure/table, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/effect/turf_decal/tile/green/half/contrasted, -/obj/machinery/newscaster/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "dQw" = ( /obj/effect/turf_decal/siding/thinplating_new/dark, /obj/effect/turf_decal/tile/neutral, @@ -39289,11 +39250,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"fPj" = ( -/obj/structure/table/wood/fancy, -/obj/item/food/grown/poppy/geranium/fraxinella, -/turf/open/floor/glass/reinforced, -/area/station/service/chapel/funeral) "fPp" = ( /obj/effect/turf_decal/siding/thinplating_new/dark, /obj/effect/turf_decal/tile/neutral, @@ -42814,11 +42770,6 @@ /obj/structure/curtain, /turf/open/floor/iron/white/textured_large, /area/station/commons/toilet/restrooms) -"gnZ" = ( -/obj/effect/turf_decal/box/red, -/obj/structure/cable, -/turf/open/floor/iron/dark/small, -/area/station/ai/upload/chamber) "goa" = ( /obj/effect/turf_decal/siding/thinplating_new/dark/corner{ dir = 4 @@ -49251,7 +49202,6 @@ /obj/effect/turf_decal/siding/dark{ dir = 6 }, -/obj/item/storage/photo_album/captain, /obj/item/camera, /turf/open/floor/iron/dark/textured_large, /area/station/command/heads_quarters/captain) @@ -51922,6 +51872,16 @@ }, /turf/open/floor/grass, /area/station/ai/satellite/exterior) +"hGA" = ( +/obj/effect/turf_decal/stripes/red/line{ + dir = 8 + }, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/obj/effect/turf_decal/stripes/red/box, +/turf/open/floor/iron/dark/small, +/area/station/ai/upload/chamber) "hGO" = ( /obj/effect/turf_decal/tile/dark/diagonal_centre, /obj/structure/table/glass, @@ -59174,17 +59134,6 @@ "iJc" = ( /turf/closed/wall/r_wall, /area/station/command/eva) -"iJd" = ( -/obj/effect/turf_decal/siding/purple, -/obj/structure/table/reinforced/rglass, -/obj/item/aicard, -/obj/item/pai_card, -/obj/item/circuitboard/aicore, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/structure/cable, -/turf/open/floor/iron/dark/textured, -/area/station/command/heads_quarters/rd) "iJe" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 8 @@ -60339,6 +60288,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark/textured, /area/station/engineering/atmos/storage) +"iSe" = ( +/obj/structure/table/glass, +/obj/item/food/grown/flower/rose{ + pixel_y = 8 + }, +/turf/open/floor/iron/dark/herringbone, +/area/station/service/hydroponics) "iSi" = ( /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 1 @@ -62611,20 +62567,6 @@ /obj/machinery/door/window/left/directional/south, /turf/open/floor/iron/white/textured_large, /area/station/engineering/break_room) -"jke" = ( -/obj/machinery/computer/upload/borg/no_lock{ - dir = 4 - }, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/effect/turf_decal/stripes/red/box, -/obj/machinery/door/window/left/directional/east{ - name = "Cyborg Upload Console Window"; - req_access = list("ai_upload") - }, -/turf/open/floor/iron/dark/small, -/area/station/ai/upload/chamber) "jkh" = ( /obj/effect/turf_decal/stripes/box, /obj/machinery/computer/gateway_control, @@ -66195,18 +66137,6 @@ dir = 4 }, /area/station/maintenance/aft/lesser) -"jLW" = ( -/obj/effect/turf_decal/tile/green/half/contrasted, -/obj/structure/rack, -/obj/item/storage/test_tube_rack, -/obj/item/storage/box/beakers{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/item/storage/box/syringes, -/obj/item/storage/pill_bottle/sepsisillin, -/turf/open/floor/iron/dark, -/area/station/medical/virology) "jLY" = ( /obj/structure/hedge, /obj/effect/turf_decal/siding/wood{ @@ -71957,20 +71887,6 @@ color = "#3d3e42" }, /area/station/command) -"kBI" = ( -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/structure/table/wood/fancy, -/obj/machinery/computer/pod/old/mass_driver_controller/chapelgun{ - pixel_y = 30 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 9 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel/office) "kBR" = ( /obj/effect/turf_decal/siding/dark/corner, /obj/effect/turf_decal/siding/dark/corner{ @@ -81885,23 +81801,6 @@ /obj/effect/turf_decal/tile/dark_blue/diagonal_edge, /turf/open/floor/iron/white/diagonal, /area/station/commons/toilet/restrooms) -"meC" = ( -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 10 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark/corner{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/red/filled/corner{ - dir = 4 - }, -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/obj/machinery/camera/autoname/directional/west{ - network = list("prison") - }, -/turf/open/floor/iron/dark/textured, -/area/station/security/prison) "meE" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -87474,6 +87373,11 @@ dir = 8 }, /area/station/maintenance/starboard/fore) +"mVV" = ( +/obj/structure/table/wood/fancy, +/obj/item/food/grown/flower/poppy/geranium/fraxinella, +/turf/open/floor/glass/reinforced, +/area/station/service/chapel/funeral) "mVZ" = ( /obj/machinery/status_display/evac/directional/north, /obj/structure/closet/secure_closet/courtroom, @@ -88506,6 +88410,20 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/brig/lower) +"nfd" = ( +/obj/effect/turf_decal/siding/purple, +/obj/structure/table/reinforced/rglass, +/obj/item/aicard, +/obj/item/pai_card, +/obj/item/circuitboard/aicore, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/structure/cable, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/iron/dark/textured, +/area/station/command/heads_quarters/rd) "nfm" = ( /obj/effect/turf_decal/trimline/yellow/filled/warning{ dir = 10 @@ -94793,6 +94711,21 @@ dir = 4 }, /area/station/hallway/secondary/exit/departure_lounge) +"obw" = ( +/obj/structure/table/wood/fancy/red, +/obj/effect/spawner/random/aimodule/harmful, +/obj/structure/window/reinforced/spawner/directional/north, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/flasher/directional/east{ + id = "AI" + }, +/obj/machinery/light/directional/east, +/obj/machinery/door/window/brigdoor/right/directional/west{ + name = "High-Risk Modules"; + req_access = list("captain") + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "obB" = ( /obj/machinery/light/floor, /obj/effect/turf_decal/trimline/blue, @@ -96041,6 +95974,13 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room/upper) +"okc" = ( +/obj/structure/flora/bush/flowers_pp/style_3, +/obj/item/food/grown/flower/poppy/lily, +/turf/open/misc/asteroid/airless{ + icon_state = "asteroid2" + }, +/area/space/nearstation) "okj" = ( /obj/effect/decal/cleanable/blood/old, /turf/open/misc/asteroid/airless, @@ -96450,6 +96390,15 @@ /obj/structure/marker_beacon/cerulean, /turf/open/floor/engine/hull/reinforced, /area/space/nearstation) +"omX" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy/lily, +/obj/item/food/grown/flower/poppy/geranium{ + pixel_x = 6; + pixel_y = 6 + }, +/turf/open/floor/carpet, +/area/station/service/chapel/office) "omY" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/structure/flora/bush/fullgrass/style_random, @@ -96749,10 +96698,6 @@ dir = 4 }, /area/station/maintenance/central) -"ooJ" = ( -/obj/item/food/grown/harebell, -/turf/open/misc/asteroid/airless, -/area/space/nearstation) "ooN" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 10 @@ -98838,6 +98783,11 @@ /obj/structure/flora/rock/pile/jungle/style_random, /turf/open/floor/engine/hull/reinforced, /area/space/nearstation) +"oFe" = ( +/obj/structure/table/glass, +/obj/item/food/grown/flower/poppy/lily, +/turf/open/floor/iron/dark/herringbone, +/area/station/service/hydroponics) "oFl" = ( /obj/effect/turf_decal/tile/blue, /obj/structure/chair/stool/bar/directional, @@ -100166,30 +100116,6 @@ }, /turf/open/floor/iron/white/herringbone, /area/station/commons/fitness) -"oOX" = ( -/obj/structure/table/reinforced/rglass, -/obj/item/ai_module/supplied/freeform, -/obj/item/ai_module/reset{ - pixel_x = 2; - pixel_y = 3 - }, -/obj/machinery/flasher/directional/south{ - id = "AI" - }, -/obj/effect/turf_decal/siding/dark{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 8 - }, -/obj/effect/turf_decal/siding/dark{ - dir = 4 - }, -/obj/structure/railing{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai/upload/chamber) "oOZ" = ( /obj/structure/flora/rock/pile/style_random, /obj/effect/spawner/random/trash, @@ -107487,6 +107413,16 @@ /obj/effect/landmark/start/scientist, /turf/open/floor/carpet, /area/station/science/breakroom) +"pPg" = ( +/obj/structure/table/glass, +/obj/item/stack/spacecash/c10, +/obj/item/food/grown/flower/harebell{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/structure/sign/poster/contraband/random/directional/south, +/turf/open/floor/plating, +/area/station/maintenance/starboard/fore) "pPh" = ( /obj/machinery/door/airlock/maintenance, /obj/structure/cable, @@ -107860,6 +107796,7 @@ dir = 1 }, /obj/machinery/light/warm/directional/north, +/obj/item/storage/photo_album/bar, /turf/open/floor/iron/dark/small, /area/station/service/bar/backroom) "pRK" = ( @@ -108482,24 +108419,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark/small, /area/station/engineering/atmos) -"pWA" = ( -/obj/structure/table/wood/fancy/red, -/obj/item/ai_module/reset/purge{ - pixel_y = 11 - }, -/obj/effect/spawner/random/aimodule/harmful, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/flasher/directional/east{ - id = "AI" - }, -/obj/machinery/light/directional/east, -/obj/machinery/door/window/brigdoor/right/directional/west{ - name = "High-Risk Modules"; - req_access = list("captain") - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "pWS" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/turf_decal/siding/thinplating_new/dark{ @@ -109263,12 +109182,6 @@ /obj/effect/turf_decal/tile/blue/diagonal_centre, /turf/open/floor/iron/white/diagonal, /area/station/cargo/breakroom) -"qdw" = ( -/obj/item/food/grown/poppy/geranium, -/turf/open/misc/asteroid/airless{ - icon_state = "asteroid9" - }, -/area/space/nearstation) "qdy" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -110240,11 +110153,6 @@ }, /turf/open/floor/iron/dark, /area/station/medical/break_room) -"qlJ" = ( -/obj/structure/table/glass, -/obj/item/food/grown/poppy/lily, -/turf/open/floor/iron/dark/herringbone, -/area/station/service/hydroponics) "qlL" = ( /obj/effect/turf_decal/bot_white/left, /obj/machinery/digital_clock/directional/north, @@ -112622,6 +112530,17 @@ dir = 1 }, /area/station/cargo/storage) +"qEo" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/red/line{ + dir = 1 + }, +/obj/machinery/holopad/secure, +/obj/effect/turf_decal/box/red, +/turf/open/floor/iron/dark/small, +/area/station/ai/upload/chamber) "qEr" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line{ dir = 4 @@ -113760,6 +113679,12 @@ }, /turf/open/floor/iron/dark, /area/station/security/office) +"qLQ" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/red/full, +/obj/machinery/ai_law_rack/base/core, +/turf/open/floor/iron/dark/small, +/area/station/ai/upload/chamber) "qLS" = ( /obj/effect/mapping_helpers/broken_floor, /obj/effect/turf_decal/siding/thinplating_new/corner{ @@ -113845,6 +113770,20 @@ }, /turf/open/floor/iron/dark/herringbone, /area/station/service/chapel/funeral) +"qMx" = ( +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/structure/table/wood/fancy, +/obj/machinery/computer/pod/old/mass_driver_controller/chapelgun{ + pixel_y = 30 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 9 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel/office) "qMz" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -114755,6 +114694,11 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/cargo/mining_breakroom) +"qUO" = ( +/obj/structure/table/glass, +/obj/item/food/grown/flower/poppy/geranium, +/turf/open/floor/iron/dark/herringbone, +/area/station/service/hydroponics) "qUX" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 10 @@ -115819,6 +115763,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"rdG" = ( +/obj/effect/turf_decal/stripes/red/line{ + dir = 4 + }, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/obj/effect/turf_decal/stripes/red/box, +/turf/open/floor/iron/dark/small, +/area/station/ai/upload/chamber) "rdK" = ( /turf/closed/wall/r_wall, /area/station/engineering/engine_smes) @@ -117302,20 +117256,6 @@ }, /turf/open/floor/iron/white, /area/station/science/lab) -"rop" = ( -/obj/machinery/computer/upload/ai/no_lock{ - dir = 8 - }, -/obj/structure/window/reinforced/spawner/directional/east, -/obj/structure/window/reinforced/spawner/directional/north, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/effect/turf_decal/stripes/red/box, -/obj/machinery/door/window/right/directional/west{ - name = "Upload Console Window"; - req_access = list("ai_upload") - }, -/turf/open/floor/iron/dark/small, -/area/station/ai/upload/chamber) "rot" = ( /obj/effect/turf_decal/siding/wideplating_new/dark{ dir = 4 @@ -122667,6 +122607,17 @@ /obj/machinery/oven/range, /turf/open/floor/iron/white/diagonal, /area/station/service/kitchen) +"scA" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/purple/line{ + dir = 8 + }, +/obj/structure/table/glass, +/obj/item/aicard, +/turf/open/floor/iron/dark/textured, +/area/station/engineering/storage/tech) "scC" = ( /obj/effect/spawner/random/trash/grime, /obj/effect/decal/cleanable/dirt, @@ -125549,6 +125500,33 @@ /obj/structure/cable, /turf/open/floor/wood/large, /area/station/command/heads_quarters/ce) +"sBd" = ( +/obj/structure/sign/plaques/kiddie/badger{ + pixel_y = 32 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark, +/obj/structure/table/wood/fancy, +/obj/machinery/light/directional/north, +/turf/open/floor/iron/dark/herringbone, +/area/station/service/chapel/funeral) "sBr" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -129319,13 +129297,6 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/medical/lower) -"ted" = ( -/obj/structure/flora/bush/flowers_pp/style_3, -/obj/item/food/grown/poppy/lily, -/turf/open/misc/asteroid/airless{ - icon_state = "asteroid2" - }, -/area/space/nearstation) "tep" = ( /obj/effect/turf_decal/trimline/purple/filled/line, /obj/effect/turf_decal/trimline/purple/filled/line{ @@ -136597,15 +136568,6 @@ dir = 8 }, /area/station/cargo/storage) -"ufP" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy/lily, -/obj/item/food/grown/poppy/geranium{ - pixel_x = 6; - pixel_y = 6 - }, -/turf/open/floor/carpet, -/area/station/service/chapel/office) "ufT" = ( /obj/effect/turf_decal/siding/thinplating_new/dark, /obj/machinery/status_display/evac/directional/north, @@ -137728,6 +137690,33 @@ }, /turf/open/floor/iron/white, /area/station/hallway/primary/fore) +"upF" = ( +/obj/structure/table/reinforced/rglass, +/obj/item/ai_module/law/supplied/freeform, +/obj/machinery/flasher/directional/south{ + id = "AI" + }, +/obj/effect/turf_decal/siding/dark{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 8 + }, +/obj/effect/turf_decal/siding/dark{ + dir = 4 + }, +/obj/structure/railing{ + dir = 4 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 3 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 2; + pixel_x = 3 + }, +/turf/open/floor/iron/dark, +/area/station/ai/upload/chamber) "upM" = ( /obj/machinery/airalarm/directional/north, /turf/open/floor/iron/smooth_large, @@ -138409,18 +138398,6 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark/textured_large, /area/station/ai/satellite/atmos) -"uuG" = ( -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/purple/line{ - dir = 8 - }, -/obj/structure/table/glass, -/obj/item/aicard, -/obj/item/ai_module/reset, -/turf/open/floor/iron/dark/textured, -/area/station/engineering/storage/tech) "uuH" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -139209,6 +139186,18 @@ dir = 8 }, /area/station/maintenance/department/science) +"uAZ" = ( +/obj/effect/turf_decal/tile/green/half/contrasted, +/obj/structure/rack, +/obj/item/storage/test_tube_rack, +/obj/item/storage/box/beakers{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/obj/item/storage/pill_bottle/sepsisillin, +/turf/open/floor/iron/dark, +/area/station/medical/virology) "uBa" = ( /obj/structure/cable, /obj/effect/spawner/random/maintenance, @@ -139394,6 +139383,12 @@ dir = 2 }, /area/station/hallway/secondary/exit/departure_lounge) +"uCv" = ( +/obj/item/food/grown/flower/poppy/geranium, +/turf/open/misc/asteroid/airless{ + icon_state = "asteroid9" + }, +/area/space/nearstation) "uCx" = ( /obj/structure/window/reinforced/spawner/directional/north, /obj/structure/window/reinforced/spawner/directional/south, @@ -139461,6 +139456,27 @@ /obj/machinery/camera/autoname/directional/north, /turf/open/floor/carpet/green, /area/station/service/library/upper) +"uDj" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 5 + }, +/obj/effect/turf_decal/siding/thinplating_new/dark/corner{ + dir = 8 + }, +/obj/structure/table/wood/fancy/royalblack, +/obj/item/food/grown/flower/poppy{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy{ + pixel_x = 2; + pixel_y = 3 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 11 + }, +/turf/open/floor/iron/dark/diagonal, +/area/station/service/chapel) "uDk" = ( /obj/effect/turf_decal/trimline/dark_green, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -140977,26 +140993,6 @@ /obj/structure/window/reinforced/spawner/directional/north, /turf/open/floor/grass, /area/station/service/barber) -"uQk" = ( -/obj/effect/turf_decal/siding/thinplating_new/dark{ - dir = 9 - }, -/obj/effect/turf_decal/siding/thinplating_new/dark/corner, -/obj/structure/table/wood/fancy/royalblack, -/obj/item/food/grown/harebell{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/food/grown/harebell{ - pixel_x = 6; - pixel_y = 6 - }, -/obj/item/food/grown/harebell{ - pixel_x = 3; - pixel_y = 3 - }, -/turf/open/floor/iron/dark/diagonal, -/area/station/service/chapel) "uQl" = ( /obj/structure/reagent_dispensers/water_cooler, /obj/effect/turf_decal/siding/dark{ @@ -143457,11 +143453,6 @@ /obj/structure/flora/rock/style_2, /turf/open/misc/sandy_dirt, /area/station/security/brig) -"vhe" = ( -/obj/structure/table/glass, -/obj/item/food/grown/poppy/geranium, -/turf/open/floor/iron/dark/herringbone, -/area/station/service/hydroponics) "vhm" = ( /obj/structure/table/reinforced, /obj/structure/desk_bell{ @@ -144244,15 +144235,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/dark/small, /area/station/maintenance/central) -"vlM" = ( -/obj/machinery/ai_slipper{ - uses = 8 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark/small, -/area/station/ai/upload/chamber) "vlP" = ( /obj/structure/hedge, /obj/structure/railing{ @@ -146805,13 +146787,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron/cafeteria, /area/station/commons/dorms/laundry) -"vFX" = ( -/obj/structure/table/glass, -/obj/item/food/grown/rose{ - pixel_y = 8 - }, -/turf/open/floor/iron/dark/herringbone, -/area/station/service/hydroponics) "vGe" = ( /obj/effect/turf_decal/siding/dark{ dir = 8 @@ -150185,6 +150160,15 @@ dir = 1 }, /area/station/commons/dorms) +"whQ" = ( +/obj/structure/table, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/effect/turf_decal/tile/green/half/contrasted, +/obj/machinery/newscaster/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "whR" = ( /obj/effect/turf_decal/siding/wideplating_new/dark, /obj/structure/railing, @@ -152134,6 +152118,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark/small, /area/station/science/robotics/mechbay) +"wxq" = ( +/obj/effect/turf_decal/siding/thinplating_new/dark{ + dir = 9 + }, +/obj/effect/turf_decal/trimline/red/filled/corner, +/obj/effect/turf_decal/siding/thinplating_new/dark/corner, +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/structure/sign/poster/official/bless_this_spess/directional/west, +/turf/open/floor/iron/dark/textured, +/area/station/security/prison) "wxu" = ( /obj/structure/hedge, /obj/machinery/light/directional/north, @@ -177803,7 +177800,7 @@ gym gym gym spU -uQk +dgB vRQ oaI tqf @@ -178317,7 +178314,7 @@ gym gym gym spU -bpZ +uDj jjS deE fUz @@ -190124,7 +190121,7 @@ siR siR hwz lIu -jLW +uAZ njb fTR wls @@ -201170,7 +201167,7 @@ cQl wJf wRM cZw -iJd +nfd rBC tNR vZs @@ -216107,9 +216104,9 @@ irx woC fNa uKJ -jke +rdG iTr -oOX +upF irx rEV exU @@ -216364,8 +216361,8 @@ irx grJ nhl rbI -gnZ -vlM +qLQ +qEo gNE pTC dsB @@ -216621,7 +216618,7 @@ irx woC cTd tgE -rop +hGA hNt jUX irx @@ -217135,7 +217132,7 @@ irx irx irx jPI -pWA +obw egi nYF irx @@ -237924,8 +237921,8 @@ txW gtn igh vej -qdw -ted +uCv +okc nPV txW txW @@ -238182,7 +238179,7 @@ gtn igh igh oKM -ooJ +aoU txW txW txW @@ -238206,7 +238203,7 @@ oNf pCa yhl yhl -kBI +qMx dOw dBF tlb @@ -238728,7 +238725,7 @@ tSN fGo cyu icw -ufP +omX ogB mJl wjK @@ -240260,7 +240257,7 @@ txW txW txW gTL -dac +sBd nZu pDZ nZu @@ -241290,7 +241287,7 @@ dXn gXd nZu fDc -fPj +mVV tPN nZu rta @@ -244374,7 +244371,7 @@ dtb xKy agJ hVc -vFX +iSe kOI dmT sTp @@ -244631,7 +244628,7 @@ dtb xKy nPV hVc -qlJ +oFe hUj jqK jCV @@ -244888,7 +244885,7 @@ xKy igQ nPV hVc -vhe +qUO dNG fTW iHJ @@ -265230,7 +265227,7 @@ xZZ wLh dlI lIM -uuG +scA rbo wCe nNO @@ -267783,7 +267780,7 @@ cTY rqD gMr bBC -dQv +whQ eUv rpI rvl @@ -269099,9 +269096,9 @@ saU iaN aeb wYi -cXw +wxq nZq -meC +cMJ mHT mHT lOr @@ -269306,7 +269303,7 @@ rvc sBX xKE oDQ -bZk +pPg xGM mrt ekk diff --git a/_maps/map_files/Vampire/generic/forest.dmm b/_maps/map_files/Vampire/generic/forest.dmm index 70705ddef6d9..82756ed1a744 100644 --- a/_maps/map_files/Vampire/generic/forest.dmm +++ b/_maps/map_files/Vampire/generic/forest.dmm @@ -804,7 +804,7 @@ /turf/open/misc/grass, /area/vtm/interior/forest/cave) "fm" = ( -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /obj/effect/turf_decal/darkpack/dirt{ dir = 10 }, @@ -2102,7 +2102,7 @@ /area/vtm/outside/forest/sept) "pO" = ( /obj/structure/flora/bush/flowers_yw/style_random, -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /obj/structure/flora/bush/flowers_br/style_random, /turf/open/misc/dirt, /area/vtm/outside/forest) @@ -2305,7 +2305,7 @@ /area/vtm/interior/forest/sept) "rd" = ( /obj/structure/flora/bush/flowers_yw/style_random, -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /turf/open/misc/grass, /area/vtm/outside/forest) "re" = ( @@ -4938,7 +4938,7 @@ "KJ" = ( /obj/structure/flora/bush/flowers_br/style_random, /obj/structure/flora/bush/flowers_pp/style_random, -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /obj/effect/realistic_fog/dense, /turf/open/misc/grass, /area/vtm/outside/forest) @@ -5834,7 +5834,7 @@ /area/vtm/outside/forest) "Ry" = ( /obj/structure/flora/bush/flowers_yw/style_random, -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /obj/effect/turf_decal/darkpack/dirt{ dir = 6 }, diff --git a/_maps/map_files/Vampire/special_fran/special_fran_umbra.dmm b/_maps/map_files/Vampire/special_fran/special_fran_umbra.dmm index 479eb3034d7d..a82d78da5726 100644 --- a/_maps/map_files/Vampire/special_fran/special_fran_umbra.dmm +++ b/_maps/map_files/Vampire/special_fran/special_fran_umbra.dmm @@ -931,7 +931,7 @@ /turf/open/floor/city/plating_mono, /area/vtm/outside/penumbra) "Id" = ( -/obj/item/food/grown/moonflower{ +/obj/item/food/grown/flower/moonflower{ anchored = 1 }, /turf/open/misc/grass/random/grass, diff --git a/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm b/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm index 752fc6365e09..14ff8d7383f5 100644 --- a/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm +++ b/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm @@ -3164,7 +3164,7 @@ "akH" = ( /obj/structure/flora/bush/flowers_br/style_random, /obj/structure/flora/bush/flowers_pp/style_random, -/obj/item/food/grown/moonflower, +/obj/item/food/grown/flower/moonflower, /turf/open/misc/grass, /area/vtm/westfield_mall/forest) "akI" = ( @@ -17155,6 +17155,10 @@ /obj/effect/mapping_helpers/door/autoname, /turf/open/floor/iron/grimy, /area/vtm/westfield_mall/interior/art_store) +"lci" = ( +/obj/effect/landmark/start/darkpack/hecata/nonni, +/turf/open/floor/wood/smooth/old, +/area/vtm/interior/bianchiBank) "lra" = ( /obj/effect/turf_decal/bordur, /turf/open/floor/plating/roofwalk, @@ -57852,7 +57856,7 @@ aXr akW aQs ajj -aae +lci att azw aTq diff --git a/_maps/map_files/tramstation/maintenance_modules/barcargoupper_attachment_a_1.dmm b/_maps/map_files/tramstation/maintenance_modules/barcargoupper_attachment_a_1.dmm index 033eea79f616..53e5a3fd69a5 100644 --- a/_maps/map_files/tramstation/maintenance_modules/barcargoupper_attachment_a_1.dmm +++ b/_maps/map_files/tramstation/maintenance_modules/barcargoupper_attachment_a_1.dmm @@ -117,7 +117,7 @@ /obj/item/food/grown/banana, /obj/item/food/grown/banana, /obj/item/food/grown/banana, -/obj/item/food/grown/sunflower, +/obj/item/food/grown/flower/sunflower, /turf/open/floor/iron/white, /area/station/maintenance/department/cargo) "T" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 3f91df390e11..d5a0c31a3cde 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -70,11 +70,6 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/station/asteroid) -"aan" = ( -/turf/open/floor/iron/stairs/medium{ - dir = 8 - }, -/area/station/commons/vacant_room) "aao" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -109,11 +104,6 @@ }, /turf/open/floor/iron, /area/station/security/prison/safe) -"aaA" = ( -/obj/effect/turf_decal/sand/plating, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "aaD" = ( /obj/structure/ore_box, /turf/open/misc/asteroid/airless, @@ -1827,55 +1817,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/smooth, /area/station/maintenance/disposal) -"agt" = ( -/obj/machinery/computer/message_monitor{ - dir = 8 - }, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) -"agv" = ( -/obj/structure/cable, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/plating, -/area/station/commons/vacant_room) -"agw" = ( -/obj/structure/rack, -/obj/item/clothing/shoes/sneakers/black{ - pixel_x = 1; - pixel_y = -1 - }, -/obj/item/clothing/head/soft/black{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/clothing/gloves/fingerless, -/obj/item/clothing/shoes/sneakers/black{ - pixel_x = 1; - pixel_y = -1 - }, -/obj/item/clothing/gloves/fingerless, -/obj/item/clothing/head/soft/black{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/clothing/under/syndicate, -/obj/item/clothing/under/syndicate, -/obj/item/clothing/under/misc/syndicate_souvenir, -/turf/open/floor/iron/grimy, -/area/station/commons/vacant_room) "agy" = ( /obj/structure/alien/weeds, /turf/open/misc/asteroid/airless, /area/station/asteroid) -"agz" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "agA" = ( /obj/effect/turf_decal/sand/plating, /obj/effect/turf_decal/loading_area, @@ -1906,11 +1851,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/smooth, /area/station/maintenance/disposal) -"agG" = ( -/obj/structure/dresser, -/obj/item/cigarette/cigar/havana, -/turf/open/floor/iron/grimy, -/area/station/commons/vacant_room) "agH" = ( /obj/machinery/power/apc/auto_name/directional/west, /obj/effect/spawner/holiday/bar_keg, @@ -2268,14 +2208,6 @@ /obj/machinery/light/directional/east, /turf/open/floor/noslip/tram, /area/station/hallway/primary/tram/center) -"aiK" = ( -/obj/structure/toilet{ - pixel_y = 18 - }, -/obj/structure/sink/directional/east, -/obj/structure/mirror/directional/west, -/turf/open/floor/iron/showroomfloor, -/area/station/commons/vacant_room) "ajc" = ( /obj/machinery/button/door/directional/west{ name = "Privacy Bolts"; @@ -3238,6 +3170,11 @@ /obj/item/stock_parts/power_store/cell/high, /turf/open/floor/iron, /area/station/command/eva) +"asN" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/harebell, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "asQ" = ( /obj/structure/sign/warning/no_smoking, /turf/closed/wall, @@ -3261,10 +3198,6 @@ }, /turf/open/floor/plating, /area/station/science/ordnance/testlab) -"atl" = ( -/obj/structure/hoop, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "atr" = ( /turf/open/floor/iron/white/side{ dir = 4 @@ -3361,9 +3294,6 @@ /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/iron/grimy, /area/station/ai/satellite/foyer) -"auz" = ( -/turf/closed/wall, -/area/station/commons/vacant_room) "auA" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/neutral/filled/line, @@ -3992,12 +3922,6 @@ /obj/structure/chair/comfy/brown, /turf/open/floor/carpet, /area/station/command/heads_quarters/captain) -"aBI" = ( -/obj/machinery/computer/security{ - dir = 4 - }, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "aBK" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -6042,6 +5966,14 @@ dir = 4 }, /area/station/maintenance/central/greater) +"aXg" = ( +/obj/machinery/power/smes{ + charge = 5e+06 + }, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "aXo" = ( /obj/structure/punching_bag, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -6467,14 +6399,6 @@ /obj/effect/mapping_helpers/airlock/access/any/command/ai_upload, /turf/open/floor/iron/dark, /area/station/ai/satellite/interior) -"bhG" = ( -/obj/machinery/door/airlock/centcom{ - name = "Syndicate Secure Airlock System"; - desc = "Truly, a marvel of modern engineering." - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/iron/grimy, -/area/station/commons/vacant_room) "bhY" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 1 @@ -6589,6 +6513,16 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"bkw" = ( +/obj/item/paper/fluff/ruins/tram_syndicate/initiation, +/obj/machinery/computer/records/medical/syndie{ + dir = 1; + req_access = list("syndicate") + }, +/obj/machinery/digital_clock/directional/south, +/obj/machinery/light/directional/south, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "blg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/trimline/tram/filled/line{ @@ -7189,17 +7123,6 @@ /obj/structure/flora/bush/fullgrass/style_random, /turf/open/misc/grass/jungle/station, /area/station/science/explab) -"bxd" = ( -/obj/structure/table, -/obj/item/radio/intercom/directional/east{ - name = "Common Channel"; - pixel_x = 0; - listening = 0; - freerange = 1 - }, -/obj/structure/sign/calendar/directional/south, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "bxC" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 9 @@ -9565,6 +9488,13 @@ /obj/machinery/wall_healer/directional/west, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"cjo" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/turf/open/floor/iron/dark, +/area/station/service/chapel) "cjy" = ( /turf/closed/wall/r_wall, /area/station/engineering/main) @@ -10759,6 +10689,13 @@ }, /turf/open/floor/plating/elevatorshaft, /area/station/security/execution/transfer) +"cEK" = ( +/obj/effect/turf_decal/sand/plating, +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 1 + }, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "cFb" = ( /obj/structure/sign/warning/electric_shock, /turf/closed/wall/r_wall, @@ -11104,12 +11041,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/central/greater) -"cKm" = ( -/obj/structure/stairs/east, -/turf/open/floor/iron/stairs/medium{ - dir = 8 - }, -/area/station/commons/vacant_room) "cKE" = ( /obj/effect/decal/cleanable/dirt, /obj/item/weldingtool, @@ -12070,6 +12001,9 @@ /obj/structure/cable, /turf/open/floor/iron/grimy, /area/station/service/chapel/office) +"daV" = ( +/turf/open/floor/iron/grimy, +/area/ruin/space/has_grav/syndicate_outpost) "dbc" = ( /obj/effect/turf_decal/trimline/brown/filled/corner{ dir = 4 @@ -12086,15 +12020,6 @@ /obj/item/storage/toolbox/drone, /turf/open/floor/iron/dark, /area/station/security/armory) -"dbr" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 1 - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/obj/machinery/door/airlock/external/glass, -/obj/effect/turf_decal/sand/plating, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "dbu" = ( /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 @@ -12307,6 +12232,19 @@ /obj/item/stack/medical/bruise_pack, /turf/open/floor/iron, /area/station/commons/fitness) +"deL" = ( +/obj/structure/table/wood, +/obj/item/radio/intercom/directional/north, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/machinery/camera/directional/north{ + c_tag = "Civilian - Chapel Monastary" + }, +/turf/open/floor/carpet, +/area/station/service/chapel/monastery) "deU" = ( /obj/structure/closet/emcloset, /obj/machinery/light/dim/directional/west, @@ -13291,10 +13229,6 @@ }, /turf/open/floor/iron/white, /area/station/science/lower) -"dxT" = ( -/obj/machinery/oven/range, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "dyb" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -13373,6 +13307,18 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/secondary/service) +"dyF" = ( +/obj/machinery/flasher/directional/west{ + id = "AI" + }, +/obj/machinery/camera/motion/directional/west{ + c_tag = "Secure - AI Upload" + }, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "dyI" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -13695,6 +13641,13 @@ "dEv" = ( /turf/closed/wall/r_wall, /area/station/security/armory) +"dED" = ( +/obj/structure/table, +/obj/machinery/computer/libraryconsole/bookmanagement{ + dir = 8 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "dEH" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock/external{ @@ -14223,17 +14176,6 @@ /obj/machinery/light/directional/north, /turf/open/floor/iron/white, /area/station/science/ordnance/office) -"dNT" = ( -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/table/wood/fancy/blue, -/obj/item/ai_module/supplied/freeform, -/obj/effect/spawner/random/aimodule/neutral, -/obj/machinery/door/window/left/directional/east{ - name = "Neutral Modules"; - req_access = list("captain") - }, -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) "dNY" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/neutral/filled/line{ @@ -14351,19 +14293,6 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) -"dPT" = ( -/obj/structure/table/wood, -/obj/item/radio/intercom/directional/north, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/machinery/camera/directional/north{ - c_tag = "Civilian - Chapel Monastary" - }, -/turf/open/floor/carpet, -/area/station/service/chapel/monastery) "dQd" = ( /obj/structure/table, /obj/item/storage/box/teargas{ @@ -15310,6 +15239,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, /turf/open/floor/iron/dark, /area/station/science/xenobiology) +"eiL" = ( +/obj/machinery/status_display/ai/directional/east, +/obj/structure/table/wood/fancy/green, +/obj/effect/spawner/random/aimodule/harmless, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "eiR" = ( /obj/structure/disposalpipe/segment{ dir = 10 @@ -15716,6 +15651,12 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/engineering/atmospherics_engine) +"era" = ( +/obj/structure/sink/kitchen/directional/south, +/turf/open/floor/iron/white/smooth_edge{ + dir = 1 + }, +/area/ruin/space/has_grav/syndicate_outpost) "ers" = ( /obj/effect/turf_decal/trimline/neutral/filled/corner, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -16004,15 +15945,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/cargo/miningdock) -"exv" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 1 - }, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "exx" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -16941,6 +16873,18 @@ }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) +"eOX" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "eOZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply, /turf/closed/wall/r_wall, @@ -17088,6 +17032,10 @@ /obj/effect/turf_decal/trimline/white/warning, /turf/open/floor/iron, /area/station/maintenance/tram/mid) +"eRN" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "eRQ" = ( /obj/machinery/camera{ dir = 6; @@ -17142,13 +17090,6 @@ }, /turf/open/floor/iron, /area/station/command/bridge) -"eSI" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/components/tank/air{ - dir = 1 - }, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "eSM" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -18102,6 +18043,15 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) +"fkp" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 1 + }, +/obj/structure/railing/corner/end{ + dir = 8 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "fkL" = ( /obj/machinery/atmospherics/components/binary/pump{ name = "Plasma Outlet Pump"; @@ -18421,17 +18371,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/brig) -"fpj" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/structure/closet/crate/bin, -/obj/effect/spawner/random/trash/food_packaging, -/obj/effect/spawner/random/trash/food_packaging, -/obj/effect/spawner/random/trash/food_packaging, -/obj/effect/spawner/random/trash/cigbutt, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "fpQ" = ( /obj/effect/turf_decal/trimline/red/filled/corner, /obj/structure/cable, @@ -18903,6 +18842,13 @@ }, /turf/open/floor/iron, /area/station/service/theater) +"fyM" = ( +/obj/machinery/modular_computer/preset/civilian{ + dir = 8 + }, +/obj/structure/window/spawner/directional/north, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "fyR" = ( /obj/structure/flora/bush/sparsegrass/style_random, /obj/machinery/light/floor, @@ -19138,12 +19084,16 @@ /obj/machinery/light/warm/directional/south, /turf/open/misc/grass/jungle/station, /area/station/science/explab) -"fEQ" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/obj/machinery/digital_clock/directional/south, +"fEN" = ( +/obj/machinery/firealarm/directional/north, +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, /turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) +/area/ruin/space/has_grav/syndicate_outpost) "fEV" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -19363,6 +19313,16 @@ dir = 8 }, /area/station/science/lobby) +"fIu" = ( +/obj/structure/table, +/obj/item/radio/intercom/directional/east{ + name = "Custom Channel"; + pixel_x = 0; + listening = 0; + freerange = 1 + }, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "fIB" = ( /obj/structure/table, /obj/machinery/recharger{ @@ -20212,13 +20172,6 @@ /obj/structure/sign/calendar/directional/north, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/cmo) -"fYw" = ( -/obj/machinery/computer/upload/borg/no_lock{ - dir = 8 - }, -/obj/machinery/status_display/ai/directional/east, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "fYB" = ( /obj/effect/turf_decal/sand/plating, /turf/open/floor/catwalk_floor, @@ -20427,9 +20380,6 @@ /obj/item/kirbyplants/organic/plant10, /turf/open/floor/iron/white, /area/station/science/lab) -"gcp" = ( -/turf/closed/wall/r_wall, -/area/station/commons/vacant_room) "gcx" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/tile/purple/fourcorners, @@ -20445,6 +20395,17 @@ /obj/item/radio/intercom/directional/north, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"gcG" = ( +/obj/structure/window/reinforced/spawner/directional/west, +/obj/structure/table/wood/fancy/blue, +/obj/item/ai_module/law/supplied/freeform, +/obj/effect/spawner/random/aimodule/neutral, +/obj/machinery/door/window/left/directional/east{ + name = "Neutral Modules"; + req_access = list("captain") + }, +/turf/open/floor/circuit, +/area/station/ai/upload/chamber) "gcJ" = ( /obj/machinery/door/airlock/external{ name = "Solar Maintenance" @@ -20562,12 +20523,6 @@ "gfK" = ( /turf/closed/wall/r_wall, /area/station/security/execution/education) -"gfV" = ( -/obj/structure/table/wood/fancy/green, -/obj/effect/spawner/round_default_module, -/obj/machinery/camera/motion/directional/east, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "gfX" = ( /obj/structure/fluff/iced_abductor, /turf/open/misc/asteroid/airless, @@ -20636,13 +20591,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/maintenance/port/central) -"ggV" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "ghg" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/catwalk_floor, @@ -20853,6 +20801,15 @@ }, /turf/open/misc/grass/jungle/station, /area/station/science/explab) +"gkO" = ( +/obj/structure/bed{ + dir = 4 + }, +/obj/item/bedsheet/syndie{ + dir = 4 + }, +/turf/open/floor/iron/grimy, +/area/ruin/space/has_grav/syndicate_outpost) "gkQ" = ( /obj/machinery/computer/atmos_control/mix_tank{ dir = 8 @@ -21008,12 +20965,6 @@ }, /turf/open/space/openspace, /area/station/solars/port) -"gnq" = ( -/obj/structure/railing/corner{ - dir = 4 - }, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "gnr" = ( /obj/structure/table, /obj/item/storage/box/bodybags{ @@ -22336,9 +22287,9 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) -"gNy" = ( +"gNC" = ( /turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) +/area/ruin/space/has_grav/syndicate_outpost) "gNE" = ( /obj/structure/bed/medical{ dir = 4 @@ -22349,6 +22300,10 @@ }, /turf/open/floor/iron/white, /area/station/medical/treatment_center) +"gNG" = ( +/obj/structure/chair/office, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "gNK" = ( /obj/structure/table/reinforced, /obj/machinery/door/window/brigdoor/left/directional/north{ @@ -23469,6 +23424,12 @@ }, /turf/open/floor/wood, /area/station/command/meeting_room) +"hir" = ( +/obj/structure/filingcabinet, +/obj/structure/window/spawner/directional/north, +/obj/item/paper/fluff/ruins/tram_syndicate/blueprints, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "hiG" = ( /obj/effect/turf_decal/sand, /obj/effect/turf_decal/trimline/red/filled/line{ @@ -23605,12 +23566,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/checker, /area/station/commons/lounge) -"hkF" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/ai_module/reset, -/turf/open/floor/iron/dark, -/area/station/engineering/storage/tech) "hkN" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/simple/purple/visible{ @@ -23806,10 +23761,6 @@ /obj/machinery/portable_atmospherics/canister/nitrous_oxide, /turf/open/floor/iron/dark, /area/station/science/ordnance/storage) -"hoA" = ( -/obj/effect/landmark/firealarm_sanity, -/turf/open/openspace, -/area/station/commons/vacant_room) "hoB" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -25998,14 +25949,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/cafeteria, /area/station/science/breakroom) -"igV" = ( -/obj/structure/closet/emcloset/anchored, -/obj/effect/turf_decal/sand/plating, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 - }, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "ihb" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 9 @@ -26228,6 +26171,12 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"ilO" = ( +/obj/machinery/oven/range, +/turf/open/floor/iron/white/smooth_corner{ + dir = 1 + }, +/area/ruin/space/has_grav/syndicate_outpost) "ilS" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 8 @@ -26292,6 +26241,13 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"inE" = ( +/obj/machinery/firealarm/directional/north, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 8 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "inK" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Recreation Area Maintenance Access" @@ -26554,16 +26510,6 @@ }, /turf/open/floor/iron/dark, /area/station/medical/break_room) -"isP" = ( -/obj/structure/table, -/obj/item/radio/intercom/directional/east{ - name = "Custom Channel"; - pixel_x = 0; - listening = 0; - freerange = 1 - }, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "isV" = ( /obj/machinery/firealarm/directional/west, /obj/effect/turf_decal/trimline/red/filled/line{ @@ -26656,6 +26602,17 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper, /turf/open/floor/plating, /area/station/hallway/secondary/entry) +"iuD" = ( +/obj/structure/table, +/obj/item/radio/intercom/directional/east{ + name = "Common Channel"; + pixel_x = 0; + listening = 0; + freerange = 1 + }, +/obj/structure/sign/calendar/directional/south, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "iuJ" = ( /obj/structure/bed/double, /obj/effect/spawner/random/bedsheet/double, @@ -26837,13 +26794,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/catwalk_floor, /area/station/maintenance/tram/mid) -"ixC" = ( -/obj/machinery/computer/upload/ai/no_lock{ - dir = 8 - }, -/obj/machinery/status_display/evac/directional/east, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "ixK" = ( /obj/machinery/airalarm/directional/north, /obj/machinery/duct, @@ -27247,6 +27197,14 @@ /obj/structure/cable, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"iGC" = ( +/obj/machinery/power/apc/auto_name/directional/west, +/obj/structure/cable, +/obj/machinery/ai_slipper{ + uses = 8 + }, +/turf/open/floor/circuit, +/area/station/ai/upload/chamber) "iGF" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 8 @@ -27658,14 +27616,6 @@ /obj/machinery/incident_display/tram/directional/south, /turf/open/floor/iron, /area/station/hallway/primary/tram/center) -"iOC" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/obj/machinery/door/airlock/external/glass, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "iOG" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 4 @@ -27859,10 +27809,6 @@ /obj/structure/cable, /turf/open/floor/wood, /area/station/service/theater) -"iSw" = ( -/obj/machinery/griddle, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "iSC" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/neutral/filled/line{ @@ -28362,10 +28308,6 @@ /obj/structure/railing/corner, /turf/open/space/openspace, /area/station/solars/starboard/fore) -"jap" = ( -/obj/structure/chair/office, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "jav" = ( /obj/structure/alien/weeds/node, /turf/open/misc/asteroid/airless, @@ -28538,6 +28480,20 @@ }, /turf/open/floor/iron, /area/station/security/prison/safe) +"jdu" = ( +/obj/structure/table, +/obj/item/storage/bag/tray, +/obj/item/kitchen/rollingpin{ + pixel_x = 6 + }, +/obj/item/reagent_containers/condiment/enzyme{ + pixel_x = -5; + pixel_y = 9 + }, +/obj/structure/window/spawner/directional/west, +/obj/item/knife/kitchen, +/turf/open/floor/iron/white/smooth_corner, +/area/ruin/space/has_grav/syndicate_outpost) "jdF" = ( /obj/structure/railing, /obj/effect/turf_decal/trimline/blue/filled/line, @@ -29355,6 +29311,14 @@ /obj/structure/sign/warning/electric_shock/directional/north, /turf/open/floor/plating, /area/station/science/xenobiology) +"jqN" = ( +/obj/machinery/door/airlock/centcom{ + name = "Syndicate Secure Airlock System"; + desc = "Truly, a marvel of modern engineering." + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/turf/open/floor/iron/showroomfloor, +/area/ruin/space/has_grav/syndicate_outpost) "jqR" = ( /obj/structure/table, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -30073,6 +30037,10 @@ /obj/machinery/power/apc/auto_name/directional/north, /turf/open/floor/iron, /area/station/maintenance/tram/mid) +"jCr" = ( +/obj/structure/extinguisher_cabinet/directional/south, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "jCH" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/effect/turf_decal/tile/bar/opposingcorners, @@ -30280,16 +30248,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/grass, /area/station/medical/virology) -"jFt" = ( -/obj/structure/railing{ - dir = 4 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, -/obj/structure/tank_dispenser/oxygen, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "jFC" = ( /obj/machinery/vending/hydroseeds{ slogan_delay = 700 @@ -30485,6 +30443,11 @@ "jJv" = ( /turf/open/floor/circuit, /area/station/ai/satellite/chamber) +"jJw" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/obj/machinery/light_switch/directional/south, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "jJz" = ( /obj/structure/chair{ dir = 8 @@ -32363,10 +32326,6 @@ }, /turf/open/floor/plating/elevatorshaft, /area/station/science/xenobiology) -"knl" = ( -/obj/structure/sink/kitchen/directional/south, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "knA" = ( /obj/machinery/door/airlock/command{ name = "Head of Personnel" @@ -33398,15 +33357,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/tram/mid) -"kFs" = ( -/obj/structure/bed{ - dir = 4 - }, -/obj/item/bedsheet/syndie{ - dir = 4 - }, -/turf/open/floor/iron/grimy, -/area/station/commons/vacant_room) "kFu" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 10 @@ -33872,11 +33822,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) -"kMm" = ( -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/turf/open/floor/iron/dark, -/area/station/service/chapel) "kMn" = ( /obj/effect/turf_decal/trimline/brown/filled/line, /obj/effect/turf_decal/trimline/brown/filled/corner{ @@ -34971,6 +34916,15 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"leD" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/stripes/line{ + dir = 6 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/obj/machinery/airalarm/directional/west, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "leX" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/sorting/mail{ @@ -35873,14 +35827,6 @@ }, /turf/open/floor/iron/dark, /area/station/commons/lounge) -"lsy" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/machinery/computer/libraryconsole/bookmanagement, -/obj/structure/table, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "lsJ" = ( /obj/effect/turf_decal/trimline/blue/filled/line, /obj/machinery/camera/directional/south{ @@ -37616,6 +37562,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"lVA" = ( +/obj/machinery/computer/security{ + dir = 4 + }, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "lVC" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -38032,6 +37984,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet, /area/station/service/library) +"mcY" = ( +/obj/machinery/power/port_gen/pacman{ + anchored = 1 + }, +/obj/structure/cable, +/obj/effect/turf_decal/delivery, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "mdf" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -38558,6 +38519,23 @@ /obj/machinery/meter, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"mmJ" = ( +/obj/machinery/status_display/evac/directional/east, +/obj/structure/table/wood/fancy/green, +/obj/effect/spawner/round_default_module, +/obj/item/ai_module/law/supplied/freeform{ + pixel_x = 2; + pixel_y = 3 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 1; + pixel_x = -2 + }, +/obj/item/ai_module/law/supplied/freeform{ + pixel_y = 2 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "mns" = ( /obj/effect/turf_decal/trimline/brown/filled/corner{ dir = 1 @@ -38740,6 +38718,11 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"mqM" = ( +/turf/open/floor/iron/stairs/medium{ + dir = 8 + }, +/area/ruin/space/has_grav/syndicate_outpost) "mrf" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -39252,6 +39235,10 @@ }, /turf/open/floor/iron/dark/airless, /area/station/science/ordnance/freezerchamber) +"mCn" = ( +/obj/effect/landmark/atmospheric_sanity/ignore_area, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "mCu" = ( /obj/machinery/light/directional/west, /turf/open/floor/noslip/tram, @@ -39305,6 +39292,16 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"mCY" = ( +/obj/structure/cable, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "mDl" = ( /obj/machinery/recharger{ pixel_y = 4 @@ -39477,6 +39474,26 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/white, /area/station/medical/medbay/central) +"mGh" = ( +/obj/structure/table/wood, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/item/food/grown/flower/poppy{ + pixel_y = 2 + }, +/obj/machinery/light/cold/directional/north, +/turf/open/floor/carpet, +/area/station/service/chapel/monastery) "mGl" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/circuit, @@ -39696,20 +39713,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/commons/dorms) -"mJj" = ( -/obj/structure/table, -/obj/item/storage/bag/tray, -/obj/item/kitchen/rollingpin{ - pixel_x = 6 - }, -/obj/item/reagent_containers/condiment/enzyme{ - pixel_x = -5; - pixel_y = 9 - }, -/obj/structure/window/spawner/directional/west, -/obj/item/knife/kitchen, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "mJm" = ( /obj/effect/spawner/random/engineering/tracking_beacon, /obj/effect/turf_decal/tile/blue/fourcorners, @@ -39794,6 +39797,13 @@ /obj/effect/turf_decal/tile/dark_green/fourcorners, /turf/open/floor/iron/white, /area/station/science/genetics) +"mLa" = ( +/obj/structure/table, +/obj/item/plate, +/obj/item/kitchen/fork, +/obj/machinery/computer/security/telescreen/entertainment/directional/west, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "mLh" = ( /obj/effect/turf_decal/siding/dark{ dir = 10 @@ -39873,6 +39883,13 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/station/maintenance/central/greater) +"mNc" = ( +/obj/item/toy/basketball{ + pixel_x = 8; + pixel_y = 14 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "mNp" = ( /obj/structure/chair/sofa/middle{ dir = 8 @@ -40100,6 +40117,13 @@ /obj/structure/lattice/catwalk, /turf/open/openspace/airless, /area/station/asteroid) +"mSK" = ( +/obj/item/radio/intercom/directional/east{ + name = "ai intercom"; + frequency = 1447 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "mST" = ( /obj/structure/cable, /turf/open/floor/catwalk_floor, @@ -40152,13 +40176,6 @@ dir = 1 }, /area/station/service/kitchen) -"mVj" = ( -/obj/machinery/modular_computer/preset/civilian{ - dir = 8 - }, -/obj/structure/window/spawner/directional/north, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "mVp" = ( /obj/effect/turf_decal/trimline/blue/filled/line, /obj/machinery/holopad, @@ -40405,13 +40422,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) -"naI" = ( -/obj/item/toy/basketball{ - pixel_x = 8; - pixel_y = 14 - }, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "naN" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/grunge{ @@ -40467,11 +40477,6 @@ /obj/effect/turf_decal/trimline/tram/filled/warning, /turf/open/floor/iron, /area/station/hallway/primary/tram/right) -"nbF" = ( -/obj/machinery/power/apc/auto_name/directional/west, -/obj/structure/cable, -/turf/open/floor/circuit, -/area/station/ai/upload/chamber) "nbI" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/preopen{ @@ -40829,9 +40834,6 @@ }, /turf/open/floor/iron/stairs/right, /area/station/hallway/secondary/construction/engineering) -"nim" = ( -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "niF" = ( /obj/structure/railing/corner, /obj/structure/window/reinforced/spawner/directional/north, @@ -41061,25 +41063,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/security/evidence) -"nlf" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 5 - }, -/obj/structure/table/glass, -/obj/machinery/requests_console/directional/east{ - name = "Virology Requests Console"; - department = "Virology" - }, -/obj/effect/mapping_helpers/requests_console/ore_update, -/obj/item/storage/box/beakers{ - pixel_x = 2; - pixel_y = 2 - }, -/obj/item/storage/box/syringes, -/obj/structure/sign/clock/directional/north, -/obj/item/storage/pill_bottle/sepsisillin, -/turf/open/floor/iron/white, -/area/station/medical/virology) "nlm" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock/external{ @@ -41331,6 +41314,17 @@ /obj/structure/sign/calendar/directional/north, /turf/open/floor/iron, /area/station/security/checkpoint/escape) +"noK" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/obj/machinery/door/airlock/external, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 1 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "noW" = ( /obj/structure/table/optable{ id_tag = "lowersurgery" @@ -41411,17 +41405,6 @@ }, /turf/open/floor/glass, /area/station/commons/fitness/recreation) -"nrf" = ( -/obj/structure/window/reinforced/spawner/directional/west, -/obj/structure/table/wood/fancy/red, -/obj/item/ai_module/reset/purge, -/obj/effect/spawner/random/aimodule/harmful, -/obj/machinery/door/window/left/directional/east{ - name = "Dangerous Modules"; - req_access = list("captain") - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "nrh" = ( /obj/structure/table/glass, /obj/item/storage/fancy/coffee_cart_rack{ @@ -42265,6 +42248,13 @@ }, /turf/open/floor/iron/white, /area/station/security/medical) +"nHp" = ( +/obj/machinery/camera/motion/directional/east, +/obj/machinery/ai_law_rack/base/core{ + pixel_y = 12 + }, +/turf/open/floor/circuit/green, +/area/station/ai/upload/chamber) "nHu" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 @@ -42385,6 +42375,25 @@ }, /turf/open/floor/iron/white, /area/station/science/ordnance/office) +"nJU" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 5 + }, +/obj/structure/table/glass, +/obj/machinery/requests_console/directional/east{ + name = "Virology Requests Console"; + department = "Virology" + }, +/obj/effect/mapping_helpers/requests_console/ore_update, +/obj/item/storage/box/beakers{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/storage/box/syringes, +/obj/structure/sign/clock/directional/north, +/obj/item/storage/pill_bottle/sepsisillin, +/turf/open/floor/iron/white, +/area/station/medical/virology) "nKb" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 10 @@ -42559,6 +42568,9 @@ /obj/item/radio/intercom/directional/north, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"nNe" = ( +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "nNh" = ( /obj/structure/flora/bush/sunny/style_random, /turf/open/misc/grass/jungle/station, @@ -43320,9 +43332,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) -"oae" = ( -/turf/open/floor/iron/grimy, -/area/station/commons/vacant_room) "oai" = ( /obj/effect/turf_decal/tile/neutral/tram, /obj/effect/landmark/transport/nav_beacon/tram/platform/tramstation/west, @@ -43514,6 +43523,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/security/prison/work) +"oef" = ( +/obj/structure/closet/emcloset/anchored, +/obj/effect/turf_decal/sand/plating, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "oeP" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 @@ -43724,18 +43738,6 @@ }, /turf/open/floor/iron, /area/station/cargo/miningdock/cafeteria) -"oiL" = ( -/obj/structure/filingcabinet, -/obj/structure/window/spawner/directional/north, -/obj/item/paper{ - name = "station layout"; - icon = 'icons/obj/scrolls.dmi'; - icon_state = "blueprints"; - default_raw_text = "A crude mapping of the station layout based on leaked internal documents and orbital snapshots taken during construction. I'm not sure how up-to-date this is anymore.."; - inhand_icon_state = "blueprints" - }, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "oiV" = ( /obj/effect/turf_decal/trimline/neutral/filled/corner{ dir = 1 @@ -43759,6 +43761,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood/large, /area/station/service/library) +"ojJ" = ( +/obj/machinery/door/airlock/centcom{ + name = "Syndicate Secure Airlock System"; + desc = "Truly, a marvel of modern engineering." + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/turf/open/floor/iron/grimy, +/area/ruin/space/has_grav/syndicate_outpost) "ojQ" = ( /obj/effect/turf_decal/trimline/blue/filled/corner, /obj/effect/turf_decal/trimline/blue/filled/corner{ @@ -43983,6 +43993,16 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) +"ooe" = ( +/obj/structure/railing/corner{ + dir = 4 + }, +/obj/machinery/light/small/dim/directional/west, +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "oog" = ( /obj/machinery/disposal/bin, /obj/effect/turf_decal/trimline/yellow/filled/line{ @@ -44377,6 +44397,15 @@ /obj/effect/spawner/random/structure/tank_holder, /turf/open/floor/iron/dark, /area/station/cargo/drone_bay) +"owo" = ( +/obj/structure/railing/corner/end/flip{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "owI" = ( /obj/machinery/camera/directional/west{ c_tag = "Hallway - Port Tram Platform North-West" @@ -44917,6 +44946,9 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/white, /area/station/medical/surgery/fore) +"oJA" = ( +/turf/open/floor/iron/white, +/area/ruin/space/has_grav/syndicate_outpost) "oKe" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, @@ -45129,26 +45161,6 @@ /obj/effect/turf_decal/trimline/neutral/filled/corner, /turf/open/floor/iron, /area/station/hallway/secondary/exit) -"oOT" = ( -/obj/structure/table/wood, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/item/food/grown/poppy{ - pixel_y = 2 - }, -/obj/machinery/light/cold/directional/north, -/turf/open/floor/carpet, -/area/station/service/chapel/monastery) "oOZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -45816,10 +45828,6 @@ "pcu" = ( /turf/closed/wall/r_wall, /area/station/maintenance/tram/mid) -"pcA" = ( -/obj/machinery/firealarm/directional/north, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "pcE" = ( /obj/structure/chair{ dir = 8 @@ -46096,6 +46104,16 @@ /obj/structure/railing, /turf/open/space/openspace, /area/station/solars/port) +"pim" = ( +/obj/structure/railing{ + dir = 4 + }, +/obj/structure/tank_dispenser/oxygen, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "piv" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 4 @@ -46246,14 +46264,6 @@ /obj/structure/window/reinforced/spawner/directional/west, /turf/open/floor/carpet, /area/station/command/bridge) -"pkQ" = ( -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, -/obj/machinery/firealarm/directional/north, -/obj/effect/landmark/firealarm_sanity, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "plh" = ( /obj/effect/turf_decal/siding/thinplating/corner{ dir = 4 @@ -46288,15 +46298,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/wood, /area/station/service/bar/backroom) -"plz" = ( -/obj/machinery/power/port_gen/pacman{ - anchored = 1 - }, -/obj/structure/cable, -/obj/effect/turf_decal/delivery, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "plQ" = ( /obj/effect/turf_decal/trimline/red/filled/corner, /obj/structure/disposalpipe/segment{ @@ -48581,6 +48582,18 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/white, /area/station/medical/chemistry) +"pXN" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/obj/machinery/door/airlock/external, +/obj/effect/turf_decal/sand/plating, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 1 + }, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "pXZ" = ( /obj/effect/turf_decal/trimline/yellow/filled/corner{ dir = 8 @@ -49250,6 +49263,9 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/warden) +"qjJ" = ( +/turf/open/openspace, +/area/ruin/space/has_grav/syndicate_outpost) "qjL" = ( /obj/structure/window/spawner/directional/east, /obj/structure/window/spawner/directional/west, @@ -49477,11 +49493,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/cargo/drone_bay) -"qoD" = ( -/obj/structure/table, -/obj/machinery/microwave, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "qoV" = ( /obj/effect/turf_decal/sand/plating, /turf/open/floor/catwalk_floor, @@ -49726,12 +49737,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/tram/right) -"qtM" = ( -/obj/machinery/shower/directional/north, -/obj/structure/curtain, -/obj/item/soap/syndie, -/turf/open/floor/iron/showroomfloor, -/area/station/commons/vacant_room) "qtS" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ @@ -50086,9 +50091,6 @@ /obj/structure/chair/sofa/bench/tram/right, /turf/open/openspace, /area/station/hallway/primary/tram/center) -"qBq" = ( -/turf/open/openspace, -/area/station/commons/vacant_room) "qBw" = ( /obj/machinery/holopad, /obj/effect/landmark/navigate_destination/bridge, @@ -50641,6 +50643,10 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/science/server) +"qKR" = ( +/obj/structure/extinguisher_cabinet/directional/south, +/turf/open/floor/iron/white/smooth_edge, +/area/ruin/space/has_grav/syndicate_outpost) "qKW" = ( /obj/structure/table/reinforced, /obj/machinery/keycard_auth/wall_mounted/directional/south, @@ -51181,6 +51187,32 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/white, /area/station/command/heads_quarters/cmo) +"qUP" = ( +/obj/structure/rack, +/obj/item/clothing/shoes/sneakers/black{ + pixel_x = 1; + pixel_y = -1 + }, +/obj/item/clothing/head/soft/black{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/clothing/gloves/fingerless, +/obj/item/clothing/shoes/sneakers/black{ + pixel_x = 1; + pixel_y = -1 + }, +/obj/item/clothing/gloves/fingerless, +/obj/item/clothing/head/soft/black{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/clothing/under/syndicate, +/obj/item/clothing/under/syndicate, +/obj/item/clothing/under/misc/syndicate_souvenir, +/obj/machinery/light/small/directional/north, +/turf/open/floor/iron/grimy, +/area/ruin/space/has_grav/syndicate_outpost) "qUS" = ( /obj/machinery/vending/medical, /obj/effect/turf_decal/siding/white{ @@ -51522,6 +51554,12 @@ /obj/machinery/power/apc/auto_name/directional/west, /turf/open/floor/iron, /area/station/hallway/secondary/exit) +"rai" = ( +/obj/structure/chair{ + dir = 8 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "ram" = ( /obj/machinery/atmospherics/pipe/smart/manifold/violet/visible{ dir = 4 @@ -52029,19 +52067,6 @@ /obj/effect/landmark/start/quartermaster, /turf/open/floor/carpet, /area/station/command/heads_quarters/qm) -"riN" = ( -/obj/structure/table, -/obj/effect/spawner/random/food_or_drink/refreshing_beverage, -/obj/item/reagent_containers/condiment/saltshaker{ - pixel_x = -9; - pixel_y = 12 - }, -/obj/item/reagent_containers/condiment/peppermill{ - pixel_x = -1; - pixel_y = 13 - }, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "riS" = ( /obj/machinery/door/firedoor, /obj/effect/turf_decal/trimline/neutral/filled/line{ @@ -52148,6 +52173,19 @@ /obj/effect/turf_decal/trimline/green/filled/line, /turf/open/floor/iron/dark, /area/station/security/prison/garden) +"rkU" = ( +/obj/structure/table, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/item/aicard, +/obj/item/circuitboard/aicore, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/item/circuitboard/machine/ai_law_rack/small{ + pixel_y = -2 + }, +/turf/open/floor/iron/cafeteria, +/area/station/command/heads_quarters/rd) "rle" = ( /obj/effect/turf_decal/trimline/dark_green/filled/corner{ dir = 1 @@ -52832,6 +52870,23 @@ /obj/structure/transport/linear/public, /turf/open/floor/plating/elevatorshaft, /area/station/maintenance/tram/mid) +"rwM" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/closet/crate/bin, +/obj/effect/spawner/random/trash/food_packaging, +/obj/effect/spawner/random/trash/food_packaging, +/obj/effect/spawner/random/trash/food_packaging, +/obj/effect/spawner/random/trash/cigbutt, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 1 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "rwN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -53169,6 +53224,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/catwalk_floor, /area/station/maintenance/tram/right) +"rDi" = ( +/obj/structure/hoop, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "rDj" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 1 @@ -53537,14 +53596,6 @@ }, /turf/open/floor/engine/hull, /area/station/solars/port) -"rLB" = ( -/obj/machinery/power/smes{ - charge = 5e+06 - }, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "rLG" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 4 @@ -53950,6 +54001,10 @@ /obj/machinery/firealarm/directional/east, /turf/open/floor/iron, /area/station/cargo/miningfoundry) +"rRw" = ( +/obj/effect/landmark/firealarm_sanity, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "rRy" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/closed/wall/r_wall, @@ -54064,11 +54119,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/engineering/break_room) -"rSH" = ( -/obj/structure/closet/secure_closet/freezer/fridge/open, -/obj/structure/window/spawner/directional/west, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "rTm" = ( /obj/structure/sign/warning/vacuum/external/directional/east, /obj/machinery/computer/security/labor, @@ -54116,11 +54166,19 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"rUp" = ( +/turf/closed/wall/r_wall, +/area/ruin/space/has_grav/syndicate_outpost) "rUr" = ( /obj/machinery/module_duplicator, /obj/machinery/light/directional/south, /turf/open/floor/iron, /area/station/science/explab) +"rUF" = ( +/obj/structure/dresser, +/obj/item/cigarette/cigar/havana, +/turf/open/floor/iron/grimy, +/area/ruin/space/has_grav/syndicate_outpost) "rUQ" = ( /obj/structure/window/reinforced/fulltile, /turf/open/floor/grass, @@ -54340,15 +54398,6 @@ }, /turf/open/misc/asteroid/airless, /area/station/asteroid) -"rYV" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/stripes/line{ - dir = 6 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/obj/machinery/airalarm/directional/west, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "rYW" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 @@ -54635,15 +54684,6 @@ /obj/machinery/light/small/dim/directional/north, /turf/open/floor/catwalk_floor, /area/station/hallway/primary/tram/right) -"seQ" = ( -/obj/machinery/flasher/directional/west{ - id = "AI" - }, -/obj/machinery/camera/motion/directional/west{ - c_tag = "Secure - AI Upload" - }, -/turf/open/floor/circuit/red, -/area/station/ai/upload/chamber) "seR" = ( /turf/open/floor/glass/reinforced/tram, /area/station/hallway/primary/tram/right) @@ -55109,6 +55149,9 @@ /obj/structure/cable/multilayer/connected, /turf/open/floor/engine, /area/station/engineering/supermatter/engine) +"smZ" = ( +/turf/closed/wall, +/area/ruin/space/has_grav/syndicate_outpost) "sna" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 8 @@ -56058,6 +56101,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/security/brig) +"sDQ" = ( +/obj/machinery/griddle, +/obj/machinery/light/cold/directional/east, +/turf/open/floor/iron/white/smooth_edge{ + dir = 4 + }, +/area/ruin/space/has_grav/syndicate_outpost) "sDY" = ( /obj/structure/railing{ dir = 1 @@ -56537,6 +56587,13 @@ "sNs" = ( /turf/closed/wall, /area/station/commons/storage/primary) +"sNL" = ( +/obj/machinery/shower/directional/north, +/obj/structure/curtain, +/obj/machinery/light/floor, +/obj/item/soap/syndie, +/turf/open/floor/iron/showroomfloor, +/area/ruin/space/has_grav/syndicate_outpost) "sNQ" = ( /obj/structure/disposalpipe/junction{ dir = 2 @@ -56681,6 +56738,11 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) +"sPA" = ( +/turf/open/floor/iron/white/smooth_edge{ + dir = 8 + }, +/area/ruin/space/has_grav/syndicate_outpost) "sPB" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/neutral/filled/line, @@ -56813,6 +56875,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"sTh" = ( +/obj/machinery/computer/message_monitor{ + dir = 8 + }, +/turf/open/floor/iron/dark/small, +/area/ruin/space/has_grav/syndicate_outpost) "sTq" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ dir = 8 @@ -57346,16 +57414,6 @@ "tby" = ( /turf/open/floor/iron/white, /area/station/science/research) -"tbK" = ( -/obj/structure/table, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/aicard, -/obj/item/circuitboard/aicore, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/cafeteria, -/area/station/command/heads_quarters/rd) "tcq" = ( /obj/structure/table/reinforced, /obj/item/stack/sheet/iron/fifty, @@ -57485,13 +57543,6 @@ /obj/structure/cable, /turf/open/floor/catwalk_floor, /area/station/hallway/primary/tram/left) -"teo" = ( -/obj/structure/chair{ - dir = 8 - }, -/obj/effect/landmark/atmospheric_sanity/ignore_area, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "tes" = ( /turf/open/floor/glass/reinforced, /area/station/command/heads_quarters/rd) @@ -57590,6 +57641,13 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron, /area/station/commons/dorms) +"tgx" = ( +/obj/structure/closet/secure_closet/freezer/fridge/open, +/obj/structure/window/spawner/directional/west, +/turf/open/floor/iron/white/smooth_corner{ + dir = 4 + }, +/area/ruin/space/has_grav/syndicate_outpost) "tgB" = ( /obj/structure/toilet{ pixel_y = 13 @@ -57782,6 +57840,11 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"tkd" = ( +/obj/structure/table, +/obj/item/aicard, +/turf/open/floor/iron/dark, +/area/station/engineering/storage/tech) "tkj" = ( /obj/effect/turf_decal/trimline/blue/filled/corner{ dir = 1 @@ -57793,14 +57856,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/white, /area/station/medical/surgery/aft) -"tkp" = ( -/obj/machinery/door/airlock/centcom{ - name = "Syndicate Secure Airlock System"; - desc = "Truly, a marvel of modern engineering." - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/iron/showroomfloor, -/area/station/commons/vacant_room) "tkv" = ( /obj/structure/ladder, /obj/effect/decal/cleanable/dirt, @@ -58596,20 +58651,6 @@ }, /turf/open/floor/iron/dark, /area/station/science/explab) -"typ" = ( -/obj/structure/table/wood/fancy/green, -/obj/effect/spawner/random/aimodule/harmless, -/obj/item/ai_module/supplied/freeform{ - pixel_x = 2; - pixel_y = 3 - }, -/obj/item/ai_module/reset, -/obj/item/radio/intercom/directional/east{ - name = "ai intercom"; - frequency = 1447 - }, -/turf/open/floor/circuit/green, -/area/station/ai/upload/chamber) "tyt" = ( /obj/structure/bookcase/random/reference, /obj/item/tgui_book/manual/dsm{ @@ -59905,6 +59946,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/commons/dorms) +"tVY" = ( +/obj/structure/table, +/obj/machinery/microwave, +/turf/open/floor/iron/white/smooth_corner{ + dir = 8 + }, +/area/ruin/space/has_grav/syndicate_outpost) "tWb" = ( /obj/machinery/camera/directional/east{ c_tag = "Civilian - Theatre Stage" @@ -62388,18 +62436,6 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/engineering/atmos) -"uHQ" = ( -/obj/item/paper{ - name = "initiation paperwork"; - default_raw_text = "Congradulations, agent 'INSERT NAME HERE'! You have been assigned reconnaissance duty among the orbiting rocks of Indecipheres! As this location was previously scouted as a potential build site for a Nanotrasen outpost, one of our benefactors has taken the oppertunity to pre-emptively construct a listening outpost within the region! You have been tasked with monitoring the potentially active future crew and logging all events onboard. If you are a Nanotrasen official who has stumbled upon this outpost before it could be properly established: Please ignore this entire paper." - }, -/obj/machinery/computer/records/medical/syndie{ - dir = 1; - req_access = list("syndicate") - }, -/obj/machinery/digital_clock/directional/south, -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "uHT" = ( /obj/effect/turf_decal/trimline/red/filled/line, /obj/effect/turf_decal/trimline/neutral/filled/warning, @@ -62684,6 +62720,12 @@ }, /turf/open/floor/plating, /area/station/commons/vacant_room/office) +"uMO" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/obj/machinery/digital_clock/directional/south, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "uMQ" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 4 @@ -63963,13 +64005,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/carpet, /area/station/command/heads_quarters/qm) -"vju" = ( -/obj/structure/table, -/obj/item/plate, -/obj/item/kitchen/fork, -/obj/machinery/computer/security/telescreen/entertainment/directional/west, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "vjv" = ( /obj/structure/railing{ dir = 4 @@ -64971,21 +65006,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/checker, /area/station/commons/lounge) -"vBB" = ( -/obj/machinery/power/terminal{ - dir = 1 - }, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/warning/electric_shock/directional/east, -/obj/item/storage/toolbox/mechanical{ - name = "moderately suspicious looking toolbox"; - desc = "Danger. Kinda robust."; - icon_state = "syndicate"; - inhand_icon_state = "toolbox_syndi" - }, -/turf/open/floor/plating, -/area/station/commons/vacant_room) "vBM" = ( /obj/effect/landmark/start/hangover, /obj/effect/turf_decal/trimline/neutral/filled/line{ @@ -65426,10 +65446,6 @@ }, /turf/open/floor/iron, /area/station/science/explab) -"vIM" = ( -/obj/structure/extinguisher_cabinet/directional/south, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "vJg" = ( /obj/structure/bodycontainer/morgue/beeper_off{ dir = 8 @@ -67173,6 +67189,13 @@ }, /turf/open/floor/iron, /area/station/command/heads_quarters/ce) +"wqZ" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/components/tank/air{ + dir = 1 + }, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "wrf" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -67306,10 +67329,6 @@ /obj/effect/turf_decal/tile/purple/fourcorners, /turf/open/floor/iron/white, /area/station/science/research) -"wtK" = ( -/obj/structure/extinguisher_cabinet/directional/south, -/turf/open/floor/iron/dark/herringbone, -/area/station/commons/vacant_room) "wtQ" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ dir = 4 @@ -67365,6 +67384,15 @@ /obj/machinery/light/small/dim/directional/east, /turf/open/floor/engine, /area/station/science/xenobiology) +"wuR" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "wuU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/showcase/cyborg/old{ @@ -67411,6 +67439,7 @@ /obj/item/stack/spacecash/c100, /obj/machinery/airalarm/directional/north, /obj/item/gun/ballistic/shotgun/doublebarrel, +/obj/item/storage/photo_album/bar, /turf/open/floor/wood, /area/station/service/bar/backroom) "wwj" = ( @@ -68448,6 +68477,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/cargo/miningdock) +"wOh" = ( +/obj/structure/window/reinforced/spawner/directional/west, +/obj/structure/table/wood/fancy/red, +/obj/effect/spawner/random/aimodule/harmful, +/obj/machinery/door/window/left/directional/east{ + name = "Dangerous Modules"; + req_access = list("captain") + }, +/turf/open/floor/circuit/red, +/area/station/ai/upload/chamber) "wOw" = ( /turf/open/floor/iron/grimy, /area/station/ai/satellite/foyer) @@ -68624,12 +68663,6 @@ /obj/structure/flora/bush/jungle/b/style_random, /turf/open/misc/grass/jungle/station, /area/station/science/explab) -"wSx" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 8 - }, -/turf/open/floor/iron/white, -/area/station/commons/vacant_room) "wSS" = ( /obj/structure/weightmachine, /turf/open/floor/iron, @@ -69981,6 +70014,20 @@ /obj/effect/mapping_helpers/airalarm/surgery, /turf/open/floor/iron/white, /area/station/medical/surgery/aft) +"xuF" = ( +/obj/machinery/door/airlock/centcom{ + name = "Syndicate Secure Airlock System"; + desc = "Truly, a marvel of modern engineering." + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/obj/effect/mapping_helpers/airlock/unres/delayed{ + dir = 4 + }, +/turf/open/floor/iron/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "xuI" = ( /obj/effect/turf_decal/stripes/corner{ dir = 1 @@ -70200,6 +70247,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/hallway/secondary/service) +"xzd" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "xzn" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 6 @@ -70217,6 +70268,12 @@ }, /turf/open/floor/wood/large, /area/station/service/barber) +"xzE" = ( +/obj/structure/stairs/east, +/turf/open/floor/iron/stairs/medium{ + dir = 8 + }, +/area/ruin/space/has_grav/syndicate_outpost) "xzI" = ( /obj/machinery/vending/games, /obj/structure/sign/clock/directional/north, @@ -70229,17 +70286,6 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron/dark, /area/station/engineering/atmospherics_engine) -"xAc" = ( -/obj/machinery/door/airlock/centcom{ - name = "Syndicate Secure Airlock System"; - desc = "Truly, a marvel of modern engineering." - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/obj/structure/cable, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/iron/herringbone, -/area/station/commons/vacant_room) "xAe" = ( /obj/effect/turf_decal/trimline/purple/filled/corner{ dir = 4 @@ -70543,6 +70589,20 @@ "xFx" = ( /turf/open/floor/iron, /area/station/commons/fitness) +"xFU" = ( +/obj/structure/table, +/obj/effect/spawner/random/food_or_drink/refreshing_beverage, +/obj/item/reagent_containers/condiment/saltshaker{ + pixel_x = -9; + pixel_y = 12 + }, +/obj/item/reagent_containers/condiment/peppermill{ + pixel_x = -1; + pixel_y = 13 + }, +/obj/machinery/light/directional/west, +/turf/open/floor/iron/dark/herringbone, +/area/ruin/space/has_grav/syndicate_outpost) "xGw" = ( /obj/machinery/door/airlock/external{ name = "Labor Camp Shuttle Airlock" @@ -71422,6 +71482,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/commons/fitness/recreation/entertainment) +"xYl" = ( +/obj/machinery/power/terminal{ + dir = 1 + }, +/obj/structure/cable, +/obj/effect/decal/cleanable/dirt, +/obj/structure/sign/warning/electric_shock/directional/east, +/obj/item/storage/toolbox/mechanical{ + name = "moderately suspicious looking toolbox"; + desc = "Danger. Kinda robust."; + icon_state = "syndicate"; + inhand_icon_state = "toolbox_syndi" + }, +/obj/machinery/light/small/directional/east, +/turf/open/floor/plating, +/area/ruin/space/has_grav/syndicate_outpost) "xYp" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ dir = 1 @@ -71525,6 +71601,14 @@ /obj/structure/girder, /turf/open/floor/plating, /area/station/maintenance/tram/left) +"yab" = ( +/obj/structure/toilet{ + pixel_y = 18 + }, +/obj/structure/sink/directional/east, +/obj/structure/mirror/directional/west, +/turf/open/floor/iron/showroomfloor, +/area/ruin/space/has_grav/syndicate_outpost) "yaf" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 1 @@ -71700,9 +71784,6 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron, /area/station/hallway/secondary/command) -"ydj" = ( -/turf/open/floor/iron/dark/small, -/area/station/commons/vacant_room) "ydk" = ( /obj/item/bikehorn, /obj/item/grown/bananapeel, @@ -76345,10 +76426,10 @@ aaa aaa aaa aaa -gcp -gcp -gcp -gcp +rUp +rUp +rUp +rUp aaa aac aac @@ -76602,14 +76683,14 @@ aaa aaa aaa aaa -gcp -aiK -qtM -gcp +rUp +yab +sNL +rUp aaa -gcp -gcp -gcp +rUp +rUp +rUp aac aac aaa @@ -76858,15 +76939,15 @@ aaa aaa aaa aaa -gcp -gcp -tkp -auz -gcp -gcp -gcp -igV -gcp +rUp +rUp +jqN +smZ +rUp +rUp +rUp +oef +rUp agK aac aac @@ -77115,15 +77196,15 @@ aaa aaa aaa aaa -gcp -atl -naI -vju -riN -agz -iOC -aaA -dbr +rUp +rDi +mNc +mLa +xFU +xzd +noK +cEK +pXN aac aac aaa @@ -77372,15 +77453,15 @@ aaa aaa aaa aaa -gcp -pcA -gnq -gNy -teo -agz -gcp -gcp -gcp +rUp +fEN +owo +mCn +rai +jJw +rUp +rUp +rUp agK aac aaa @@ -77629,14 +77710,14 @@ vXM vXM aaa aaa -gcp -aan -lsy -agz -agz -agz -fEQ -gcp +rUp +mqM +eOX +rRw +eRN +xzd +uMO +rUp aaa aaa aaa @@ -77886,16 +77967,16 @@ vXM vXM aaa aaa -gcp -cKm -fpj -agz -gNy -auz -xAc -gcp -gcp -gcp +rUp +xzE +rwM +gNC +dED +smZ +xuF +rUp +rUp +rUp aaa aaa vXM @@ -78143,16 +78224,16 @@ vXM vXM aaa aaa -gcp -gcp -mJj -wSx -rSH -auz -agv -rYV -eSI -gcp +rUp +rUp +jdu +sPA +tgx +smZ +mCY +leD +wqZ +rUp aaa aaa vXM @@ -78401,15 +78482,15 @@ vXM aaa aaa aaa -gcp -knl -nim -vIM -auz -rLB -vBB -plz -gcp +rUp +era +oJA +qKR +smZ +aXg +xYl +mcY +rUp aaa aaa vXM @@ -78658,15 +78739,15 @@ vXM vXM aaa aaa -gcp -qoD -iSw -dxT -gcp -gcp -gcp -gcp -gcp +rUp +tVY +sDQ +ilO +rUp +rUp +rUp +rUp +rUp aaa aaa vXM @@ -78915,11 +78996,11 @@ vXM vXM aaa aaa -gcp -gcp -gcp -gcp -gcp +rUp +rUp +rUp +rUp +rUp aaa aaa aaa @@ -101463,7 +101544,7 @@ jnq lQg tsa khV -hkF +tkd kdb gSf pkp @@ -121793,12 +121874,12 @@ vjT fjn mBW hJl -nrf +wOh hJl reC xnS hJl -dNT +gcG hJl iix iix @@ -122051,10 +122132,10 @@ xAx nYL hJl pMi -seQ +dyF jWf liZ -nbF +iGC sKA hJl aaa @@ -122565,10 +122646,10 @@ qDL dIK hJl hJl -fYw -typ -gfV -ixC +eiL +mSK +nHp +mmJ hJl hJl aaa @@ -142651,11 +142732,11 @@ aaa aaa aaa aaa -gcp -gcp -gcp -gcp -gcp +rUp +rUp +rUp +rUp +rUp aaa aaa aaa @@ -142908,14 +142989,14 @@ aac aac aac aaa -gcp -jFt -gnq -wtK -gcp -gcp -gcp -gcp +rUp +pim +ooe +jCr +rUp +rUp +rUp +rUp aaa aaa aac @@ -143165,14 +143246,14 @@ pMW pMW aac aaa -gcp -qBq -exv -gNy -oiL -aBI -isP -gcp +rUp +qjJ +wuR +gNC +hir +lVA +fIu +rUp aaa aaa aac @@ -143422,14 +143503,14 @@ pMW pMW aaa aaa -gcp -hoA -exv -gNy -ydj -jap -uHQ -gcp +rUp +qjJ +wuR +gNC +nNe +gNG +bkw +rUp aaa aaa aaa @@ -143679,14 +143760,14 @@ pMW pMW aaa aaa -gcp -pkQ -gNy -gNy -mVj -agt -bxd -gcp +rUp +inE +fkp +gNC +fyM +sTh +iuD +rUp aaa aaa aaa @@ -143936,14 +144017,14 @@ pMW pMW aaa aaa -gcp -gcp -gcp -bhG -auz -gcp -gcp -gcp +rUp +rUp +rUp +ojJ +smZ +rUp +rUp +rUp aaa aaa aaa @@ -144195,10 +144276,10 @@ aaa aaa aaa aaa -gcp -oae -kFs -gcp +rUp +daV +gkO +rUp aaa aaa aaa @@ -144452,10 +144533,10 @@ aaa aaa aaa aaa -gcp -agw -agG -gcp +rUp +qUP +rUF +rUp aaa aaa aaa @@ -144709,10 +144790,10 @@ pMW aaa aaa aaa -gcp -gcp -gcp -gcp +rUp +rUp +rUp +rUp aaa aac pMW @@ -155199,8 +155280,8 @@ jLf fXB nuw kUo -kMm -ggV +asN +cjo vCZ ikL tSp @@ -156744,7 +156825,7 @@ dVo eGl kUo vCZ -dPT +deL wiU kkg buB @@ -157001,7 +157082,7 @@ kUo kUo kUo vCZ -oOT +mGh vMJ kkg buB @@ -173698,7 +173779,7 @@ nIr ugt ugt ugt -nlf +nJU ihB eNH buW @@ -183972,7 +184053,7 @@ jIO bCx uvB rBb -tbK +rkU kye tes kye diff --git a/_maps/minigame/CTF/cruiser.dmm b/_maps/minigame/CTF/cruiser.dmm index 897ea48c860e..4297fcaf98bd 100644 --- a/_maps/minigame/CTF/cruiser.dmm +++ b/_maps/minigame/CTF/cruiser.dmm @@ -13,7 +13,7 @@ /obj/effect/turf_decal/tile/green{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "bN" = ( /obj/effect/turf_decal/siding/purple/corner{ @@ -23,7 +23,7 @@ /area/centcom/ctf) "cb" = ( /obj/effect/turf_decal/tile/green/anticorner/contrasted, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "ck" = ( /turf/closed/indestructible/syndicate, @@ -46,7 +46,7 @@ /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "cK" = ( /obj/effect/spawner/structure/window/reinforced/shuttle/indestructible, @@ -60,7 +60,7 @@ /obj/effect/turf_decal/tile/red{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "dt" = ( /obj/structure/window/reinforced/spawner/directional/north{ @@ -80,7 +80,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "es" = ( /obj/machinery/computer{ @@ -190,7 +190,7 @@ color = "#121212"; dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "lI" = ( /obj/machinery/power/shuttle_engine/heater, @@ -199,7 +199,7 @@ "lL" = ( /obj/structure/table/reinforced/ctf, /obj/effect/turf_decal/tile/red/anticorner/contrasted, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "lS" = ( /obj/structure/sign/warning/radiation/rad_area{ @@ -231,7 +231,7 @@ /area/centcom/ctf) "nj" = ( /obj/structure/barricade/security/ctf, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "nx" = ( /obj/machinery/computer{ @@ -256,7 +256,7 @@ /obj/effect/turf_decal/tile/green{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "oL" = ( /obj/structure/window/reinforced/spawner/directional/north{ @@ -287,14 +287,14 @@ /area/centcom/ctf) "sc" = ( /obj/effect/turf_decal/tile/red, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "sz" = ( /turf/open/floor/mineral/titanium/purple, /area/centcom/ctf) "tM" = ( /obj/effect/turf_decal/tile/red/fourcorners, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "tN" = ( /obj/structure/table/reinforced/ctf, @@ -316,7 +316,7 @@ /obj/effect/turf_decal/tile/red{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "uz" = ( /obj/structure/table/reinforced/ctf, @@ -332,20 +332,20 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "vy" = ( /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "vL" = ( /obj/effect/turf_decal/siding/white{ color = "#121212"; dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "vN" = ( /obj/effect/turf_decal/siding/purple{ @@ -361,16 +361,16 @@ /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "wO" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "xl" = ( /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "xK" = ( /obj/structure/window/reinforced/spawner/directional/east, @@ -378,7 +378,7 @@ /area/centcom/ctf) "xS" = ( /obj/effect/turf_decal/tile/green/half/contrasted, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "xU" = ( /obj/machinery/computer{ @@ -391,13 +391,13 @@ color = "#121212"; dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "yn" = ( /obj/effect/turf_decal/siding/white/corner{ color = "#121212" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "yw" = ( /obj/effect/turf_decal/siding/white/corner{ @@ -418,7 +418,7 @@ /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "zs" = ( /obj/structure/barricade/security/ctf, @@ -449,7 +449,7 @@ color = "#121212"; dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "Bh" = ( /obj/effect/turf_decal/siding/blue{ @@ -507,7 +507,7 @@ /area/centcom/ctf) "FM" = ( /obj/effect/turf_decal/tile/red/half/contrasted, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "FP" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -545,7 +545,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "HN" = ( /obj/structure/window/reinforced/spawner/directional/west{ @@ -571,7 +571,7 @@ color = "#121212"; dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "IN" = ( /obj/effect/turf_decal/trimline/red/warning{ @@ -583,11 +583,11 @@ /obj/effect/turf_decal/tile/green{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "Jr" = ( /obj/effect/turf_decal/tile/green/fourcorners, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "Jt" = ( /turf/open/floor/mineral/titanium/tiled/white, @@ -609,7 +609,7 @@ /obj/effect/turf_decal/tile/red{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "KO" = ( /obj/machinery/power/shuttle_engine/heater, @@ -638,7 +638,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "ML" = ( /obj/effect/turf_decal/siding/white/corner{ @@ -662,7 +662,7 @@ /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "Ou" = ( /obj/effect/turf_decal/trimline/red/warning{ @@ -688,7 +688,7 @@ /area/centcom/ctf) "Pm" = ( /obj/effect/turf_decal/tile/red/anticorner/contrasted, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "Pn" = ( /obj/structure/trap/ctf/green, @@ -721,7 +721,7 @@ /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/centcom/ctf) "Ri" = ( /obj/structure/trap/ctf/red, @@ -738,19 +738,19 @@ /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "RH" = ( /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "RT" = ( /obj/effect/turf_decal/tile/green/anticorner/contrasted{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "RV" = ( /turf/open/floor/mineral/titanium/tiled, @@ -770,7 +770,7 @@ /turf/open/floor/mineral/titanium/blue, /area/centcom/ctf) "Us" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "Vl" = ( /obj/effect/turf_decal/siding/purple/corner, @@ -799,7 +799,7 @@ /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "WB" = ( /obj/effect/turf_decal/trimline/green/warning{ @@ -825,7 +825,7 @@ /area/centcom/ctf) "Zf" = ( /obj/effect/turf_decal/tile/green, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/centcom/ctf) "Zk" = ( /obj/machinery/computer{ diff --git a/_maps/minigame/deathmatch/secu_ring.dmm b/_maps/minigame/deathmatch/secu_ring.dmm index 615103813faa..3e8263ed19dd 100644 --- a/_maps/minigame/deathmatch/secu_ring.dmm +++ b/_maps/minigame/deathmatch/secu_ring.dmm @@ -13,41 +13,41 @@ /turf/open/floor/mineral/plastitanium/red, /area/deathmatch) "n" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/deathmatch) "o" = ( /obj/machinery/recharger, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/deathmatch) "t" = ( /obj/structure/barricade/security/ctf, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/deathmatch) "u" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/deathmatch) "G" = ( /obj/structure/barricade/security/ctf, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/deathmatch) "H" = ( /obj/machinery/light{ dir = 8 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/deathmatch) "I" = ( /turf/closed/indestructible/reinforced, /area/deathmatch) "M" = ( /obj/machinery/light, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/deathmatch) "N" = ( /obj/machinery/light{ dir = 4 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/deathmatch) "O" = ( /obj/machinery/light{ @@ -59,7 +59,7 @@ /obj/machinery/light{ dir = 1 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/deathmatch) "S" = ( /obj/machinery/light{ @@ -72,7 +72,7 @@ /turf/open/floor/mineral/plastitanium/red, /area/deathmatch) "W" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/deathmatch) "X" = ( /turf/open/floor/mineral/plastitanium/red, diff --git a/_maps/safehouses/den.dmm b/_maps/safehouses/den.dmm index 9b0b37f53515..f989fcec14b4 100644 --- a/_maps/safehouses/den.dmm +++ b/_maps/safehouses/den.dmm @@ -5,7 +5,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "b" = ( /obj/modular_map_connector, @@ -26,7 +26,7 @@ pixel_x = 4; pixel_y = 2 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "i" = ( /obj/effect/spawner/structure/window/reinforced, @@ -40,16 +40,16 @@ /obj/structure/railing/corner/end{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "p" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "r" = ( /obj/effect/turf_decal/trimline/yellow/line{ dir = 9 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "u" = ( /obj/structure/railing, @@ -66,7 +66,7 @@ dir = 4 }, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/safehouse) "C" = ( /turf/closed/wall, @@ -79,7 +79,7 @@ /obj/effect/turf_decal/trimline/yellow/corner{ dir = 1 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "E" = ( /obj/effect/turf_decal/trimline/yellow/line{ @@ -87,7 +87,7 @@ }, /obj/effect/decal/cleanable/generic, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/safehouse) "G" = ( /obj/effect/spawner/structure/window/reinforced, @@ -102,7 +102,7 @@ dir = 5 }, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/safehouse) "J" = ( /obj/effect/spawner/structure/window/reinforced, @@ -115,28 +115,28 @@ "K" = ( /obj/effect/spawner/random/vending/colavend, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "M" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "O" = ( /obj/effect/turf_decal/trimline/yellow/line{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "R" = ( /obj/effect/turf_decal/trimline/yellow/line{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "U" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/structure/sign/departments/cargo/directional/west, /obj/machinery/light/small/directional/west, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "W" = ( /obj/effect/spawner/structure/window/reinforced, @@ -161,13 +161,13 @@ pixel_y = 6; pixel_x = -10 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) "Z" = ( /obj/effect/turf_decal/trimline/yellow/warning{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/safehouse) (1,1,1) = {" diff --git a/_maps/safehouses/ice.dmm b/_maps/safehouses/ice.dmm index aedeaba8c642..aa2e366c6fb0 100644 --- a/_maps/safehouses/ice.dmm +++ b/_maps/safehouses/ice.dmm @@ -40,7 +40,7 @@ dir = 1 }, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "n" = ( /obj/effect/turf_decal/weather/snow/corner{ @@ -82,7 +82,7 @@ dir = 5 }, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "x" = ( /obj/effect/turf_decal/weather/snow/corner{ @@ -136,7 +136,7 @@ dir = 9 }, /obj/effect/landmark/bitrunning/hololadder_spawn, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/safehouse) "I" = ( /obj/effect/decal/cleanable/dirt/dust, diff --git a/_maps/shuttles/arrival/arrival_northstar.dmm b/_maps/shuttles/arrival/arrival_northstar.dmm index fadde8f9df42..689074884390 100644 --- a/_maps/shuttles/arrival/arrival_northstar.dmm +++ b/_maps/shuttles/arrival/arrival_northstar.dmm @@ -25,7 +25,7 @@ pixel_y = 4 }, /obj/item/storage/toolbox/emergency, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "g" = ( /obj/machinery/power/shuttle_engine/propulsion/burst/right{ @@ -35,7 +35,7 @@ /area/shuttle/arrival) "j" = ( /obj/structure/closet/firecloset/full, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "k" = ( /obj/structure/table/reinforced/plastitaniumglass, @@ -44,43 +44,43 @@ /obj/item/clothing/mask/breath, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/emergency_oxygen, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "n" = ( /obj/effect/turf_decal/trimline/red/warning{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "q" = ( /obj/structure/closet/wardrobe/grey, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "r" = ( /obj/effect/turf_decal/trimline/red/warning{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "s" = ( /obj/effect/turf_decal/trimline/red/corner{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "t" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/arrival) "u" = ( /obj/effect/turf_decal/trimline/red/corner, /obj/effect/turf_decal/trimline/red/corner{ dir = 8 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/arrival) "v" = ( /obj/structure/closet/wardrobe/mixed, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "w" = ( /obj/machinery/power/shuttle_engine/propulsion/burst/left{ @@ -92,14 +92,14 @@ /obj/effect/turf_decal/trimline/red/corner{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "z" = ( /obj/structure/extinguisher_cabinet/directional/south, /obj/effect/turf_decal/trimline/red/warning{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "A" = ( /obj/effect/turf_decal/trimline/red/warning{ @@ -108,7 +108,7 @@ /obj/effect/turf_decal/trimline/red/warning{ dir = 4 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/arrival) "C" = ( /obj/effect/turf_decal/trimline/red/corner{ @@ -117,7 +117,7 @@ /obj/effect/turf_decal/trimline/red/corner{ dir = 1 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/arrival) "D" = ( /obj/machinery/power/shuttle_engine/propulsion{ @@ -132,7 +132,7 @@ /obj/effect/turf_decal/trimline/red/warning{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "F" = ( /obj/structure/chair/comfy/shuttle, @@ -150,7 +150,7 @@ dir = 1 }, /obj/machinery/light/red/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "L" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -163,7 +163,7 @@ /obj/effect/turf_decal/trimline/red/corner{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "N" = ( /obj/structure/chair/comfy/shuttle, @@ -171,7 +171,7 @@ /area/shuttle/arrival) "P" = ( /obj/effect/turf_decal/trimline/red/warning, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/arrival) "V" = ( /obj/structure/chair/comfy/shuttle, @@ -180,7 +180,7 @@ /area/shuttle/arrival) "W" = ( /obj/effect/turf_decal/trimline/red/corner, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/arrival) "Z" = ( /obj/effect/spawner/structure/window/survival_pod, diff --git a/_maps/shuttles/cargo/cargo_catwalk.dmm b/_maps/shuttles/cargo/cargo_catwalk.dmm index 7136321584f2..a8fd77f5ab9f 100644 --- a/_maps/shuttles/cargo/cargo_catwalk.dmm +++ b/_maps/shuttles/cargo/cargo_catwalk.dmm @@ -1,6 +1,6 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/supply) "c" = ( /obj/machinery/light/directional/east, @@ -49,7 +49,7 @@ /turf/open/floor/iron/smooth_large, /area/shuttle/supply) "p" = ( -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/supply) "r" = ( /obj/structure/railing, diff --git a/_maps/shuttles/cargo/cargo_northstar.dmm b/_maps/shuttles/cargo/cargo_northstar.dmm index 59232e5a1e5b..d28eaaab1365 100644 --- a/_maps/shuttles/cargo/cargo_northstar.dmm +++ b/_maps/shuttles/cargo/cargo_northstar.dmm @@ -53,10 +53,10 @@ /turf/open/floor/catwalk_floor/iron_smooth, /area/shuttle/supply) "h" = ( -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/supply) "i" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/supply) "j" = ( /obj/machinery/conveyor{ diff --git a/_maps/shuttles/emergency/emergency_monastery.dmm b/_maps/shuttles/emergency/emergency_monastery.dmm index 10ff3ff18011..1bfbda982e50 100644 --- a/_maps/shuttles/emergency/emergency_monastery.dmm +++ b/_maps/shuttles/emergency/emergency_monastery.dmm @@ -1689,7 +1689,7 @@ /area/shuttle/escape) "AF" = ( /obj/structure/table/wood, -/obj/item/food/grown/harebell, +/obj/item/food/grown/flower/harebell, /turf/open/floor/iron/dark, /area/shuttle/escape) "AG" = ( @@ -2831,7 +2831,7 @@ /area/shuttle/escape) "PD" = ( /obj/structure/table/wood, -/obj/item/food/grown/poppy, +/obj/item/food/grown/flower/poppy, /turf/open/floor/iron/dark, /area/shuttle/escape) "PJ" = ( @@ -3038,8 +3038,8 @@ /area/shuttle/escape) "Sc" = ( /obj/structure/table/wood, -/obj/item/food/grown/poppy, -/obj/item/food/grown/harebell, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/harebell, /turf/open/floor/iron/dark, /area/shuttle/escape) "Se" = ( diff --git a/_maps/shuttles/emergency/emergency_northstar.dmm b/_maps/shuttles/emergency/emergency_northstar.dmm index 0bb628683fb6..014d73088549 100644 --- a/_maps/shuttles/emergency/emergency_northstar.dmm +++ b/_maps/shuttles/emergency/emergency_northstar.dmm @@ -233,7 +233,7 @@ name = "Emergency Shuttle Bridge" }, /obj/effect/mapping_helpers/airlock/access/all/command/general, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/escape) "uy" = ( /obj/machinery/door/airlock/survival_pod/glass{ @@ -249,7 +249,7 @@ /turf/open/floor/catwalk_floor/iron_dark, /area/shuttle/escape) "vO" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/escape) "wy" = ( /turf/open/floor/mineral/plastitanium/red, @@ -277,7 +277,7 @@ /turf/open/floor/plating, /area/shuttle/escape) "AW" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/escape) "Bk" = ( /obj/structure/chair/comfy/shuttle{ @@ -295,7 +295,7 @@ /obj/structure/railing/corner{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/escape) "CO" = ( /turf/open/floor/catwalk_floor/iron_smooth, @@ -360,7 +360,7 @@ /turf/template_noop, /area/template_noop) "FQ" = ( -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/escape) "HE" = ( /obj/structure/chair/comfy/shuttle, @@ -397,7 +397,7 @@ /obj/structure/railing/corner{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/escape) "JC" = ( /obj/structure/railing{ @@ -427,7 +427,7 @@ /obj/structure/railing/corner{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/escape) "NR" = ( /obj/effect/turf_decal/trimline/white/filled/line{ @@ -460,7 +460,7 @@ "Pa" = ( /obj/machinery/status_display/evac/directional/east, /obj/machinery/light/red/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/escape) "Qo" = ( /obj/structure/chair/comfy/shuttle{ @@ -480,7 +480,7 @@ }, /area/shuttle/escape) "Qw" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/shuttle/escape) "QF" = ( /obj/structure/railing{ diff --git a/_maps/shuttles/emergency/emergency_tombstone.dmm b/_maps/shuttles/emergency/emergency_tombstone.dmm new file mode 100644 index 000000000000..7507f551eba0 --- /dev/null +++ b/_maps/shuttles/emergency/emergency_tombstone.dmm @@ -0,0 +1,1381 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"aj" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 4 + }, +/obj/item/radio/intercom/directional/north, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"as" = ( +/obj/docking_port/mobile/emergency{ + dir = 2; + name = "Tombstone shuttle" + }, +/obj/machinery/door/airlock/grunge{ + name = "Cemetery" + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"aI" = ( +/obj/machinery/power/shuttle_engine/heater{ + dir = 8 + }, +/obj/structure/window/reinforced/spawner/directional/east, +/turf/open/floor/plating/airless, +/area/shuttle/escape/tombstone) +"aS" = ( +/obj/machinery/door/airlock/external, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/turf/open/floor/catwalk_floor, +/area/shuttle/escape/tombstone) +"aX" = ( +/obj/item/statuebust, +/obj/structure/table/wood, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"bG" = ( +/obj/effect/turf_decal/tile/dark_green/half{ + dir = 4 + }, +/turf/open/floor/iron/freezer/edge{ + dir = 4 + }, +/area/shuttle/escape/tombstone) +"bR" = ( +/obj/structure/closet/crate/grave/filled, +/obj/effect/spawner/random/exotic/grave, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"cb" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/button/crematorium{ + id = "graveyard_shuttle_crematorium"; + dir = 4; + pixel_y = 25 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"ct" = ( +/obj/effect/turf_decal/tile/dark_green/anticorner{ + dir = 1 + }, +/obj/structure/sign/poster/official/cleanliness/directional/west, +/turf/open/floor/iron/freezer/corner, +/area/shuttle/escape/tombstone) +"cu" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark/corner, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"cV" = ( +/obj/structure/table, +/obj/item/storage/box/gloves, +/obj/item/reagent_containers/spray/cleaner, +/obj/effect/turf_decal/siding/thinplating/dark, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"dd" = ( +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"dh" = ( +/obj/effect/turf_decal/tile/dark_green/half{ + dir = 8 + }, +/obj/machinery/light/small/dim/directional/west, +/turf/open/floor/iron/freezer/edge{ + dir = 8 + }, +/area/shuttle/escape/tombstone) +"dr" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 8 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"dy" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/item/flashlight/flare/torch, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"dF" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 4 + }, +/obj/structure/extinguisher_cabinet/directional/south, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"eq" = ( +/obj/structure/table/reinforced, +/obj/effect/spawner/surgery_tray/full/morgue, +/turf/open/floor/iron/freezer, +/area/shuttle/escape/tombstone) +"et" = ( +/obj/machinery/light/small/directional/south, +/obj/structure/sign/warning/vacuum/directional/south, +/turf/open/floor/catwalk_floor, +/area/shuttle/escape/tombstone) +"eW" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"fm" = ( +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"fx" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/effect/turf_decal/weather/dirt, +/obj/item/storage/fancy/candle_box, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"fS" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 9 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"gm" = ( +/obj/structure/mirror/directional/north, +/obj/structure/sink/directional/south, +/obj/effect/turf_decal/tile/dark_green/half{ + dir = 1 + }, +/turf/open/floor/iron/freezer/edge{ + dir = 1 + }, +/area/shuttle/escape/tombstone) +"it" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/mob/living/basic/mouse, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"iz" = ( +/obj/effect/spawner/random/trash/hobo_squat{ + spawn_loot_chance = 50 + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"iN" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 6 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"jl" = ( +/obj/effect/turf_decal/tile/dark_green/anticorner{ + dir = 8 + }, +/obj/structure/sign/poster/official/cleanliness/directional/west, +/turf/open/floor/iron/freezer/corner{ + dir = 4 + }, +/area/shuttle/escape/tombstone) +"jS" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"kz" = ( +/obj/effect/turf_decal/trimline/dark_red/warning{ + dir = 6 + }, +/obj/structure/closet/crate/coffin, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/brig) +"kM" = ( +/obj/machinery/door/airlock/grunge{ + name = "Cemetery" + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"kR" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/item/storage/fancy/candle_box, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"la" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"ly" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/obj/item/flashlight/lantern, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"lV" = ( +/obj/effect/spawner/random/trash/graffiti{ + spawn_loot_chance = 50 + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"mc" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"mm" = ( +/obj/structure/kitchenspike, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"mr" = ( +/obj/machinery/door/airlock/external, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/turf/open/floor/catwalk_floor, +/area/shuttle/escape/tombstone) +"mt" = ( +/obj/effect/turf_decal/tile/dark_green/half{ + dir = 1 + }, +/turf/open/floor/iron/freezer/edge{ + dir = 1 + }, +/area/shuttle/escape/tombstone) +"mx" = ( +/obj/effect/turf_decal/trimline/dark_red/warning{ + dir = 10 + }, +/obj/structure/closet/crate/coffin, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/brig) +"mz" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"mZ" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 8 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"ni" = ( +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/weather/dirt, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"nx" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/obj/item/bouquet/poppy{ + pixel_x = 19 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"ok" = ( +/obj/item/flashlight/flare/torch, +/obj/effect/turf_decal/weather/dirt{ + dir = 9 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"px" = ( +/obj/item/flashlight/flare/torch, +/obj/effect/turf_decal/weather/dirt{ + dir = 10 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"qn" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"qr" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"qx" = ( +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"qM" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"rz" = ( +/obj/machinery/computer/operating{ + dir = 8 + }, +/turf/open/floor/iron/freezer, +/area/shuttle/escape/tombstone) +"rH" = ( +/obj/structure/closet/crate/coffin, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"rK" = ( +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/cobweb, +/obj/structure/closet/l3closet, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"rP" = ( +/obj/machinery/status_display/evac, +/turf/closed/wall/mineral/plastitanium, +/area/shuttle/escape/tombstone) +"si" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 4 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"so" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"sP" = ( +/obj/machinery/door/airlock/grunge{ + name = "Morgue" + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"th" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"vD" = ( +/obj/effect/turf_decal/weather/dirt, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"vK" = ( +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"vL" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 1 + }, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue" + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"vP" = ( +/obj/effect/spawner/random/trash/garbage{ + spawn_scatter_radius = 1; + spawn_loot_count = 2; + spawn_loot_chance = 50 + }, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"vT" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 8 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"vZ" = ( +/obj/structure/bodycontainer/crematorium{ + id = "graveyard_shuttle_crematorium" + }, +/obj/structure/sign/warning/hot_temp/directional/north, +/turf/open/floor/iron/dark/smooth_large, +/area/shuttle/escape/brig) +"wa" = ( +/obj/effect/turf_decal/siding/thinplating/dark, +/obj/structure/sign/departments/maint/directional/north, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"wb" = ( +/obj/structure/extinguisher_cabinet, +/turf/closed/wall/mineral/plastitanium, +/area/shuttle/escape/tombstone) +"we" = ( +/obj/structure/table/optable, +/turf/open/floor/iron/freezer, +/area/shuttle/escape/tombstone) +"wu" = ( +/obj/item/flashlight/flare/torch, +/obj/effect/turf_decal/weather/dirt{ + dir = 5 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"wX" = ( +/obj/machinery/vending/wardrobe/coroner_wardrobe, +/turf/open/floor/iron/dark/smooth_half, +/area/shuttle/escape/tombstone) +"wZ" = ( +/obj/effect/spawner/costume/plaguedoctor, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"xc" = ( +/obj/machinery/status_display/evac, +/turf/closed/wall/mineral/titanium, +/area/shuttle/escape/tombstone) +"xt" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 5 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"yj" = ( +/obj/structure/closet/crate/grave/filled, +/obj/effect/spawner/random/exotic/grave, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"ym" = ( +/obj/effect/turf_decal/tile/dark_green/anticorner{ + dir = 4 + }, +/turf/open/floor/iron/freezer/corner{ + dir = 8 + }, +/area/shuttle/escape/tombstone) +"yo" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"yJ" = ( +/obj/machinery/computer/emergency_shuttle{ + dir = 8 + }, +/obj/machinery/light/small/dim/directional/east, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"zA" = ( +/obj/machinery/portable_atmospherics/canister/miasma, +/obj/effect/turf_decal/delivery, +/obj/effect/decal/cleanable/cobweb, +/obj/structure/sign/warning/biohazard/directional/south, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"Ab" = ( +/obj/structure/bodycontainer/morgue/beeper_off{ + dir = 8 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Ah" = ( +/obj/structure/bodycontainer/morgue/beeper_off{ + dir = 8 + }, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Aw" = ( +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"AA" = ( +/obj/item/flashlight/flare/torch, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"AS" = ( +/obj/effect/turf_decal/trimline/dark_red/warning{ + dir = 5 + }, +/obj/structure/closet/crate/coffin, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/brig) +"Bj" = ( +/turf/template_noop, +/area/template_noop) +"Cz" = ( +/obj/machinery/light/small/dim/directional/west, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue" + }, +/obj/structure/sign/clock/directional/west, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"DO" = ( +/obj/effect/turf_decal/trimline/neutral/warning, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue" + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Ei" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"Fi" = ( +/obj/machinery/light/small/dim/directional/west, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue" + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"FJ" = ( +/obj/structure/mineral_door{ + name = "Crypt" + }, +/obj/structure/barricade/wooden/crude, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"FZ" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/effect/decal/remains/human/smokey/maintenance, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Gf" = ( +/mob/living/basic/spider/maintenance, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"Gs" = ( +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/obj/structure/tank_holder/extinguisher, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"GU" = ( +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/weather/dirt{ + dir = 6 + }, +/obj/structure/extinguisher_cabinet/directional/east, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Hy" = ( +/obj/item/flashlight/flare/torch, +/obj/effect/turf_decal/weather/dirt{ + dir = 6 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"HZ" = ( +/obj/structure/closet/crate/coffin, +/obj/effect/turf_decal/trimline/dark_red/warning{ + dir = 9 + }, +/obj/item/radio/intercom/directional/east, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/brig) +"If" = ( +/obj/effect/turf_decal/tile/dark_green/anticorner, +/turf/open/floor/iron/freezer/corner{ + dir = 1 + }, +/area/shuttle/escape/tombstone) +"Ih" = ( +/obj/machinery/power/shuttle_engine/propulsion{ + dir = 8 + }, +/turf/open/floor/plating/airless, +/area/shuttle/escape/tombstone) +"JE" = ( +/obj/effect/spawner/random/trash/garbage{ + spawn_scatter_radius = 1; + spawn_loot_count = 2; + spawn_loot_chance = 50 + }, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"Lv" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 4 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"MS" = ( +/obj/machinery/door/airlock/grunge{ + name = "Crematorium" + }, +/obj/effect/mapping_helpers/airlock/access/all/security/general, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/tombstone) +"MT" = ( +/obj/machinery/door/airlock/maintenance_hatch, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"Nw" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 10 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Ny" = ( +/obj/item/clothing/head/helmet/skull, +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"NU" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"NV" = ( +/obj/item/radio/intercom/directional/south, +/obj/effect/turf_decal/tile/dark_green/half, +/turf/open/floor/iron/freezer/edge, +/area/shuttle/escape/tombstone) +"Oa" = ( +/obj/structure/kitchenspike, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/siding/thinplating/dark, +/obj/machinery/light/small/dim/directional/east, +/obj/structure/extinguisher_cabinet/directional/east, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"Ow" = ( +/obj/structure/table/wood, +/obj/item/shovel, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Pi" = ( +/obj/effect/turf_decal/weather/dirt, +/obj/structure/sign/poster/contraband/random/directional/south, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"PM" = ( +/turf/open/floor/catwalk_floor, +/area/shuttle/escape/tombstone) +"PP" = ( +/obj/effect/turf_decal/trimline/neutral/warning, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue"; + dir = 8 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Qd" = ( +/obj/machinery/smartfridge/organ, +/turf/open/floor/iron/dark/smooth_half, +/area/shuttle/escape/tombstone) +"Ru" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/item/bouquet/sunflower{ + pixel_y = 44 + }, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"RZ" = ( +/obj/machinery/door/airlock/grunge{ + name = "Morgue" + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"Ty" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/effect/turf_decal/weather/dirt, +/obj/item/flashlight/flare/torch, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Uu" = ( +/turf/closed/wall/mineral/plastitanium, +/area/shuttle/escape/tombstone) +"UA" = ( +/obj/effect/turf_decal/tile/dark_green/half, +/turf/open/floor/iron/freezer/edge, +/area/shuttle/escape/tombstone) +"UM" = ( +/obj/structure/closet/crate/grave/fresh, +/obj/structure/sign/painting/library{ + pixel_y = 33; + dir = 1 + }, +/obj/effect/spawner/random/exotic/grave, +/obj/effect/decal/cleanable/cobweb, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"Vl" = ( +/obj/structure/closet/crate/grave/fresh, +/obj/structure/sign/painting/library{ + pixel_x = 32 + }, +/obj/effect/spawner/random/exotic/grave, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"VQ" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 8 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"VW" = ( +/obj/structure/rack, +/obj/item/storage/box/bodybags{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/storage/box/bodybags, +/obj/effect/turf_decal/siding/thinplating/dark{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"Wp" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"Ws" = ( +/obj/effect/turf_decal/trimline/neutral/warning{ + dir = 1 + }, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue"; + dir = 8 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"WE" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 1 + }, +/obj/structure/sign/poster/contraband/random/directional/north, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Xg" = ( +/obj/structure/closet/crate/grave/fresh, +/obj/structure/sign/painting/library{ + pixel_y = 33; + dir = 1 + }, +/obj/effect/spawner/random/exotic/grave, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"Xl" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/thinplating/dark, +/turf/open/floor/mineral/plastitanium/red, +/area/shuttle/escape/brig) +"Xv" = ( +/obj/effect/turf_decal/weather/dirt{ + dir = 8 + }, +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/obj/item/shovel, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"Yp" = ( +/obj/machinery/light/small/dim/directional/east, +/obj/structure/bodycontainer/morgue/beeper_off{ + name = "morgue"; + dir = 8 + }, +/turf/open/floor/iron/dark/textured_half, +/area/shuttle/escape/tombstone) +"Yt" = ( +/obj/machinery/suit_storage_unit/standard_unit, +/obj/effect/turf_decal/bot, +/obj/effect/decal/cleanable/cobweb, +/turf/open/floor/plating, +/area/shuttle/escape/tombstone) +"YF" = ( +/obj/machinery/door/airlock/grunge{ + name = "Secure Morgue" + }, +/obj/effect/mapping_helpers/airlock/access/all/command/general, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) +"YN" = ( +/obj/item/kirbyplants/random, +/obj/effect/turf_decal/weather/dirt{ + dir = 5 + }, +/obj/item/radio/intercom/directional/east, +/turf/open/misc/asteroid/basalt/wasteland/station, +/area/shuttle/escape/tombstone) +"YS" = ( +/obj/structure/closet/crate/grave/filled, +/obj/structure/sign/painting/library{ + pixel_x = 32 + }, +/obj/effect/spawner/random/exotic/grave, +/turf/open/misc/dirt/station, +/area/shuttle/escape/tombstone) +"Zj" = ( +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 1 + }, +/obj/effect/turf_decal/siding/thinplating/dark/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/shuttle/escape/tombstone) + +(1,1,1) = {" +Bj +Bj +Ih +Ih +Bj +Ih +Ih +Bj +Ih +Ih +Bj +Ih +Ih +Bj +Bj +"} +(2,1,1) = {" +Bj +Bj +aI +aI +lV +aI +aI +iz +aI +aI +vP +aI +aI +Bj +Bj +"} +(3,1,1) = {" +Bj +Bj +Uu +Uu +Uu +Uu +Uu +Uu +Uu +Uu +Uu +Uu +Uu +Bj +Bj +"} +(4,1,1) = {" +Bj +Bj +Uu +Aw +Aw +lV +Aw +Gf +vP +Aw +Aw +zA +Uu +Bj +Bj +"} +(5,1,1) = {" +Uu +Uu +Uu +Aw +Uu +Uu +Uu +rP +Uu +Uu +Aw +Uu +Uu +aS +Uu +"} +(6,1,1) = {" +Uu +rK +Uu +Aw +Uu +Qd +ct +dh +jl +MT +Aw +Uu +Yt +et +Uu +"} +(7,1,1) = {" +Uu +Aw +vP +Aw +Uu +wX +mt +eq +UA +Uu +JE +MT +Aw +PM +Uu +"} +(8,1,1) = {" +Uu +MT +Uu +Uu +Uu +Uu +gm +we +NV +Uu +Uu +Uu +Uu +mr +Uu +"} +(9,1,1) = {" +Uu +wa +vL +Cz +DO +cV +mt +rz +UA +VW +vL +Fi +DO +qn +Uu +"} +(10,1,1) = {" +Uu +qx +xt +si +iN +la +ym +bG +If +la +xt +si +iN +qn +Uu +"} +(11,1,1) = {" +mz +Wp +mc +mc +mc +Zj +mc +mc +mc +Zj +mc +mc +mc +Ei +mz +"} +(12,1,1) = {" +mz +eW +qM +qM +qM +cu +qM +it +qM +cu +qM +qM +qM +yo +mz +"} +(13,1,1) = {" +Uu +qx +fS +VQ +Nw +la +fS +VQ +Nw +la +fS +VQ +Nw +qn +Uu +"} +(14,1,1) = {" +sP +qx +Ws +Yp +PP +la +Ws +Yp +PP +la +Ws +Yp +PP +Gs +Uu +"} +(15,1,1) = {" +Uu +Uu +Uu +wb +Uu +RZ +Uu +rP +Uu +RZ +Uu +Uu +Uu +Uu +Uu +"} +(16,1,1) = {" +kM +ok +vT +vT +vT +vK +vT +vT +vT +vK +vT +vT +vT +px +Uu +"} +(17,1,1) = {" +Uu +fm +vK +th +th +vK +th +th +th +vK +th +th +vK +ni +Uu +"} +(18,1,1) = {" +mz +so +vD +rH +rH +Ty +rH +rH +rH +dy +rH +rH +so +vD +mz +"} +(19,1,1) = {" +mz +Ny +vD +rH +rH +fx +rH +xc +rH +kR +rH +rH +so +FZ +mz +"} +(20,1,1) = {" +mz +so +vD +rH +rH +Ty +rH +rH +rH +dy +rH +rH +so +vD +mz +"} +(21,1,1) = {" +Uu +fm +vK +vT +vT +vK +vT +vT +vT +vK +vT +vT +vK +ni +Uu +"} +(22,1,1) = {" +as +wu +th +th +th +th +vK +vK +vK +th +th +vK +th +Hy +Uu +"} +(23,1,1) = {" +Uu +Uu +mz +mz +Uu +Uu +WE +vK +Pi +Uu +Uu +FJ +Uu +Uu +Uu +"} +(24,1,1) = {" +MS +jS +Xl +AS +kz +mz +so +vK +vD +Uu +UM +Ru +Ow +aX +Uu +"} +(25,1,1) = {" +Uu +vZ +dr +mZ +mZ +MS +vK +AA +vD +Uu +Xg +so +vK +vK +mz +"} +(26,1,1) = {" +Uu +cb +NU +qr +qr +mz +YN +vK +GU +Uu +Xv +th +ly +nx +mz +"} +(27,1,1) = {" +Uu +mm +Oa +HZ +mx +Uu +Uu +YF +Uu +Uu +bR +Vl +YS +yj +Uu +"} +(28,1,1) = {" +Uu +Uu +Uu +Uu +Uu +Uu +dd +wZ +dd +Uu +Uu +Uu +Uu +Uu +Uu +"} +(29,1,1) = {" +Bj +Bj +Bj +Bj +Bj +Uu +aj +Lv +dF +Uu +Bj +Bj +Bj +Bj +Bj +"} +(30,1,1) = {" +Bj +Bj +Bj +Bj +Bj +mz +Ah +yJ +Ab +mz +Bj +Bj +Bj +Bj +Bj +"} +(31,1,1) = {" +Bj +Bj +Bj +Bj +Bj +mz +mz +rP +mz +mz +Bj +Bj +Bj +Bj +Bj +"} diff --git a/_maps/shuttles/emergency/emergency_tranquility.dmm b/_maps/shuttles/emergency/emergency_tranquility.dmm index 5eccef94229d..f0a729ec5255 100644 --- a/_maps/shuttles/emergency/emergency_tranquility.dmm +++ b/_maps/shuttles/emergency/emergency_tranquility.dmm @@ -1731,7 +1731,7 @@ /obj/item/book/manual/fish_catalog{ pixel_x = -4 }, -/obj/item/food/grown/poppy/lily{ +/obj/item/food/grown/flower/poppy/lily{ pixel_y = 9; pixel_x = 6 }, diff --git a/_maps/shuttles/ert_bounty.dmm b/_maps/shuttles/ert_bounty.dmm index b1fbf895fa58..991d43161b25 100644 --- a/_maps/shuttles/ert_bounty.dmm +++ b/_maps/shuttles/ert_bounty.dmm @@ -23,7 +23,7 @@ preferred_direction = 4 }, /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "d" = ( /obj/structure/table, @@ -35,7 +35,7 @@ pixel_x = 4; pixel_y = -4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "f" = ( /obj/structure/table, @@ -51,7 +51,7 @@ pixel_x = -6; pixel_y = -7 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "h" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, @@ -60,11 +60,11 @@ "i" = ( /obj/structure/tank_dispenser/oxygen, /obj/effect/turf_decal/stripes/full, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "j" = ( /obj/structure/curtain/bounty, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "k" = ( /obj/structure/table/reinforced, @@ -76,7 +76,7 @@ pixel_x = 8; pixel_y = 14 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "l" = ( /obj/machinery/power/shuttle_engine/propulsion{ @@ -90,7 +90,7 @@ }, /obj/effect/mapping_helpers/airlock/cyclelink_helper, /obj/machinery/door/airlock/external/ruin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "n" = ( /turf/closed/wall/mineral/plastitanium, @@ -99,39 +99,39 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "q" = ( /obj/structure/closet/crate/bin, /obj/item/grenade/chem_grenade/glitter/pink, /obj/item/trash/can, /obj/item/cigbutt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "r" = ( /obj/structure/chair/office{ dir = 8 }, /obj/effect/landmark/ert_shuttle_spawn, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "s" = ( /obj/machinery/computer/shuttle/hunter{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "t" = ( /obj/machinery/suit_storage_unit/standard_unit{ storage_type = /obj/item/tank/internals/oxygen/yellow }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "u" = ( /obj/structure/chair/comfy/shuttle{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "v" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -145,43 +145,43 @@ dir = 1 }, /obj/structure/sign/warning/vacuum/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "x" = ( /obj/structure/chair/office{ dir = 4 }, /obj/effect/landmark/ert_shuttle_spawn, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "y" = ( /obj/structure/chair/office{ dir = 4 }, /obj/effect/landmark/ert_shuttle_spawn, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "z" = ( /obj/structure/table/reinforced, /obj/item/storage/fancy/cigarettes/cigars/havana{ pixel_y = 6 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "A" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "B" = ( /obj/machinery/light/floor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "D" = ( /obj/machinery/power/smes, /obj/effect/turf_decal/stripes/full, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "E" = ( /obj/structure/table, @@ -193,17 +193,17 @@ pixel_x = -4; pixel_y = 15 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "F" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper, /obj/machinery/door/airlock/external/ruin, /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "G" = ( /obj/machinery/light/small/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "H" = ( /obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/hunter{ @@ -211,14 +211,14 @@ y_offset = 6; x_offset = 3 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "I" = ( /obj/structure/chair/comfy/shuttle{ dir = 4 }, /obj/effect/landmark/ert_shuttle_brief_spawn, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "J" = ( /obj/effect/turf_decal/stripes/line, @@ -226,28 +226,28 @@ dir = 1 }, /obj/machinery/door/airlock/external/ruin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "K" = ( /obj/structure/chair/office{ dir = 8 }, /obj/effect/landmark/ert_shuttle_spawn, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "L" = ( /obj/effect/landmark/ert_shuttle_spawn, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "M" = ( /obj/machinery/suit_storage_unit/standard_unit{ storage_type = /obj/item/tank/internals/oxygen/yellow }, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "O" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "P" = ( /obj/structure/table, @@ -259,12 +259,12 @@ pixel_x = 10; pixel_y = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "Q" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/sign/warning/vacuum/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "R" = ( /obj/structure/table, @@ -273,7 +273,7 @@ pixel_y = 16; pixel_x = -7 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "S" = ( /obj/structure/table/reinforced, @@ -292,17 +292,17 @@ /obj/item/pen/fourcolor{ pixel_x = -4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "T" = ( /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "V" = ( /obj/machinery/computer/camera_advanced{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "X" = ( /obj/structure/rack, @@ -319,7 +319,7 @@ pixel_y = -1 }, /obj/effect/turf_decal/stripes/full, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "Y" = ( /obj/structure/table, @@ -336,11 +336,11 @@ pixel_x = -9; pixel_y = 6 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "Z" = ( /obj/structure/bookcase/random/reference, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) (1,1,1) = {" diff --git a/_maps/shuttles/escape_pod/escape_pod_cramped.dmm b/_maps/shuttles/escape_pod/escape_pod_cramped.dmm index 5de7563f1552..3a424f502b48 100644 --- a/_maps/shuttles/escape_pod/escape_pod_cramped.dmm +++ b/_maps/shuttles/escape_pod/escape_pod_cramped.dmm @@ -1,6 +1,6 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "B" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/shuttle/pod_1) "N" = ( /obj/docking_port/mobile/pod{ @@ -25,7 +25,7 @@ /area/shuttle/pod_1) "Z" = ( /obj/machinery/power/shuttle_engine/propulsion/burst, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/shuttle/pod_1) (1,1,1) = {" diff --git a/_maps/shuttles/ferry/ferry_fancy.dmm b/_maps/shuttles/ferry/ferry_fancy.dmm index a8e553fef1a6..1d2bdd614f93 100644 --- a/_maps/shuttles/ferry/ferry_fancy.dmm +++ b/_maps/shuttles/ferry/ferry_fancy.dmm @@ -11,7 +11,7 @@ /area/shuttle/transport) "d" = ( /obj/machinery/door/airlock/external/ruin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/transport) "e" = ( /obj/machinery/power/shuttle_engine/propulsion/left{ @@ -28,14 +28,14 @@ /area/shuttle/transport) "g" = ( /obj/structure/chair/comfy/shuttle, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/transport) "h" = ( /obj/machinery/light/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "i" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "j" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -46,17 +46,17 @@ /area/shuttle/transport) "k" = ( /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "l" = ( /obj/machinery/door/airlock/titanium, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "m" = ( /obj/machinery/computer/shuttle/ferry/request{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/transport) "n" = ( /obj/machinery/door/airlock/titanium, @@ -66,7 +66,7 @@ name = "ferry shuttle"; preferred_direction = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "o" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -81,15 +81,15 @@ /obj/structure/chair/comfy/shuttle{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/transport) "q" = ( /obj/machinery/light/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) "r" = ( /obj/machinery/door/airlock/external/ruin, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/transport) (1,1,1) = {" diff --git a/_maps/shuttles/hunter/hunter_bounty.dmm b/_maps/shuttles/hunter/hunter_bounty.dmm index 2932196835c0..6ea2a3853142 100644 --- a/_maps/shuttles/hunter/hunter_bounty.dmm +++ b/_maps/shuttles/hunter/hunter_bounty.dmm @@ -47,21 +47,21 @@ /obj/structure/chair/office{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "n" = ( /obj/structure/table, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "o" = ( /obj/structure/chair/office{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "p" = ( /obj/effect/mob_spawn/ghost_role/human/fugitive/bounty/hook, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "q" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -74,76 +74,76 @@ /obj/machinery/computer/launchpad{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "s" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "t" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "u" = ( /obj/structure/curtain/bounty, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "v" = ( /obj/structure/chair/office{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "w" = ( /obj/structure/table, /obj/item/phone, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "x" = ( /obj/machinery/fugitive_locator, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "y" = ( /obj/structure/chair/office{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "z" = ( /obj/structure/chair/comfy/shuttle{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "A" = ( /obj/machinery/computer/shuttle/hunter{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "B" = ( /obj/machinery/launchpad, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "E" = ( /obj/structure/table, /obj/item/binoculars, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "F" = ( /obj/machinery/power/smes, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "G" = ( /obj/machinery/fugitive_capture, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "H" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "I" = ( /obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/hunter{ @@ -151,19 +151,19 @@ y_offset = 6; x_offset = 3 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "K" = ( /obj/machinery/suit_storage_unit/standard_unit{ storage_type = /obj/item/tank/internals/oxygen/yellow }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "M" = ( /obj/effect/mob_spawn/ghost_role/human/fugitive/bounty/armor{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "N" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper, @@ -174,13 +174,13 @@ /area/shuttle/hunter) "O" = ( /obj/machinery/light/floor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "P" = ( /obj/structure/fluff/empty_sleeper{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "Q" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper{ @@ -196,17 +196,17 @@ /obj/item/pinpointer/shuttle{ pixel_y = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "S" = ( /obj/structure/table, /obj/item/storage/toolbox/mechanical, /obj/machinery/light/small/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "T" = ( /obj/structure/tank_dispenser/oxygen, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "U" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper, @@ -246,23 +246,23 @@ storage_type = /obj/item/tank/internals/oxygen/yellow }, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) "X" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 }, /obj/machinery/light/floor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "Y" = ( /obj/structure/table, /obj/item/multitool, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter) "Z" = ( /obj/effect/mob_spawn/ghost_role/human/fugitive/bounty/synth, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter) (1,1,1) = {" diff --git a/_maps/shuttles/hunter/hunter_russian.dmm b/_maps/shuttles/hunter/hunter_russian.dmm index 46840f7cdd78..ce6b2163e322 100644 --- a/_maps/shuttles/hunter/hunter_russian.dmm +++ b/_maps/shuttles/hunter/hunter_russian.dmm @@ -7,7 +7,7 @@ /obj/effect/turf_decal/siding/red{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "bi" = ( /obj/machinery/computer/shuttle/hunter{ @@ -41,13 +41,13 @@ /obj/effect/turf_decal/bot, /obj/structure/reagent_dispensers/fueltank, /obj/item/weldingtool/largetank, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "cM" = ( /obj/effect/turf_decal/siding/red/corner{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/hunter/russian) "cU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -75,7 +75,7 @@ }, /obj/effect/turf_decal/bot_red, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "gp" = ( /obj/machinery/airalarm/directional/east, @@ -88,7 +88,7 @@ /obj/effect/turf_decal/siding/red{ dir = 10 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "gv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -103,7 +103,7 @@ /obj/effect/decal/cleanable/dirt, /obj/machinery/fugitive_capture, /obj/structure/sign/poster/contraband/bountyhunters/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "ho" = ( /obj/effect/decal/cleanable/dirt, @@ -122,7 +122,7 @@ /obj/item/reagent_containers/cup/glass/bottle/vodka, /obj/item/crowbar/large/old, /obj/item/lighter/greyscale, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "iV" = ( /turf/template_noop, @@ -153,7 +153,7 @@ pixel_y = 2 }, /obj/item/lighter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "lS" = ( /obj/machinery/computer/crew/syndie{ @@ -177,7 +177,7 @@ /obj/item/ammo_box/speedloader/strilka310, /obj/item/ammo_box/speedloader/strilka310, /obj/item/ammo_box/speedloader/strilka310, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "qF" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -186,7 +186,7 @@ /obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/dirt, /obj/machinery/meter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "rB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -197,7 +197,7 @@ name = "Cockpit Access" }, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "sR" = ( /obj/structure/cable, @@ -206,7 +206,7 @@ }, /obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "tj" = ( /turf/open/floor/mineral/plastitanium, @@ -220,7 +220,7 @@ }, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "uT" = ( /obj/machinery/suit_storage_unit/standard_unit{ @@ -228,7 +228,7 @@ storage_type = /obj/item/tank/internals/oxygen/yellow }, /obj/effect/turf_decal/siding/red, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "vZ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -247,21 +247,21 @@ pixel_x = 5; pixel_y = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "wF" = ( /obj/effect/turf_decal/bot_red, /obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/dirt, /obj/effect/mob_spawn/ghost_role/human/fugitive/russian/leader, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "xd" = ( /obj/effect/turf_decal/siding/red{ dir = 1 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "xf" = ( /obj/machinery/door/airlock/security/glass{ @@ -272,7 +272,7 @@ }, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "xE" = ( /obj/machinery/door/airlock/security/glass{ @@ -283,13 +283,13 @@ }, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "yo" = ( /obj/effect/turf_decal/bot, /obj/structure/mecha_wreckage/ripley, /obj/item/book/manual/ripley_build_and_repair, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "yp" = ( /obj/structure/chair/comfy/shuttle{ @@ -304,7 +304,7 @@ }, /obj/effect/turf_decal/bot_red, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "zk" = ( /obj/machinery/door/airlock/external/glass/ruin{ @@ -316,7 +316,7 @@ }, /obj/effect/mapping_helpers/airlock/locked, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "zQ" = ( /obj/machinery/atmospherics/components/tank/air, @@ -357,13 +357,13 @@ dir = 1 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Dr" = ( /obj/effect/turf_decal/siding/red/corner{ dir = 8 }, -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/hunter/russian) "Du" = ( /obj/effect/decal/cleanable/dirt, @@ -400,7 +400,7 @@ /obj/item/clothing/mask/breath, /obj/item/crowbar, /obj/item/crowbar, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Ej" = ( /obj/machinery/power/apc/auto_name/directional/east, @@ -414,7 +414,7 @@ dir = 9 }, /obj/effect/mapping_helpers/apc/cut_AI_wire, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "EK" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -451,7 +451,7 @@ "GF" = ( /obj/effect/turf_decal/bot, /obj/machinery/fugitive_locator, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "IV" = ( /obj/structure/table, @@ -473,7 +473,7 @@ dir = 9 }, /obj/machinery/light/small/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Jh" = ( /obj/effect/turf_decal/stripes/line, @@ -513,18 +513,18 @@ }, /obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "LD" = ( /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/dirt, /obj/structure/kitchenspike, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "LQ" = ( /obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/generic, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "LT" = ( /obj/machinery/door/airlock/security/glass{ @@ -536,14 +536,14 @@ }, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "LW" = ( /obj/effect/spawner/structure/window/survival_pod, /turf/open/floor/plating, /area/shuttle/hunter/russian) "Me" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/hunter/russian) "MO" = ( /obj/effect/turf_decal/siding/red{ @@ -552,7 +552,7 @@ /obj/effect/decal/cleanable/dirt, /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Nk" = ( /obj/machinery/door/airlock/external/glass/ruin{ @@ -573,7 +573,7 @@ name = "Cargo Hold Access" }, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "Se" = ( /obj/effect/turf_decal/stripes/line{ @@ -590,7 +590,7 @@ dir = 1 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Th" = ( /obj/effect/mob_spawn/ghost_role/human/fugitive/russian{ @@ -599,7 +599,7 @@ /obj/effect/turf_decal/bot_red, /obj/effect/decal/cleanable/dirt, /obj/structure/sign/poster/contraband/kss13/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "Uu" = ( /obj/effect/turf_decal/siding/red{ @@ -613,7 +613,7 @@ /obj/item/ammo_box/magazine/m9mm, /obj/item/ammo_box/speedloader/strilka310, /obj/item/crowbar, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "UD" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -643,7 +643,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper, /obj/effect/mapping_helpers/airlock/locked, /obj/effect/mapping_helpers/airlock/access/all/hunter, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/hunter/russian) "VA" = ( /obj/machinery/light/small/directional/west, @@ -651,7 +651,7 @@ /obj/effect/decal/cleanable/dirt, /obj/machinery/portable_atmospherics/canister/oxygen, /obj/structure/sign/poster/contraband/communist_state/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "WG" = ( /obj/structure/cable, @@ -673,7 +673,7 @@ /obj/item/storage/medkit/ancient, /obj/item/storage/medkit/ancient, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/hunter/russian) "XW" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ diff --git a/_maps/shuttles/infiltrator/infiltrator_advanced.dmm b/_maps/shuttles/infiltrator/infiltrator_advanced.dmm index 988b06f2c7ff..66b68ae5aeec 100644 --- a/_maps/shuttles/infiltrator/infiltrator_advanced.dmm +++ b/_maps/shuttles/infiltrator/infiltrator_advanced.dmm @@ -42,7 +42,7 @@ /obj/item/storage/toolbox/syndicate, /obj/item/assembly/voice, /obj/item/crowbar/red, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "aj" = ( /obj/machinery/computer/shuttle/syndicate, @@ -193,7 +193,7 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper, /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/bridge) "aB" = ( /obj/machinery/porta_turret/syndicate/shuttle{ @@ -294,7 +294,7 @@ }, /obj/effect/turf_decal/delivery, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "aU" = ( /obj/effect/turf_decal/stripes/line{ @@ -323,7 +323,7 @@ }, /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/hallway) "aX" = ( /obj/structure/table/reinforced, @@ -387,7 +387,7 @@ /obj/machinery/status_display/ai{ pixel_y = 32 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "be" = ( /obj/effect/turf_decal/delivery, @@ -402,7 +402,7 @@ }, /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/hallway) "bf" = ( /turf/closed/wall/mineral/plastitanium/nodiagonal, @@ -432,7 +432,7 @@ /obj/machinery/suit_storage_unit/syndicate, /obj/effect/turf_decal/box, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "bj" = ( /obj/effect/turf_decal/stripes/line{ @@ -492,7 +492,7 @@ /obj/machinery/status_display/evac{ pixel_x = -32 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "bp" = ( /obj/machinery/door/poddoor{ @@ -507,7 +507,7 @@ name = "Infiltrator Port Hatch Control"; req_access = list("syndicate") }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/airlock) "bq" = ( /obj/effect/turf_decal/delivery, @@ -734,7 +734,7 @@ /obj/effect/mapping_helpers/apc/cut_AI_wire, /obj/effect/mapping_helpers/apc/syndicate_access, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "bT" = ( /obj/structure/closet/crate, @@ -783,7 +783,7 @@ /obj/item/clothing/suit/apron/surgical, /obj/structure/table/reinforced, /obj/structure/window/reinforced/spawner/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "bX" = ( /obj/effect/turf_decal/stripes/corner{ @@ -818,7 +818,7 @@ name = "Infiltrator Port Airlock" }, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/airlock) "ca" = ( /turf/closed/wall/r_wall/plastitanium/syndicate/nodiagonal, @@ -858,7 +858,7 @@ dir = 1 }, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "ce" = ( /turf/closed/wall/mineral/plastitanium/nodiagonal, @@ -877,7 +877,7 @@ /obj/effect/turf_decal/box, /obj/machinery/airalarm/directional/south, /obj/effect/mapping_helpers/airalarm/syndicate_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "ci" = ( /obj/structure/table/reinforced, @@ -901,7 +901,7 @@ /obj/item/assembly/infra, /obj/item/assembly/flash/handheld, /obj/item/assembly/flash/handheld, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "cj" = ( /obj/structure/rack, @@ -1008,7 +1008,7 @@ /obj/machinery/airalarm/directional/west, /obj/effect/mapping_helpers/airalarm/syndicate_access, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "cx" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, @@ -1167,7 +1167,7 @@ name = "Infiltrator Starboard Hatch Control"; req_access = list("syndicate") }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/airlock) "cX" = ( /obj/effect/turf_decal/stripes/line{ @@ -1201,7 +1201,7 @@ pixel_x = 4; pixel_y = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "db" = ( /obj/structure/table/reinforced, @@ -1215,7 +1215,7 @@ }, /obj/item/stock_parts/power_store/cell/high, /obj/item/multitool, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dc" = ( /obj/effect/turf_decal/stripes/line{ @@ -1266,7 +1266,7 @@ }, /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "dh" = ( /obj/effect/turf_decal/stripes/line{ @@ -1304,7 +1304,7 @@ /obj/item/grenade/c4, /obj/item/grenade/c4, /obj/item/grenade/c4, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dk" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -1483,7 +1483,7 @@ }, /obj/effect/turf_decal/box, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "dE" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -1520,7 +1520,7 @@ /obj/effect/mapping_helpers/apc/syndicate_access, /obj/effect/decal/cleanable/dirt, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "dI" = ( /obj/effect/turf_decal/stripes/line{ @@ -1557,14 +1557,14 @@ /obj/effect/mapping_helpers/apc/cut_AI_wire, /obj/effect/mapping_helpers/apc/syndicate_access, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dN" = ( /obj/machinery/recharge_station, /obj/effect/turf_decal/delivery, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/structure/window/reinforced/spawner/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dO" = ( /obj/structure/closet/syndicate/personal, @@ -1578,7 +1578,7 @@ /obj/machinery/status_display/evac{ pixel_y = -32 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "dQ" = ( /obj/structure/reagent_dispensers/fueltank, @@ -1589,7 +1589,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dR" = ( /turf/closed/wall/r_wall/plastitanium/syndicate/nodiagonal, @@ -1617,20 +1617,20 @@ /obj/machinery/airalarm/directional/west, /obj/effect/mapping_helpers/airalarm/syndicate_access, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "dU" = ( /obj/structure/table/optable, /obj/effect/turf_decal/bot, /obj/effect/decal/cleanable/blood/old, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "dW" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/stripes/line, /obj/machinery/recharge_station, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "dX" = ( /obj/effect/turf_decal/stripes/line, @@ -1670,7 +1670,7 @@ /obj/structure/sign/warning/no_smoking{ pixel_x = 28 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "ec" = ( /obj/machinery/door/airlock/hatch{ @@ -1686,7 +1686,7 @@ /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, /obj/structure/sign/warning/secure_area/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/hallway) "ed" = ( /obj/effect/turf_decal/bot, @@ -1769,7 +1769,7 @@ /obj/structure/cable, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, /obj/structure/sign/warning/secure_area/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/hallway) "el" = ( /obj/effect/turf_decal/stripes/line, @@ -1820,7 +1820,7 @@ /obj/structure/extinguisher_cabinet/directional/east, /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/syndicate_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "ep" = ( /obj/structure/mirror/directional/north, @@ -1870,7 +1870,7 @@ dir = 8 }, /obj/machinery/firealarm/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "ev" = ( /turf/closed/wall/mineral/plastitanium/nodiagonal, @@ -1891,7 +1891,7 @@ /obj/effect/turf_decal/box, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/eva) "kf" = ( /obj/structure/chair/comfy/shuttle{ @@ -1917,7 +1917,7 @@ pixel_x = 6 }, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "tB" = ( /obj/effect/turf_decal/stripes/line{ @@ -2003,7 +2003,7 @@ pixel_x = 32 }, /obj/machinery/light/small/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/armory) "KJ" = ( /obj/effect/turf_decal/stripes/line{ @@ -2056,12 +2056,12 @@ }, /obj/effect/decal/cleanable/dirt, /obj/item/healthanalyzer/advanced, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "TT" = ( /obj/structure/table/reinforced, /obj/item/storage/backpack/duffelbag/syndie/surgery, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/syndicate/medical) "WC" = ( /obj/effect/turf_decal/stripes/line{ diff --git a/_maps/shuttles/mining/mining_common_northstar.dmm b/_maps/shuttles/mining/mining_common_northstar.dmm index f746f90c0abb..9ee4fbc666aa 100644 --- a/_maps/shuttles/mining/mining_common_northstar.dmm +++ b/_maps/shuttles/mining/mining_common_northstar.dmm @@ -10,7 +10,7 @@ dir = 8 }, /obj/structure/sign/nanotrasen/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "e" = ( /obj/machinery/power/shuttle_engine/heater, @@ -21,7 +21,7 @@ /obj/effect/turf_decal/caution/stand_clear/red{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "l" = ( /obj/effect/spawner/structure/window/survival_pod, @@ -39,13 +39,13 @@ /obj/effect/turf_decal/trimline/neutral/line{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "t" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "v" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/mining) "z" = ( /obj/structure/closet/crate, @@ -54,7 +54,7 @@ dir = 1 }, /obj/machinery/light/small/red/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/mining) "A" = ( /obj/structure/chair/comfy/shuttle{ @@ -63,7 +63,7 @@ /obj/effect/turf_decal/trimline/neutral/corner{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "C" = ( /obj/machinery/door/airlock/survival_pod/glass{ @@ -76,12 +76,12 @@ port_direction = 8; shuttle_id = "mining_common" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "D" = ( /obj/structure/table/reinforced, /obj/item/storage/toolbox/emergency, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "G" = ( /obj/structure/ore_box, @@ -89,28 +89,28 @@ /obj/effect/turf_decal/siding/dark{ dir = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/mining) "J" = ( /obj/effect/turf_decal/trimline/neutral/line, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "P" = ( /obj/machinery/computer/shuttle/mining/common, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "T" = ( /obj/structure/chair/comfy/shuttle{ dir = 1 }, /obj/effect/turf_decal/trimline/neutral/line, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "U" = ( /obj/structure/table/reinforced, /obj/item/radio, /obj/machinery/light/small/red/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) (1,1,1) = {" diff --git a/_maps/shuttles/mining/mining_northstar.dmm b/_maps/shuttles/mining/mining_northstar.dmm index 4a557937d778..20c9948da8b4 100644 --- a/_maps/shuttles/mining/mining_northstar.dmm +++ b/_maps/shuttles/mining/mining_northstar.dmm @@ -10,14 +10,14 @@ port_direction = 8; shuttle_id = "mining" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "c" = ( /obj/structure/table/reinforced, /obj/item/storage/toolbox/emergency, /obj/structure/sign/warning/xeno_mining/directional/north, /obj/machinery/light/small/red/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "d" = ( /obj/item/clothing/suit/hazardvest{ @@ -103,7 +103,7 @@ }, /obj/effect/turf_decal/trimline/brown, /obj/machinery/light/small/red/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/mining) "j" = ( /obj/structure/chair/comfy/shuttle{ @@ -112,7 +112,7 @@ /obj/effect/turf_decal/trimline/brown/end{ dir = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "l" = ( /obj/structure/chair/comfy/shuttle{ @@ -126,24 +126,24 @@ }, /obj/structure/sign/poster/official/work_for_a_future/directional/north, /obj/machinery/light/small/red/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "m" = ( /obj/effect/turf_decal/caution/stand_clear/red{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "s" = ( /obj/machinery/computer/shuttle/mining, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "z" = ( /obj/effect/turf_decal/siding/brown{ dir = 5 }, /obj/effect/turf_decal/trimline/brown, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/mining) "A" = ( /obj/machinery/power/shuttle_engine/propulsion, @@ -155,7 +155,7 @@ }, /obj/effect/turf_decal/trimline/brown/end, /obj/machinery/light/small/red/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "F" = ( /obj/structure/table/reinforced, @@ -163,20 +163,20 @@ /obj/item/tank/internals/emergency_oxygen, /obj/item/crowbar/red, /obj/item/clothing/mask/gas, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "G" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "I" = ( -/turf/closed/wall/mineral/titanium/survival, +/turf/closed/wall/mineral/plastitanium/survival, /area/shuttle/mining) "K" = ( /obj/effect/turf_decal/trimline/purple/line{ dir = 1 }, /obj/effect/turf_decal/trimline/purple/line, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) "P" = ( /turf/template_noop, @@ -196,7 +196,7 @@ }, /obj/effect/turf_decal/trimline/brown, /obj/structure/ore_box, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/mining) "Z" = ( /obj/structure/chair/comfy/shuttle{ @@ -209,7 +209,7 @@ dir = 4 }, /obj/structure/sign/nanotrasen/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/mining) (1,1,1) = {" diff --git a/_maps/shuttles/pirate/pirate_default.dmm b/_maps/shuttles/pirate/pirate_default.dmm index 9b6f60d16a76..135be66b0410 100644 --- a/_maps/shuttles/pirate/pirate_default.dmm +++ b/_maps/shuttles/pirate/pirate_default.dmm @@ -8,7 +8,7 @@ /obj/effect/mapping_helpers/airalarm/all_access, /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ab" = ( /obj/structure/table, @@ -75,7 +75,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "aj" = ( /turf/closed/wall/mineral/plastitanium/nodiagonal, @@ -140,7 +140,7 @@ /obj/effect/mapping_helpers/airalarm/all_access, /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ap" = ( /obj/machinery/door/airlock/hatch{ @@ -149,7 +149,7 @@ /obj/structure/barricade/wooden/crude, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "aq" = ( /obj/structure/chair/stool/directional/south, @@ -210,7 +210,7 @@ /obj/structure/barricade/wooden/crude, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "aw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -429,7 +429,7 @@ pixel_y = 3 }, /obj/machinery/recharger, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "bk" = ( /obj/effect/turf_decal/stripes/line{ @@ -451,11 +451,11 @@ /obj/structure/table, /obj/machinery/cell_charger, /obj/item/stock_parts/power_store/cell/high, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "bm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "bo" = ( /obj/machinery/light/small/directional/east, @@ -499,7 +499,7 @@ pixel_x = 13; pixel_y = 6 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "bu" = ( /obj/machinery/firealarm/directional/north, @@ -528,13 +528,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/effect/decal/cleanable/dirt, /obj/machinery/piratepad, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "bA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "bB" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, @@ -575,7 +575,7 @@ /obj/machinery/computer/piratepad_control{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "bJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -702,7 +702,7 @@ pixel_x = 5 }, /obj/structure/sign/poster/contraband/bountyhunters/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "ep" = ( /obj/structure/window/reinforced/spawner/directional/north{ @@ -760,7 +760,7 @@ anchored = 1 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "eE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -776,14 +776,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "gY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "km" = ( /obj/machinery/atmospherics/components/tank/air, @@ -805,7 +805,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/airlock/cutaiwire, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "np" = ( /obj/structure/reagent_dispensers/fueltank, @@ -841,7 +841,7 @@ pixel_x = -2; pixel_y = 2 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "wf" = ( /obj/machinery/door/airlock{ @@ -870,7 +870,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/airlock/cutaiwire, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "zw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, @@ -916,7 +916,7 @@ pixel_x = 2; pixel_y = -1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Ni" = ( /obj/effect/turf_decal/stripes/line, diff --git a/_maps/shuttles/pirate/pirate_grey.dmm b/_maps/shuttles/pirate/pirate_grey.dmm index 50594b83d9b5..9dc5377c9fdf 100644 --- a/_maps/shuttles/pirate/pirate_grey.dmm +++ b/_maps/shuttles/pirate/pirate_grey.dmm @@ -247,7 +247,7 @@ /obj/effect/turf_decal/caution/stand_clear{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "jS" = ( /obj/effect/spawner/random/trash/mess, @@ -340,7 +340,7 @@ }, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/structure/fans/tiny, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "os" = ( /obj/structure/rack, @@ -781,7 +781,7 @@ /obj/effect/turf_decal/caution/stand_clear{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "zQ" = ( /obj/structure/cable, @@ -826,7 +826,7 @@ }, /obj/machinery/light/small/directional/south, /obj/item/reagent_containers/cup/glass/coffee, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "AZ" = ( /obj/effect/decal/cleanable/dirt, @@ -858,7 +858,7 @@ dir = 8 }, /obj/structure/fans/tiny, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Bu" = ( /obj/machinery/power/apc/auto_name/directional/north{ @@ -1338,7 +1338,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "TV" = ( /obj/effect/turf_decal/trimline/yellow/warning{ diff --git a/_maps/shuttles/pirate/pirate_irs.dmm b/_maps/shuttles/pirate/pirate_irs.dmm index 5ad360f2f48f..bde069eaa77b 100644 --- a/_maps/shuttles/pirate/pirate_irs.dmm +++ b/_maps/shuttles/pirate/pirate_irs.dmm @@ -4,7 +4,7 @@ /obj/effect/decal/cleanable/blood/old, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "az" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line{ @@ -110,7 +110,7 @@ "eg" = ( /obj/effect/turf_decal/bot, /obj/machinery/power/port_gen/pacman/pre_loaded, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ev" = ( /obj/structure/chair/comfy/shuttle{ @@ -148,7 +148,7 @@ /obj/effect/turf_decal/caution/stand_clear/white{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "fO" = ( /obj/effect/turf_decal/tile/dark_blue/half{ @@ -197,7 +197,7 @@ "hW" = ( /obj/machinery/suit_storage_unit/standard_unit, /obj/effect/turf_decal/bot/left, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ie" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line{ @@ -213,7 +213,7 @@ /obj/structure/table/reinforced, /obj/machinery/light/red/dim/directional/south, /obj/item/storage/bag/money/vault, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ip" = ( /obj/structure/table, @@ -259,7 +259,7 @@ dir = 9 }, /obj/item/melee/chainofcommand, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "jp" = ( /obj/effect/turf_decal/stripes{ @@ -273,7 +273,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "jX" = ( /obj/effect/turf_decal/tile/dark/full, @@ -286,7 +286,7 @@ pixel_x = 2 }, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "kK" = ( /obj/structure/rack, @@ -316,14 +316,14 @@ id_tag = "pirateportexternal" }, /obj/effect/mapping_helpers/airlock/locked, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "lf" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/north, /obj/structure/window/reinforced/survival_pod{ dir = 5 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "lp" = ( /obj/effect/turf_decal/stripes, @@ -333,7 +333,7 @@ /obj/effect/turf_decal/tile/neutral/full, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "lE" = ( /turf/open/floor/iron/grimy, @@ -359,7 +359,7 @@ /obj/item/stack/cable_coil/five{ pixel_y = -6 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "mA" = ( /turf/open/floor/iron, @@ -378,7 +378,7 @@ /area/shuttle/pirate) "mN" = ( /obj/machinery/loot_locator, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "nk" = ( /obj/effect/turf_decal/tile/dark_blue/half{ @@ -392,7 +392,7 @@ /area/shuttle/pirate) "nq" = ( /obj/effect/turf_decal/tile/dark/full, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "nw" = ( /turf/open/floor/iron/kitchen, @@ -410,7 +410,7 @@ id_tag = "piratestarboardexternal" }, /obj/effect/mapping_helpers/airlock/locked, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "nE" = ( /obj/effect/turf_decal/trimline/yellow/filled/line, @@ -425,13 +425,13 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "nP" = ( /obj/effect/turf_decal/tile/dark/full, /obj/effect/decal/cleanable/blood/splatter, /obj/machinery/firealarm/directional/north, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "od" = ( /obj/effect/turf_decal/tile/dark_blue/half{ @@ -523,7 +523,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, /obj/machinery/door/firedoor, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "pO" = ( /obj/effect/turf_decal/trimline/yellow/filled/corner, @@ -551,10 +551,10 @@ /obj/structure/reagent_dispensers/fueltank/large, /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "qU" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "rf" = ( /obj/structure/chair/stool/directional/south, @@ -592,7 +592,7 @@ }, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/machinery/firealarm/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "sr" = ( /obj/structure/cable, @@ -652,7 +652,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "tF" = ( /obj/effect/turf_decal/trimline/yellow/filled/line, @@ -697,7 +697,7 @@ }, /obj/effect/turf_decal/bot, /obj/machinery/light/small/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "uy" = ( /obj/machinery/chem_dispenser/drinks/fullupgrade{ @@ -713,11 +713,11 @@ "uC" = ( /obj/structure/table/reinforced, /obj/item/storage/backpack/satchel, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "uJ" = ( /obj/structure/tank_dispenser, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "vi" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/corner{ @@ -753,19 +753,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "vO" = ( /obj/effect/turf_decal/bot/right, /obj/structure/reagent_dispensers/watertank/high, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "wl" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/north, /obj/structure/window/reinforced/survival_pod{ dir = 9 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ww" = ( /obj/item/documents, @@ -805,7 +805,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "yg" = ( /obj/structure/chair/stool/directional/east, @@ -864,7 +864,7 @@ dir = 1 }, /obj/effect/turf_decal/bot/right, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Ah" = ( /obj/structure/railing{ @@ -904,13 +904,13 @@ dir = 1 }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Bl" = ( /obj/machinery/shuttle_scrambler{ name = "Wireless Tax Retrieval Machine" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Bq" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line{ @@ -1007,7 +1007,7 @@ /obj/effect/turf_decal/stripes/red/line{ dir = 6 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "CM" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line{ @@ -1054,13 +1054,13 @@ /obj/machinery/computer/piratepad_control{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Eq" = ( /obj/machinery/suit_storage_unit/standard_unit, /obj/effect/turf_decal/bot, /obj/machinery/light/small/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Et" = ( /obj/machinery/power/shuttle_engine/large, @@ -1070,7 +1070,7 @@ "EJ" = ( /obj/structure/table/reinforced, /obj/machinery/light/small/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Fn" = ( /obj/structure/chair/office{ @@ -1158,7 +1158,7 @@ name = "Fallujah firehose"; desc = "Someone's got a friend in Syria!" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "HO" = ( /obj/structure/cable, @@ -1200,7 +1200,7 @@ /obj/structure/railing/corner{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "IE" = ( /obj/effect/turf_decal/trimline/dark_blue/filled/line, @@ -1283,7 +1283,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "KA" = ( /obj/structure/chair/comfy/shuttle{ @@ -1316,7 +1316,7 @@ name = "Pirate Ship"; port_direction = 2 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "KI" = ( /obj/structure/chair/comfy/shuttle{ @@ -1369,7 +1369,7 @@ /obj/effect/mapping_helpers/apc/cut_AI_wire, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, /obj/machinery/firealarm/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "KZ" = ( /obj/machinery/door/window/brigdoor/right/directional/east{ @@ -1386,7 +1386,7 @@ /turf/open/floor/iron/white, /area/shuttle/pirate) "Lf" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Lo" = ( /obj/machinery/porta_turret/syndicate/irs{ @@ -1467,7 +1467,7 @@ }, /obj/effect/turf_decal/box/red, /obj/effect/decal/cleanable/blood/gibs/old, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "My" = ( /obj/effect/turf_decal/stripes{ @@ -1543,7 +1543,7 @@ id_tag = "pirateportexternal" }, /obj/effect/mapping_helpers/airlock/locked, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Ot" = ( /obj/machinery/porta_turret/syndicate/irs{ @@ -1570,7 +1570,7 @@ pixel_x = 6; pixel_y = 2 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "PP" = ( /obj/effect/spawner/structure/window/reinforced, @@ -1590,12 +1590,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold/scrubbers/hidden/layer2, /obj/machinery/door/firedoor, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Qc" = ( /obj/effect/turf_decal/bot/right, /obj/machinery/piratepad, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Qi" = ( /obj/structure/railing, @@ -1704,7 +1704,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "SG" = ( /obj/effect/turf_decal/siding/thinplating_new/corner{ @@ -1714,7 +1714,7 @@ dir = 1 }, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "SM" = ( /obj/effect/turf_decal/trimline/blue/filled/line, @@ -1815,7 +1815,7 @@ /area/shuttle/pirate) "VS" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "WA" = ( /obj/structure/cable, @@ -1829,7 +1829,7 @@ name = "swirlie-inator 3000" }, /obj/effect/turf_decal/bot_red, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "WR" = ( /turf/template_noop, @@ -1837,7 +1837,7 @@ "WU" = ( /obj/effect/turf_decal/tile/dark/full, /obj/machinery/light/red/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Xr" = ( /obj/machinery/door/airlock/public/glass{ diff --git a/_maps/shuttles/pirate/pirate_silverscale.dmm b/_maps/shuttles/pirate/pirate_silverscale.dmm index e5780fed0cbd..a2fd2bb9625c 100644 --- a/_maps/shuttles/pirate/pirate_silverscale.dmm +++ b/_maps/shuttles/pirate/pirate_silverscale.dmm @@ -8,7 +8,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "aZ" = ( /obj/effect/turf_decal/bot_red, @@ -16,7 +16,7 @@ helmet_type = /obj/item/clothing/head/helmet/space/pirate/tophat; suit_type = /obj/item/clothing/suit/space/pirate/silverscale }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "bp" = ( /obj/machinery/computer/monitor{ @@ -30,7 +30,7 @@ /obj/machinery/door/airlock/silver, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "de" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -43,7 +43,7 @@ /obj/machinery/door/airlock/external/glass/ruin{ id_tag = "pirateportexternal" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "dI" = ( /obj/structure/dresser, @@ -55,7 +55,7 @@ }, /obj/structure/cable, /obj/effect/turf_decal/bot, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "dT" = ( /obj/structure/table, @@ -112,7 +112,7 @@ "fR" = ( /obj/machinery/airalarm/directional/east, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "fT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -132,7 +132,7 @@ /obj/machinery/door/airlock/silver, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/effect/mapping_helpers/airlock/cutaiwire, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "hu" = ( /obj/structure/chair/stool/directional/west, @@ -154,7 +154,7 @@ dir = 8 }, /obj/effect/turf_decal/trimline/blue, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "je" = ( /turf/template_noop, @@ -173,7 +173,7 @@ /obj/effect/turf_decal/caution/stand_clear{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "ky" = ( /obj/structure/toilet{ @@ -185,7 +185,7 @@ dir = 8 }, /obj/machinery/light/small/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "kF" = ( /obj/effect/turf_decal/trimline/yellow/warning, @@ -259,7 +259,7 @@ /obj/machinery/door/airlock/external/glass/ruin{ id_tag = "pirateportexternal" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "pk" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -277,7 +277,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "rB" = ( /turf/closed/wall/mineral/titanium, @@ -331,7 +331,7 @@ /area/shuttle/pirate) "wa" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "wE" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -376,12 +376,12 @@ pixel_x = 17 }, /obj/effect/turf_decal/bot, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "yv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/machinery/meter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "zm" = ( /obj/structure/secure_safe/directional/north, @@ -406,7 +406,7 @@ /obj/effect/turf_decal/caution/stand_clear{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "zQ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -419,7 +419,7 @@ /obj/machinery/door/airlock/external/glass/ruin{ id_tag = "piratestarboardexternal" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Ae" = ( /obj/machinery/shuttle_scrambler, @@ -454,7 +454,7 @@ dir = 8 }, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Bl" = ( /obj/docking_port/stationary{ @@ -479,7 +479,7 @@ /obj/machinery/door/airlock/external/glass/ruin{ id_tag = "piratestarboardexternal" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Ch" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, @@ -511,13 +511,13 @@ name = "External Bolt Control"; id = "piratestarboardexternal" }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Fl" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Gr" = ( /obj/effect/turf_decal/trimline/yellow/warning{ @@ -559,7 +559,7 @@ projectile_damage_multiplier = 1.30; desc = "A laser gun modified to cost 20 credits to fire. Someone on the station should have an ID that works with this thing." }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Ig" = ( /obj/structure/table/glass, @@ -573,7 +573,7 @@ dir = 4 }, /obj/machinery/light/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Jc" = ( /obj/structure/table/glass, @@ -608,7 +608,7 @@ /obj/structure/reagent_dispensers/watertank, /obj/effect/turf_decal/bot/right, /obj/machinery/light/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "JV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, @@ -622,14 +622,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 6 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Ln" = ( /obj/machinery/power/smes/ship, /obj/structure/cable, /obj/effect/turf_decal/bot, /obj/machinery/light/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "LE" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -704,7 +704,7 @@ dir = 4 }, /obj/effect/mapping_helpers/airlock/cutaiwire, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "Oo" = ( /obj/structure/table/glass, @@ -738,7 +738,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "OJ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -750,17 +750,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Qm" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "QI" = ( /obj/machinery/atmospherics/components/tank/air{ dir = 1 }, /obj/effect/turf_decal/bot, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "QO" = ( /obj/machinery/newscaster/directional/north, @@ -781,7 +781,7 @@ /obj/item/multitool, /obj/effect/turf_decal/bot, /obj/effect/mapping_helpers/apc/cut_AI_wire, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "RE" = ( /obj/machinery/newscaster/directional/south, @@ -820,7 +820,7 @@ pixel_y = 4 }, /obj/item/gun/ballistic/rifle/boltaction/prime, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "RU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -843,7 +843,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Sv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -864,7 +864,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "TV" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -913,7 +913,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Vw" = ( /obj/structure/table, @@ -930,7 +930,7 @@ normaldoorcontrol = 1; specialfunctions = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Ww" = ( /obj/machinery/power/shuttle_engine/heater{ @@ -941,7 +941,7 @@ "Wy" = ( /obj/structure/reagent_dispensers/fueltank, /obj/effect/turf_decal/bot/left, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "Xw" = ( /obj/structure/table/glass, @@ -984,7 +984,7 @@ /obj/item/storage/box/lethalshot, /obj/effect/turf_decal/bot, /obj/item/gun/ballistic/shotgun/doublebarrel/slugs, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/pirate) "YD" = ( /obj/structure/table, @@ -1002,7 +1002,7 @@ "ZD" = ( /obj/machinery/door/airlock/silver/glass, /obj/effect/mapping_helpers/airlock/cutaiwire, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/pirate) "ZY" = ( /obj/machinery/airalarm/directional/east, diff --git a/_maps/shuttles/ruin/ruin_cyborg_mothership.dmm b/_maps/shuttles/ruin/ruin_cyborg_mothership.dmm index a0cb642c086a..dc22dbee7347 100644 --- a/_maps/shuttles/ruin/ruin_cyborg_mothership.dmm +++ b/_maps/shuttles/ruin/ruin_cyborg_mothership.dmm @@ -540,7 +540,7 @@ /obj/structure/cable, /obj/structure/table_frame, /obj/item/aicard, -/obj/item/ai_module/core/full/overlord, +/obj/item/ai_module/law/core/full/overlord, /turf/open/floor/circuit/green/airless, /area/shuttle/ruin/cyborg_mothership) "Em" = ( diff --git a/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm b/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm index 3bbec2623919..f2cb4fd0ef3c 100644 --- a/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm +++ b/_maps/shuttles/ruin/ruin_syndicate_dropship.dmm @@ -181,7 +181,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) "rQ" = ( /obj/effect/turf_decal/stripes/line{ @@ -244,7 +244,7 @@ /obj/effect/mapping_helpers/apc/syndicate_access, /obj/effect/decal/cleanable/dirt, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) "zP" = ( /obj/machinery/computer/crew{ @@ -362,7 +362,7 @@ /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/syndicate_access, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) "Mc" = ( /obj/effect/turf_decal/tile/red/half/contrasted, @@ -373,7 +373,7 @@ dir = 1 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) "Oe" = ( /obj/machinery/power/smes/super/full, @@ -469,7 +469,7 @@ "Vh" = ( /obj/structure/chair/comfy/shuttle, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) "Vp" = ( /obj/machinery/firealarm/directional/west, @@ -481,7 +481,7 @@ }, /obj/machinery/firealarm/directional/south, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/ruin/caravan/syndicate3) (1,1,1) = {" diff --git a/_maps/shuttles/whiteship/whiteship_box.dmm b/_maps/shuttles/whiteship/whiteship_box.dmm index b0be5fc47f61..262d408c0706 100644 --- a/_maps/shuttles/whiteship/whiteship_box.dmm +++ b/_maps/shuttles/whiteship/whiteship_box.dmm @@ -1581,7 +1581,7 @@ pixel_x = -3; pixel_y = -3 }, -/obj/item/food/grown/harebell{ +/obj/item/food/grown/flower/harebell{ pixel_x = 6; pixel_y = 3 }, diff --git a/_maps/shuttles/whiteship/whiteship_cere.dmm b/_maps/shuttles/whiteship/whiteship_cere.dmm index 4696f2727a44..30125bc5cbf0 100644 --- a/_maps/shuttles/whiteship/whiteship_cere.dmm +++ b/_maps/shuttles/whiteship/whiteship_cere.dmm @@ -215,7 +215,7 @@ /area/shuttle/abandoned/crew) "gK" = ( /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "hj" = ( /obj/machinery/door/airlock/hatch{ @@ -315,12 +315,12 @@ }, /obj/item/storage/box/lights/mixed, /obj/item/storage/box/lights/bulbs, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "mV" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "nf" = ( /obj/machinery/door/airlock{ @@ -343,7 +343,7 @@ /obj/item/mining_scanner, /obj/machinery/airalarm/directional/south, /obj/effect/mapping_helpers/airalarm/unlocked, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "ns" = ( /obj/structure/cable, @@ -458,7 +458,7 @@ "rZ" = ( /obj/effect/decal/cleanable/dirt, /mob/living/basic/migo, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "su" = ( /obj/effect/spawner/structure/window/reinforced/shuttle, @@ -508,7 +508,7 @@ "wN" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/reagent_dispensers/fueltank, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "yA" = ( /obj/structure/cable, @@ -548,7 +548,7 @@ req_access = list() }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Ah" = ( /obj/structure/cable, @@ -564,7 +564,7 @@ "Bq" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/firecloset/full, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "BN" = ( /obj/structure/cable, @@ -578,7 +578,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "CC" = ( /obj/machinery/vending/boozeomat, @@ -591,14 +591,14 @@ /obj/structure/window/reinforced/spawner/directional/east, /obj/machinery/suit_storage_unit/spaceruin, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Dr" = ( /obj/effect/spawner/random/exotic/ripley, /obj/effect/decal/cleanable/dirt, /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/unlocked, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Dy" = ( /obj/structure/cable, @@ -673,14 +673,14 @@ /obj/machinery/firealarm/directional/south, /obj/structure/closet/crate, /obj/effect/spawner/random/maintenance/six, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Ha" = ( /obj/machinery/computer/mech_bay_power_console{ dir = 4 }, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Hw" = ( /obj/machinery/light/small/directional/east, @@ -713,7 +713,7 @@ /obj/structure/window/reinforced/spawner/directional/west, /obj/machinery/suit_storage_unit/spaceruin, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "HZ" = ( /obj/machinery/microwave, @@ -743,7 +743,7 @@ "Km" = ( /obj/effect/spawner/random/structure/crate, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Ko" = ( /obj/machinery/power/shuttle_engine/propulsion/right{ @@ -771,7 +771,7 @@ /obj/item/gun/energy/laser/retro, /obj/structure/closet/crate/secure/weapon, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Li" = ( /obj/structure/closet/secure_closet/freezer/meat/open, @@ -888,7 +888,7 @@ /obj/item/stack/sheet/mineral/plasma, /obj/structure/closet/crate, /obj/machinery/light/broken/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Sx" = ( /obj/machinery/door/airlock/command{ @@ -942,38 +942,38 @@ "TD" = ( /obj/machinery/suit_storage_unit/spaceruin, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "TG" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/structure/tank_dispenser/oxygen, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "US" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "Vx" = ( /obj/structure/window/reinforced/spawner/directional/east, /obj/structure/tank_dispenser/oxygen, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "VD" = ( /obj/effect/spawner/random/structure/crate, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/directional/south, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "WF" = ( /obj/machinery/light/broken/directional/north, /obj/machinery/mech_bay_recharge_port, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/cargo) "XT" = ( /obj/machinery/door/airlock/external/glass/ruin, diff --git a/_maps/shuttles/whiteship/whiteship_kilo.dmm b/_maps/shuttles/whiteship/whiteship_kilo.dmm index a7a1940b5c22..197c6384560b 100644 --- a/_maps/shuttles/whiteship/whiteship_kilo.dmm +++ b/_maps/shuttles/whiteship/whiteship_kilo.dmm @@ -160,7 +160,7 @@ /obj/effect/turf_decal/trimline/brown/warning{ dir = 10 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/engine) "ig" = ( /obj/effect/decal/cleanable/dirt, @@ -306,7 +306,7 @@ /obj/structure/cable, /obj/machinery/airalarm/directional/west, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/crew) "mH" = ( /obj/effect/turf_decal/stripes{ @@ -388,7 +388,7 @@ pixel_y = 4 }, /obj/machinery/atmospherics/components/unary/vent_pump/on, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/bridge) "qL" = ( /obj/effect/turf_decal/stripes/line{ @@ -483,7 +483,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/machinery/light/small/blacklight/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/bridge) "uA" = ( /obj/machinery/porta_turret/centcom_shuttle/weak{ @@ -515,7 +515,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /obj/effect/decal/cleanable/blood/footprints, /obj/structure/cable, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/crew) "yG" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ @@ -556,7 +556,7 @@ /obj/item/stack/cable_coil/cut, /obj/machinery/airalarm/directional/south, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/bridge) "zu" = ( /obj/machinery/cell_charger, @@ -681,7 +681,7 @@ pixel_x = 9; pixel_y = -2 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/engine) "HD" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ @@ -1043,7 +1043,7 @@ pixel_x = -24; pixel_y = -6 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned/bridge) "Tf" = ( /obj/structure/table, @@ -1061,7 +1061,7 @@ }, /obj/machinery/airalarm/directional/north, /obj/effect/mapping_helpers/airalarm/all_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned/engine) "Tg" = ( /obj/effect/decal/cleanable/dirt, diff --git a/_maps/shuttles/whiteship/whiteship_pubby.dmm b/_maps/shuttles/whiteship/whiteship_pubby.dmm index c351bbdd41b0..303794f46fb1 100644 --- a/_maps/shuttles/whiteship/whiteship_pubby.dmm +++ b/_maps/shuttles/whiteship/whiteship_pubby.dmm @@ -117,7 +117,7 @@ /obj/machinery/holopad/secure, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "eU" = ( /obj/effect/turf_decal/trimline/purple/filled/line, @@ -131,7 +131,7 @@ /obj/structure/frame/computer, /obj/item/shard, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned) "gN" = ( /obj/effect/turf_decal/trimline/purple/filled/end, @@ -239,7 +239,7 @@ /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/broken/directional/east, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "lO" = ( /obj/effect/turf_decal/bot_white, @@ -265,7 +265,7 @@ pixel_y = 28 }, /obj/machinery/recharger, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "lV" = ( /obj/machinery/computer/camera_advanced/shuttle_docker/whiteship{ @@ -274,7 +274,7 @@ }, /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned) "mh" = ( /obj/effect/turf_decal/trimline/purple/filled/corner{ @@ -329,7 +329,7 @@ /area/shuttle/abandoned) "oI" = ( /obj/structure/bookcase/manuals/research_and_development, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "oT" = ( /obj/effect/turf_decal/trimline/purple/filled/end{ @@ -668,7 +668,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/door_assembly/door_assembly_com, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "DQ" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ @@ -882,7 +882,7 @@ "Lf" = ( /obj/machinery/computer/shuttle/white_ship/bridge, /obj/effect/decal/cleanable/dirt, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/abandoned) "Lg" = ( /obj/structure/sign/warning/vacuum/external/directional/east, @@ -902,7 +902,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 }, -/obj/item/stack/tile/pod/light{ +/obj/item/stack/tile/mineral/plastitanium/pod/light{ pixel_x = 6; pixel_y = -4 }, @@ -1215,7 +1215,7 @@ }, /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small/empty/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/abandoned) "ZT" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ diff --git a/_maps/shuttles/whiteship/whiteship_tram.dmm b/_maps/shuttles/whiteship/whiteship_tram.dmm index 27391955c3fd..2ea5c4d104a6 100644 --- a/_maps/shuttles/whiteship/whiteship_tram.dmm +++ b/_maps/shuttles/whiteship/whiteship_tram.dmm @@ -129,7 +129,7 @@ /area/shuttle/abandoned/cargo) "aw" = ( /obj/structure/sign/directions/supply, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/abandoned/cargo) "aB" = ( /obj/structure/chair/comfy/shuttle, @@ -141,7 +141,7 @@ /area/shuttle/abandoned/cargo) "aG" = ( /obj/effect/turf_decal/mining, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/abandoned/cargo) "aH" = ( /obj/effect/turf_decal/bot_red, @@ -151,7 +151,7 @@ "aI" = ( /obj/effect/turf_decal/caution/white, /obj/effect/spawner/random/trash/grime, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "aJ" = ( /obj/item/stack/sheet/mineral/titanium, @@ -204,10 +204,10 @@ /area/shuttle/abandoned/cargo) "aS" = ( /obj/effect/spawner/random/trash/food_packaging, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "aT" = ( -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/abandoned/cargo) "aU" = ( /obj/machinery/porta_turret/centcom_shuttle/weak{ @@ -226,7 +226,7 @@ /obj/effect/spawner/random/trash/hobo_squat, /obj/effect/mob_spawn/corpse/human/assistant, /obj/structure/sign/warning/secure_area/directional/west, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "aX" = ( /obj/machinery/power/shuttle_engine/propulsion{ @@ -239,7 +239,7 @@ /area/shuttle/abandoned/engine) "aY" = ( /obj/structure/sign/warning/chem_diamond, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/abandoned/cargo) "aZ" = ( /turf/closed/wall/mineral/plastitanium, @@ -295,7 +295,7 @@ /obj/effect/turf_decal/caution/white{ dir = 4 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "bl" = ( /obj/effect/turf_decal/bot_red, @@ -307,7 +307,7 @@ dir = 8 }, /obj/effect/spawner/random/trash/food_packaging, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "bp" = ( /obj/effect/decal/cleanable/blood, @@ -373,7 +373,7 @@ /obj/structure/sign/directions/supply{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/shuttle/abandoned/cargo) "bC" = ( /obj/machinery/porta_turret/centcom_shuttle/weak{ @@ -577,7 +577,7 @@ }, /area/shuttle/abandoned/cargo) "cA" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "cB" = ( /obj/structure/chair/comfy/shuttle{ @@ -602,7 +602,7 @@ dir = 1 }, /obj/effect/spawner/random/exotic/ripley, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "cH" = ( /obj/effect/turf_decal/bot_red, @@ -1003,11 +1003,11 @@ "sR" = ( /obj/machinery/door/window/survival_pod/left/directional/west, /obj/effect/turf_decal/delivery/red, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "ua" = ( /obj/effect/spawner/random/trash/botanical_waste, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "ue" = ( /obj/effect/decal/cleanable/blood/tracks{ @@ -1231,7 +1231,7 @@ /area/shuttle/abandoned/crew) "KQ" = ( /obj/structure/sign/warning/secure_area/directional/east, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "LM" = ( /obj/structure/cable, @@ -1282,7 +1282,7 @@ "PI" = ( /obj/machinery/door/window/survival_pod/left/directional/east, /obj/effect/turf_decal/delivery/red, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/shuttle/abandoned/cargo) "PU" = ( /obj/effect/decal/cleanable/blood/gibs, diff --git a/_maps/templates/battlecruiser_starfury.dmm b/_maps/templates/battlecruiser_starfury.dmm index 24c52bda672d..3588ed6f485f 100644 --- a/_maps/templates/battlecruiser_starfury.dmm +++ b/_maps/templates/battlecruiser_starfury.dmm @@ -550,7 +550,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "ce" = ( /obj/machinery/shieldgen{ @@ -731,7 +731,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "cP" = ( /obj/effect/spawner/structure/window/reinforced/plasma/plastitanium, @@ -754,7 +754,7 @@ /obj/structure/disposalpipe/segment{ dir = 6 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "cU" = ( /obj/docking_port/stationary/starfury_fighter/fighter_two{ @@ -797,7 +797,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "de" = ( /obj/machinery/button/door{ @@ -1027,11 +1027,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/door/airlock/multi_tile/public/glass, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "dV" = ( /obj/machinery/door/firedoor/heavy, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "dW" = ( /obj/structure/table, @@ -1075,7 +1075,7 @@ /turf/open/floor/iron, /area/shuttle/sbc_starfury) "ec" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "ef" = ( /obj/machinery/iv_drip, @@ -1108,7 +1108,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "ei" = ( /obj/structure/chair/stool/bar/directional/south, @@ -1120,7 +1120,7 @@ /area/shuttle/sbc_starfury) "ep" = ( /obj/machinery/light/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "et" = ( /obj/structure/cable, @@ -1177,7 +1177,7 @@ /area/shuttle/sbc_starfury) "eB" = ( /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "eC" = ( /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, @@ -1317,7 +1317,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "eS" = ( /obj/structure/table, @@ -1351,7 +1351,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "fb" = ( /obj/structure/disposalpipe/segment{ @@ -1360,7 +1360,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "fd" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -1412,7 +1412,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "fj" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -1475,7 +1475,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "fr" = ( /obj/effect/turf_decal/stripes/line{ @@ -1568,7 +1568,7 @@ /obj/machinery/door/firedoor/heavy, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/machinery/door/airlock/multi_tile/public/glass, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "fE" = ( /obj/structure/table/reinforced, @@ -1584,7 +1584,7 @@ /obj/machinery/door/firedoor/heavy, /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/machinery/door/airlock/multi_tile/public/glass, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "fG" = ( /obj/structure/disposalpipe/segment, @@ -1592,7 +1592,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/door/firedoor/heavy, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "fK" = ( /obj/effect/mapping_helpers/airlock/cutaiwire, @@ -1726,7 +1726,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/vehicle/sealed/mecha/marauder/mauler/loaded, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "gH" = ( /obj/machinery/vending/boozeomat/syndicate, @@ -1763,7 +1763,7 @@ "gR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "gS" = ( /obj/machinery/firealarm/directional/east, @@ -1853,14 +1853,14 @@ }, /obj/item/storage/belt/military, /obj/effect/turf_decal/siding/red, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "hc" = ( /obj/effect/turf_decal/delivery, /obj/machinery/door/poddoor/shutters/syndicate{ id = "syndie_battlecruiser_mech" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "hd" = ( /obj/machinery/disposal/bin, @@ -2071,7 +2071,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "hC" = ( /obj/structure/closet/secure_closet{ @@ -2112,7 +2112,7 @@ /obj/effect/turf_decal/stripes/corner{ dir = 1 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "hD" = ( /obj/structure/chair/stool/bar/directional/south, @@ -2202,7 +2202,7 @@ pixel_x = 3; pixel_y = -3 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "ia" = ( /obj/structure/rack{ @@ -2217,7 +2217,7 @@ pixel_x = 3; pixel_y = -3 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "ib" = ( /obj/structure/closet/secure_closet{ @@ -2249,7 +2249,7 @@ pixel_x = 3 }, /obj/structure/extinguisher_cabinet/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "ie" = ( /obj/structure/table/reinforced/plastitaniumglass, @@ -2309,7 +2309,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "il" = ( /obj/structure/table, @@ -2361,7 +2361,7 @@ pixel_x = -1; pixel_y = 2 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "iq" = ( /obj/machinery/microwave, @@ -2436,7 +2436,7 @@ /obj/item/storage/belt/military, /obj/item/ammo_box/magazine/m7mm, /obj/item/gun/ballistic/automatic/l6_saw, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "iy" = ( /obj/item/storage/box/donkpockets{ @@ -2553,7 +2553,7 @@ }, /obj/item/clothing/mask/gas/syndicate, /obj/machinery/firealarm/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "iO" = ( /obj/structure/table, @@ -2589,7 +2589,7 @@ pixel_x = 8; pixel_y = 8 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "iP" = ( /obj/effect/turf_decal/bot, @@ -3125,7 +3125,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/light/small/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "kK" = ( /obj/effect/turf_decal/bot, @@ -3598,7 +3598,7 @@ /obj/machinery/syndicatebomb{ pixel_y = -2 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "mY" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -3651,7 +3651,7 @@ /obj/structure/disposalpipe/segment{ dir = 9 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "nN" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ @@ -3706,7 +3706,7 @@ "oO" = ( /obj/structure/tank_dispenser/oxygen, /obj/effect/turf_decal/delivery/red, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "oX" = ( /obj/structure/cable, @@ -3720,7 +3720,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "pm" = ( /obj/machinery/computer/records/medical/syndie, @@ -3792,7 +3792,7 @@ "qb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "qf" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -3858,7 +3858,7 @@ dir = 1 }, /obj/structure/sign/warning/secure_area/directional/west, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "rr" = ( /obj/structure/cable, @@ -3899,7 +3899,7 @@ specialfunctions = 4 }, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "sh" = ( /obj/structure/cable, @@ -3922,12 +3922,12 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "sY" = ( /obj/structure/disposalpipe/segment, /obj/effect/turf_decal/stripes/line, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "te" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -4154,7 +4154,7 @@ "wC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/firealarm/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "wN" = ( /obj/machinery/porta_turret/syndicate/energy{ @@ -4393,7 +4393,7 @@ /obj/machinery/door/poddoor/shutters/syndicate{ id = "syndie_battlecruiser_mech" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "AF" = ( /obj/machinery/airalarm/directional/west, @@ -4415,14 +4415,14 @@ /obj/effect/mapping_helpers/airlock/cutaiwire, /obj/machinery/door/airlock/multi_tile/public/glass, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "AQ" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 }, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Be" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -4522,7 +4522,7 @@ dir = 1 }, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Dd" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, @@ -4568,7 +4568,7 @@ "DV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Ex" = ( /obj/effect/turf_decal/stripes/line{ @@ -4765,7 +4765,7 @@ /obj/effect/turf_decal/stripes/line{ dir = 1 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "GO" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ @@ -4810,7 +4810,7 @@ /area/shuttle/sbc_starfury) "HL" = ( /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "HP" = ( /obj/structure/dresser, @@ -4819,7 +4819,7 @@ "HT" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "HU" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ @@ -4887,7 +4887,7 @@ /obj/item/ammo_box/magazine/sniper_rounds, /obj/item/gun/ballistic/rifle/sniper_rifle/syndicate, /obj/effect/mapping_helpers/airalarm/syndicate_access, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "Jm" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -4901,7 +4901,7 @@ dir = 1 }, /obj/structure/disposalpipe/segment, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "JF" = ( /obj/machinery/power/turbine/core_rotor, @@ -4940,7 +4940,7 @@ pixel_x = 3; pixel_y = -3 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/shuttle/sbc_starfury) "JP" = ( /obj/effect/turf_decal/stripes/line{ @@ -5021,7 +5021,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Mt" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -5046,7 +5046,7 @@ "ME" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/light/floor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "MK" = ( /obj/structure/disposalpipe/segment, @@ -5147,7 +5147,7 @@ /obj/structure/cable, /obj/machinery/airalarm/directional/south, /obj/effect/mapping_helpers/airalarm/syndicate_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "OG" = ( /obj/effect/turf_decal/bot, @@ -5223,7 +5223,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "PF" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -5421,12 +5421,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "SW" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/decal/cleanable/blood/oil, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Ta" = ( /obj/effect/turf_decal/stripes/line{ @@ -5541,7 +5541,7 @@ /area/shuttle/sbc_starfury) "Vr" = ( /obj/machinery/light/floor, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Vt" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -5762,7 +5762,7 @@ name = "Mech Storage Shutters"; req_access = list("syndicate_leader") }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Yt" = ( /obj/machinery/space_heater, @@ -5772,7 +5772,7 @@ "Yw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Za" = ( /obj/structure/cable, @@ -5782,7 +5782,7 @@ dir = 9 }, /obj/machinery/light/small/directional/east, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/shuttle/sbc_starfury) "Zl" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ diff --git a/_maps/templates/hilbertshotel.dmm b/_maps/templates/hilbertshotel.dmm index 8c425aa9c13d..76258e54c8eb 100644 --- a/_maps/templates/hilbertshotel.dmm +++ b/_maps/templates/hilbertshotel.dmm @@ -87,7 +87,7 @@ /area/misc/hilbertshotel) "s" = ( /obj/structure/table/wood/fancy, -/obj/item/food/grown/poppy, +/obj/item/food/grown/flower/poppy, /turf/open/indestructible/hotelwood, /area/misc/hilbertshotel) "t" = ( diff --git a/_maps/templates/holodeck_chapelcourt.dmm b/_maps/templates/holodeck_chapelcourt.dmm index 6f87e03bc162..68326ebb73d0 100644 --- a/_maps/templates/holodeck_chapelcourt.dmm +++ b/_maps/templates/holodeck_chapelcourt.dmm @@ -1,52 +1,10 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"aa" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/closet/secure_closet/courtroom, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"ab" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/turf/open/floor/holofloor/dark, -/area/template_noop) "ac" = ( /obj/structure/chair{ dir = 1 }, /turf/open/floor/holofloor/chapel/bottom_left, /area/template_noop) -"ad" = ( -/obj/structure/table/wood/fancy, -/obj/item/clothing/suit/chaplainsuit/nun, -/obj/item/clothing/head/chaplain/nun_hood, -/obj/item/clothing/suit/chaplainsuit/holidaypriest, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) "ae" = ( /obj/structure/table/wood, /obj/item/folder/blue, @@ -62,21 +20,6 @@ "ag" = ( /turf/open/floor/holofloor/chapel/top_left, /area/template_noop) -"ah" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/turf/open/floor/holofloor/dark, -/area/template_noop) "ai" = ( /turf/open/floor/holofloor/chapel/bottom_right, /area/template_noop) @@ -94,115 +37,23 @@ }, /turf/open/floor/holofloor/chapel/top_right, /area/template_noop) -"al" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/folder/blue, -/obj/item/pen/red, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"am" = ( -/obj/item/clothing/head/helmet/chaplain/witchunter_hat, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood/fancy, -/turf/open/floor/holofloor/dark, -/area/template_noop) "an" = ( /obj/structure/table/wood, /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/holofloor/dark, /area/template_noop) -"ao" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/storage/fancy/candle_box, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"ap" = ( -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"aq" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/folder/red, -/obj/item/pen/red, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"ar" = ( -/obj/item/gavelblock, -/obj/item/gavelhammer, -/obj/structure/table/wood, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"as" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) "at" = ( /obj/structure/table/wood, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, +/obj/item/food/grown/flower/harebell, /obj/effect/turf_decal/tile/neutral/opposingcorners{ dir = 1 }, /turf/open/floor/holofloor/dark, /area/template_noop) -"au" = ( -/obj/structure/table/wood/fancy, -/obj/item/book/bible, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) "av" = ( /obj/structure/table/wood, /obj/item/folder/red, @@ -212,41 +63,6 @@ }, /turf/open/floor/holofloor/dark, /area/template_noop) -"aw" = ( -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"ax" = ( -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"ay" = ( -/obj/item/toy/figure/chaplain, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood/fancy, -/turf/open/floor/holofloor/dark, -/area/template_noop) "az" = ( /obj/structure/closet/secure_closet/courtroom, /obj/effect/turf_decal/tile/neutral/opposingcorners{ @@ -285,58 +101,18 @@ }, /turf/open/floor/holofloor/chapel/bottom_right, /area/template_noop) -"aF" = ( -/obj/item/clothing/suit/costume/judgerobe, -/obj/item/clothing/head/costume/powdered_wig, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood/fancy, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aG" = ( /obj/structure/table/wood, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, -/obj/item/food/grown/poppy, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy, +/obj/item/food/grown/flower/poppy, /obj/effect/turf_decal/tile/neutral/opposingcorners{ dir = 1 }, /turf/open/floor/holofloor/dark, /area/template_noop) -"aH" = ( -/obj/structure/table/wood/fancy, -/obj/item/book/manual/wiki/security_space_law, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"aI" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aJ" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 8 @@ -346,45 +122,12 @@ "aK" = ( /turf/open/floor/holofloor/chapel/top_right, /area/template_noop) -"aL" = ( -/obj/structure/chair{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aM" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners{ dir = 1 }, /turf/open/floor/holofloor/dark, /area/template_noop) -"aN" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"aO" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/paper_bin, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aP" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/holofloor/dark, @@ -397,36 +140,12 @@ /obj/effect/turf_decal/tile/neutral/anticorner/contrasted, /turf/open/floor/holofloor/dark, /area/template_noop) -"aS" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aT" = ( /obj/structure/chair{ dir = 1 }, /turf/open/floor/holofloor/chapel/top_left, /area/template_noop) -"aU" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood, -/obj/item/hand_labeler, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aV" = ( /obj/structure/chair{ dir = 1 @@ -436,31 +155,6 @@ }, /turf/open/floor/holofloor/dark, /area/template_noop) -"aW" = ( -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/holofloor/dark, -/area/template_noop) -"aX" = ( -/obj/item/clothing/suit/chaplainsuit/bishoprobe, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/obj/structure/table/wood/fancy, -/turf/open/floor/holofloor/dark, -/area/template_noop) "aY" = ( /obj/item/gavelblock, /obj/item/gavelhammer, diff --git a/_maps/templates/mansus_backdoor.dmm b/_maps/templates/mansus_backdoor.dmm new file mode 100644 index 000000000000..ab7864012a35 --- /dev/null +++ b/_maps/templates/mansus_backdoor.dmm @@ -0,0 +1,3545 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"as" = ( +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"ay" = ( +/obj/effect/turf_decal/siding/wood/corner{ + dir = 4 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"aD" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -30 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"ei" = ( +/obj/structure/dresser, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"eo" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = 2 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"fq" = ( +/turf/open/mirage{ + dir = 4; + target_turf_x = -31; + range = 12 + }, +/area/centcom/heretic_backdoor) +"fW" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -3; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"gm" = ( +/obj/structure/table/wood/shuttle_bar, +/obj/effect/turf_decal/siding/wood, +/obj/item/paper/crumpled{ + default_raw_text = "We know you're here." + }, +/obj/item/pen, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"iG" = ( +/obj/structure/fireplace{ + desc = "It looks like it hasn't been lit in a long time." + }, +/obj/effect/decal/cleanable/ash{ + pixel_y = 0; + pixel_x = 4 + }, +/turf/open/floor/stone, +/area/centcom/heretic_backdoor) +"iL" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = 1 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"jq" = ( +/obj/effect/turf_decal/siding/wood/corner{ + dir = 4 + }, +/obj/structure/chair/sofa/corner/brown{ + dir = 1 + }, +/obj/effect/turf_decal/siding/wood/corner{ + dir = 1 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"lL" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -1; + teleport_x_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"nc" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -2; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"nf" = ( +/turf/open/mirage{ + dir = 8; + target_turf_x = 31; + range = 12 + }, +/area/centcom/heretic_backdoor) +"oy" = ( +/obj/effect/spawner/structure/window, +/obj/structure/curtain/bounty, +/turf/open/indestructible/plating, +/area/centcom/heretic_backdoor) +"oz" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"oH" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"pE" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 1; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"pS" = ( +/obj/item/paper/crumpled{ + invisibility = 101; + default_raw_text = "How do mirage work: TL;DR projects a number of turfs in front of the target turf (according to direction and offset) based on range." + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"ql" = ( +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"qF" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -2; + teleport_x_offset = -3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"rv" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -30 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"tY" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -2; + teleport_x_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"va" = ( +/obj/effect/spawner/structure/window, +/obj/structure/curtain/bounty/start_closed, +/turf/open/indestructible/plating, +/area/centcom/heretic_backdoor) +"vY" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 30 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"xb" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = -1 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"xr" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = 30 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"xA" = ( +/obj/structure/bed/double, +/obj/item/bedsheet/cosmos/double, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"xD" = ( +/obj/structure/chair/wood{ + dir = 8 + }, +/obj/effect/turf_decal/siding/wood, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"yL" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 2; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Ah" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -1; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Ak" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"Fx" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -1; + teleport_x_offset = -3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"FH" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/obj/structure/closet/cabinet, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"Gp" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"KE" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/structure/chair/sofa/right/brown{ + dir = 1 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"Lu" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = -2 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Ng" = ( +/turf/open/mirage{ + target_turf_y = 31; + range = 7 + }, +/area/centcom/heretic_backdoor) +"Nx" = ( +/turf/closed/indestructible/wood, +/area/centcom/heretic_backdoor) +"Ny" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 3; + teleport_y_offset = 1 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"OC" = ( +/obj/structure/chair/wood{ + dir = 4 + }, +/obj/effect/turf_decal/siding/wood, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"PM" = ( +/obj/structure/table/wood/shuttle_bar, +/obj/effect/turf_decal/siding/wood/corner, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) +"PY" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Qh" = ( +/turf/open/mirage{ + dir = 1; + target_turf_y = -31; + range = 7 + }, +/area/centcom/heretic_backdoor) +"Qm" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = -3; + teleport_x_offset = -3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"QU" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -3; + teleport_y_offset = 2 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Tn" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"Uo" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 3; + teleport_y_offset = 2 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"UX" = ( +/obj/structure/mineral_door/wood, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/turf/open/floor/stone, +/area/centcom/heretic_backdoor) +"WH" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = 3; + teleport_y_offset = 3 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"XR" = ( +/turf/open/floor/stone, +/area/centcom/heretic_backdoor) +"Yi" = ( +/obj/effect/step_trigger/teleporter/offset{ + teleport_x_offset = -3; + teleport_y_offset = 1 + }, +/turf/open/floor/grass/norandomicon{ + desc = "Focusing intently on individual blades of grass, it almost looks like they're moving." + }, +/area/centcom/heretic_backdoor) +"YR" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 4 + }, +/obj/structure/chair/sofa/left/brown{ + dir = 4 + }, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/turf/open/floor/wood/large, +/area/centcom/heretic_backdoor) + +(1,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(2,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +pS +as +"} +(3,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(4,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(5,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(6,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(7,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(8,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(9,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(10,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(11,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(12,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(13,1,1) = {" +as +as +as +as +as +as +as +as +oz +tY +lL +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +PY +Ny +Uo +WH +as +as +as +as +as +as +as +as +"} +(14,1,1) = {" +as +as +as +as +as +as +as +as +eo +as +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +nf +as +yL +as +as +as +as +as +as +as +as +"} +(15,1,1) = {" +as +as +as +as +as +as +as +as +iL +Qh +as +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +vY +as +Ng +pE +as +as +as +as +as +as +as +as +"} +(16,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(17,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(18,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(19,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(20,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(21,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(22,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(23,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(24,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(25,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(26,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(27,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +Nx +va +Nx +UX +Nx +Nx +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(28,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +Nx +YR +jq +ql +ql +Nx +Nx +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(29,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +Nx +XR +KE +ql +ql +OC +oy +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(30,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +Nx +iG +Ak +ql +ql +gm +oy +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(31,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +Nx +XR +Ak +ql +ql +xD +oy +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(32,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +Nx +Nx +Nx +UX +Nx +FH +Nx +Nx +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(33,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +Nx +ei +ql +ql +Nx +va +Nx +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(34,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +Nx +PM +xA +ay +Nx +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(35,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +Nx +Nx +va +Nx +Nx +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(36,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(37,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(38,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(39,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(40,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(41,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(42,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(43,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(44,1,1) = {" +as +as +as +as +as +as +as +as +Gp +Qh +aD +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +xr +Ng +Tn +as +as +as +as +as +as +as +as +"} +(45,1,1) = {" +as +as +as +as +as +as +as +as +xb +Qh +as +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +rv +as +Ng +Ah +as +as +as +as +as +as +as +as +"} +(46,1,1) = {" +as +as +as +as +as +as +as +as +Lu +as +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +fq +as +nc +as +as +as +as +as +as +as +as +"} +(47,1,1) = {" +as +as +as +as +as +as +as +as +Qm +qF +Fx +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +Yi +QU +fW +as +as +as +as +as +as +as +as +"} +(48,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(49,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(50,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(51,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(52,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(53,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(54,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(55,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(56,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(57,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(58,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} +(59,1,1) = {" +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +as +"} diff --git a/_maps/templates/shelter_1.dmm b/_maps/templates/shelter_1.dmm index 1e5d6502e03c..ff83de3a64af 100644 --- a/_maps/templates/shelter_1.dmm +++ b/_maps/templates/shelter_1.dmm @@ -1,72 +1,72 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "b" = ( /obj/effect/turf_decal/mining/survival{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "c" = ( /obj/structure/fans, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "d" = ( /obj/machinery/smartfridge/survival_pod/preloaded, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "e" = ( /obj/item/gps/computer, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "f" = ( /obj/effect/turf_decal/mining/survival{ dir = 8 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "g" = ( /obj/machinery/stasis/survival_pod, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "h" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "i" = ( /obj/structure/bed/pod, /obj/item/bedsheet/black, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "j" = ( /obj/effect/turf_decal/mining/survival{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "k" = ( /obj/structure/table/survival_pod, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "l" = ( /obj/structure/tubes, /obj/structure/chair/comfy/black{ dir = 8 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "m" = ( /obj/effect/turf_decal/mining/survival, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "n" = ( /obj/structure/fans/tiny, /obj/machinery/door/airlock/survival_pod/glass, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "o" = ( /obj/effect/turf_decal/mining, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) (1,1,1) = {" diff --git a/_maps/templates/shelter_2.dmm b/_maps/templates/shelter_2.dmm index 38a742b85fd5..fb349b2d8570 100644 --- a/_maps/templates/shelter_2.dmm +++ b/_maps/templates/shelter_2.dmm @@ -1,12 +1,12 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "b" = ( /obj/effect/turf_decal/mining/survival{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "c" = ( /obj/machinery/shower/directional/south, @@ -17,49 +17,49 @@ icon_state = "bathroom-closed"; open = 0 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "d" = ( /obj/effect/turf_decal/mining/survival{ dir = 8 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "e" = ( /obj/structure/fans, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "f" = ( /obj/machinery/smartfridge/survival_pod/preloaded, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "g" = ( /obj/item/gps/computer, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "i" = ( /obj/structure/toilet/secret{ secret_type = /obj/item/knife/combat/survival }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "j" = ( /obj/effect/turf_decal/mining/survival{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "k" = ( /obj/machinery/stasis/survival_pod, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "l" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "m" = ( /obj/structure/bed/pod, /obj/item/bedsheet/black, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "n" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/west, @@ -80,12 +80,12 @@ /obj/item/storage/medkit/brute{ pixel_x = 5 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "q" = ( /obj/structure/tubes, /obj/item/kirbyplants/random, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "r" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/west, @@ -164,16 +164,16 @@ /area/misc/survivalpod) "C" = ( /obj/effect/turf_decal/mining/survival, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "D" = ( /obj/structure/fans/tiny, /obj/machinery/door/airlock/survival_pod/glass, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "E" = ( /obj/effect/turf_decal/mining, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) (1,1,1) = {" diff --git a/_maps/templates/shelter_3.dmm b/_maps/templates/shelter_3.dmm index f474297cc1ed..0cafc1c45d42 100644 --- a/_maps/templates/shelter_3.dmm +++ b/_maps/templates/shelter_3.dmm @@ -1,55 +1,55 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "b" = ( /obj/effect/turf_decal/mining/survival{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "c" = ( -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "d" = ( /obj/effect/turf_decal/mining/survival{ dir = 1 }, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "e" = ( /obj/effect/turf_decal/mining/survival{ dir = 8 }, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "f" = ( /obj/structure/table/wood/fancy/black, /obj/machinery/chem_dispenser/drinks, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "g" = ( /obj/structure/table/wood/fancy/black, /obj/machinery/chem_dispenser/drinks/beer, /obj/machinery/light/directional/north, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "h" = ( /obj/machinery/vending/boozeomat, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "i" = ( /obj/item/book/manual/wiki/barman_recipes, /obj/item/reagent_containers/cup/glass/shaker, /obj/item/rag, /obj/structure/table/wood/fancy/black, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "j" = ( /obj/structure/table/wood/fancy/black, /obj/item/clipboard, /obj/item/toy/figure/bartender, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "k" = ( /obj/structure/table/wood/fancy/black, @@ -60,24 +60,24 @@ /obj/item/storage/fancy/cigarettes/cigars/havana{ pixel_y = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "l" = ( /obj/structure/table/wood/fancy/black, /obj/effect/spawner/random/food_or_drink/keg, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "m" = ( /obj/machinery/light/directional/north, /obj/structure/closet/secure_closet/bar/all_access, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "n" = ( /obj/structure/disposalpipe/trunk{ dir = 4 }, /obj/machinery/disposal/bin, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "o" = ( /obj/effect/turf_decal/mining/survival{ @@ -86,21 +86,21 @@ /obj/structure/disposalpipe/junction{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "p" = ( /obj/machinery/door/airlock/survival_pod/glass, /obj/effect/mapping_helpers/airlock/access/any/service/bar, /obj/effect/mapping_helpers/airlock/access/any/supply/mining, /obj/structure/fans/tiny, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "q" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "r" = ( /obj/structure/disposalpipe/segment, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "s" = ( /obj/structure/table/reinforced, @@ -109,11 +109,11 @@ pixel_y = 4 }, /obj/item/lighter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "t" = ( /obj/structure/table/reinforced, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "u" = ( /obj/structure/table/reinforced, @@ -121,20 +121,20 @@ pixel_x = -4; pixel_y = 8 }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "v" = ( /obj/machinery/door/window/survival_pod/left/directional/south{ req_one_access = list("bar","mining") }, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "w" = ( /obj/effect/turf_decal/mining/survival{ dir = 4 }, /obj/structure/disposalpipe/segment, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "x" = ( /obj/structure/chair/stool/bar/directional/south, @@ -161,7 +161,7 @@ /obj/structure/disposalpipe/segment{ dir = 9 }, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "C" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/west, @@ -169,7 +169,7 @@ /obj/structure/window/reinforced/survival_pod/spawner/directional/north, /obj/structure/window/reinforced/survival_pod/spawner/directional/south, /obj/structure/grille, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/misc/survivalpod) "D" = ( /obj/structure/chair/comfy/black, @@ -177,7 +177,7 @@ /area/misc/survivalpod) "E" = ( /obj/machinery/door/airlock/survival_pod, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/misc/survivalpod) "F" = ( /obj/structure/table/wood/fancy, @@ -195,25 +195,25 @@ /obj/structure/toilet{ dir = 4 }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/misc/survivalpod) "H" = ( /obj/structure/sink/directional/east, /obj/structure/mirror/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/misc/survivalpod) "I" = ( /obj/structure/sink/directional/west, /obj/machinery/light/small/directional/east, /obj/structure/urinal/directional/north, /obj/structure/mirror/directional/east, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/misc/survivalpod) "J" = ( /obj/effect/turf_decal/mining/survival{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "K" = ( /obj/structure/chair/comfy/black{ @@ -239,21 +239,21 @@ /obj/item/soap/deluxe, /obj/machinery/light/small/directional/east, /obj/machinery/shower/directional/south, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/misc/survivalpod) "O" = ( /obj/effect/turf_decal/mining/survival{ dir = 4 }, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "P" = ( /obj/effect/turf_decal/mining/survival, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "Q" = ( /obj/effect/turf_decal/mining/survival, -/turf/closed/wall/mineral/titanium/survival/nodiagonal, +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal, /area/misc/survivalpod) "R" = ( /obj/machinery/door/airlock/survival_pod/glass, diff --git a/_maps/templates/shelter_t.dmm b/_maps/templates/shelter_t.dmm index 1e8ff8bc590f..393db9ccb01f 100644 --- a/_maps/templates/shelter_t.dmm +++ b/_maps/templates/shelter_t.dmm @@ -1,10 +1,10 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "e" = ( /obj/structure/sign/warning/biohazard, -/turf/closed/wall/mineral/titanium/survival/pod, +/turf/closed/wall/mineral/plastitanium/survival/pod, /area/misc/survivalpod) "C" = ( /obj/machinery/door/airlock/survival_pod/glass, @@ -12,7 +12,7 @@ id = "podtoilet"; name = "Privacy Shutters" }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) "H" = ( /obj/structure/toilet, @@ -26,7 +26,7 @@ name = "Privacy Shutters"; pixel_y = 6 }, -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/misc/survivalpod) (1,1,1) = {" diff --git a/_maps/virtual_domains/beach_bar.dmm b/_maps/virtual_domains/beach_bar.dmm index 8a17361fcac0..387b651040b3 100644 --- a/_maps/virtual_domains/beach_bar.dmm +++ b/_maps/virtual_domains/beach_bar.dmm @@ -22,7 +22,7 @@ /turf/open/floor/wood, /area/virtual_domain/fullbright) "aE" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "aZ" = ( /obj/machinery/light/small/directional/east, @@ -137,7 +137,7 @@ "fr" = ( /obj/item/melee/skateboard/hoverboard, /obj/machinery/light/directional/west, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "gh" = ( /obj/structure/flora/bush/stalky/style_random, @@ -157,7 +157,7 @@ /area/virtual_domain/fullbright) "hk" = ( /obj/structure/reagent_dispensers/watertank, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "hE" = ( /obj/structure/sign/departments/restroom/directional/east, @@ -193,7 +193,7 @@ density = 1 }, /obj/effect/decal/fakelattice, -/turf/open/floor/pod/light{ +/turf/open/floor/mineral/plastitanium/pod/light{ density = 1 }, /area/virtual_domain/fullbright) @@ -228,7 +228,7 @@ /area/virtual_domain/fullbright) "lq" = ( /obj/item/melee/skateboard/hoverboard, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "lB" = ( /obj/item/toy/seashell, @@ -471,7 +471,7 @@ /turf/open/floor/wood, /area/virtual_domain/fullbright) "xw" = ( -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/fullbright) "xJ" = ( /obj/structure/closet/cabinet, @@ -553,7 +553,7 @@ /area/virtual_domain/fullbright) "zw" = ( /obj/structure/punching_bag, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/fullbright) "zI" = ( /obj/structure/marker_beacon/indigo, @@ -589,7 +589,7 @@ "AI" = ( /obj/structure/table/reinforced, /obj/machinery/reagentgrinder, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "AP" = ( /obj/machinery/chem_dispenser/drinks/beer/fullupgrade{ @@ -633,7 +633,7 @@ /area/virtual_domain/fullbright) "BQ" = ( /obj/machinery/seed_extractor, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "Cb" = ( /obj/machinery/light/directional/north, @@ -714,7 +714,7 @@ /obj/structure/table/reinforced, /obj/item/secateurs, /obj/item/reagent_containers/cup/bottle/nutrient/ez, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "FS" = ( /obj/effect/turf_decal/sand, @@ -894,7 +894,7 @@ desc = "A sink used for washing one's hands and face. It looks rusty and home-made"; name = "old sink" }, -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/fullbright) "OZ" = ( /obj/structure/marker_beacon/teal, @@ -994,7 +994,7 @@ /area/virtual_domain/fullbright) "Uq" = ( /obj/structure/weightmachine/weightlifter, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/fullbright) "UU" = ( /obj/structure/flora/bush/large/style_random, diff --git a/_maps/virtual_domains/island_brawl.dmm b/_maps/virtual_domains/island_brawl.dmm index 009ccc6c76b9..0d1ae401994a 100644 --- a/_maps/virtual_domains/island_brawl.dmm +++ b/_maps/virtual_domains/island_brawl.dmm @@ -518,7 +518,7 @@ /turf/open/misc/beach/sand, /area/virtual_domain/fullbright) "fI" = ( -/turf/open/floor/pod, +/turf/open/floor/mineral/plastitanium/pod, /area/virtual_domain/protected_space/fullbright) "fJ" = ( /obj/effect/turf_decal/siding/wood{ @@ -587,7 +587,7 @@ density = 1 }, /obj/effect/decal/fakelattice, -/turf/open/floor/pod/light{ +/turf/open/floor/mineral/plastitanium/pod/light{ density = 1 }, /area/virtual_domain/fullbright) @@ -2342,7 +2342,7 @@ /turf/open/floor/plating, /area/virtual_domain) "DK" = ( -/turf/open/floor/pod/light, +/turf/open/floor/mineral/plastitanium/pod/light, /area/virtual_domain/protected_space/fullbright) "DL" = ( /obj/structure/table, @@ -2582,7 +2582,7 @@ /area/virtual_domain) "GD" = ( /obj/effect/landmark/bitrunning/permanent_exit, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain/protected_space/fullbright) "GI" = ( /turf/open/floor/iron/dark/textured_large, diff --git a/_maps/virtual_domains/meta_central.dmm b/_maps/virtual_domains/meta_central.dmm index 003785055e6d..45dc86b781b7 100644 --- a/_maps/virtual_domains/meta_central.dmm +++ b/_maps/virtual_domains/meta_central.dmm @@ -212,15 +212,6 @@ }, /turf/open/floor/iron, /area/virtual_domain) -"bQ" = ( -/obj/effect/turf_decal/tile/neutral, -/obj/structure/railing{ - dir = 4 - }, -/obj/structure/table/reinforced/plastitaniumglass, -/obj/item/ai_module/malf, -/turf/open/floor/iron, -/area/virtual_domain) "bR" = ( /obj/structure/chair{ dir = 1 @@ -806,6 +797,14 @@ "gE" = ( /turf/open/floor/wood, /area/virtual_domain) +"gF" = ( +/obj/structure/extinguisher_cabinet/directional/west, +/obj/structure/table/glass, +/obj/item/stack/medical/wrap/gauze, +/obj/item/stack/medical/mesh, +/obj/item/stack/medical/suture, +/turf/open/floor/plating, +/area/virtual_domain) "gM" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -3007,14 +3006,6 @@ }, /turf/open/floor/iron/dark, /area/virtual_domain) -"zg" = ( -/obj/structure/extinguisher_cabinet/directional/west, -/obj/structure/table/glass, -/obj/item/stack/medical/wrap/gauze, -/obj/item/stack/medical/mesh, -/obj/item/stack/medical/suture, -/turf/open/floor/plating, -/area/virtual_domain) "zj" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -3123,6 +3114,15 @@ }, /turf/open/floor/plating, /area/virtual_domain) +"Ag" = ( +/obj/effect/turf_decal/tile/neutral, +/obj/structure/railing{ + dir = 4 + }, +/obj/structure/table/reinforced/plastitaniumglass, +/obj/item/ai_module/law/malf, +/turf/open/floor/iron, +/area/virtual_domain) "Ah" = ( /obj/effect/turf_decal/tile/neutral{ dir = 8 @@ -7010,7 +7010,7 @@ HM ps hV Hd -bQ +Ag hZ ii SN @@ -7701,7 +7701,7 @@ ul wa tM Nh -zg +gF Fj NO RG diff --git a/_maps/virtual_domains/syndicate_assault.dmm b/_maps/virtual_domains/syndicate_assault.dmm index e20438e1c412..7abf85e73501 100644 --- a/_maps/virtual_domains/syndicate_assault.dmm +++ b/_maps/virtual_domains/syndicate_assault.dmm @@ -12,21 +12,6 @@ /obj/effect/landmark/bitrunning/mob_segment, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) -"aN" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/iron/fifty, -/obj/item/stack/sheet/plasteel/twenty, -/obj/item/stack/sheet/mineral/plastitanium{ - amount = 50 - }, -/obj/item/stack/sheet/glass/fifty, -/obj/item/stack/rods/fifty, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "aO" = ( /obj/machinery/recharge_station, /turf/open/floor/mineral/plastitanium, @@ -34,6 +19,11 @@ "bh" = ( /turf/open/floor/carpet/royalblack, /area/virtual_domain) +"bt" = ( +/obj/item/ai_module/law/core/full/cybersun, +/obj/structure/table/reinforced, +/turf/open/floor/mineral/plastitanium/red, +/area/virtual_domain) "bD" = ( /obj/structure/table/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -42,9 +32,6 @@ }, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) -"bG" = ( -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "cc" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -137,6 +124,14 @@ "dp" = ( /turf/open/floor/mineral/plastitanium, /area/virtual_domain/protected_space) +"dq" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/melee/energy/sword/saber/red, +/obj/machinery/light/small/directional/north, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "dw" = ( /obj/machinery/light/small/directional/south, /turf/open/floor/mineral/plastitanium, @@ -223,14 +218,6 @@ /obj/machinery/light/directional/north, /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) -"iU" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/melee/energy/sword/saber/red, -/obj/machinery/light/small/directional/north, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "iW" = ( /obj/structure/table/reinforced, /obj/machinery/button/door{ @@ -334,6 +321,21 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) +"lQ" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/iron/fifty, +/obj/item/stack/sheet/plasteel/twenty, +/obj/item/stack/sheet/mineral/plastitanium{ + amount = 50 + }, +/obj/item/stack/sheet/glass/fifty, +/obj/item/stack/rods/fifty, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "mo" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/layer2{ dir = 8 @@ -502,18 +504,6 @@ /obj/item/gun/ballistic/automatic/pistol, /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) -"sK" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/mineral/titanium{ - amount = 40 - }, -/obj/item/stack/sheet/mineral/uranium{ - amount = 15 - }, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "sL" = ( /obj/structure/chair/comfy, /turf/open/floor/mineral/plastitanium, @@ -539,6 +529,18 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) +"uy" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/mineral/titanium{ + amount = 40 + }, +/obj/item/stack/sheet/mineral/uranium{ + amount = 15 + }, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "uP" = ( /turf/open/floor/mineral/plastitanium, /area/virtual_domain) @@ -586,6 +588,26 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/mineral/plastitanium, /area/virtual_domain/protected_space) +"wX" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/stack/sheet/mineral/gold{ + amount = 30 + }, +/obj/item/stack/sheet/mineral/silver{ + amount = 30 + }, +/obj/machinery/light/small/directional/south, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) +"xj" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/disk/surgery/forgottenship, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "xJ" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -628,11 +650,6 @@ }, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) -"yT" = ( -/obj/item/ai_module/core/full/cybersun, -/obj/structure/table/reinforced, -/turf/open/floor/mineral/plastitanium/red, -/area/virtual_domain) "yV" = ( /obj/structure/table/reinforced, /obj/item/assembly/prox_sensor, @@ -899,6 +916,14 @@ /obj/structure/sink/directional/south, /turf/open/floor/iron/dark, /area/virtual_domain) +"JU" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/obj/item/clothing/head/helmet/space/syndicate/black/engie, +/obj/item/clothing/suit/space/syndicate/black/engie, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "Kz" = ( /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) @@ -914,6 +939,9 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) +"Lx" = ( +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "LB" = ( /obj/structure/cable, /turf/open/floor/mineral/plastitanium, @@ -958,36 +986,22 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/mineral/plastitanium/red, /area/virtual_domain) -"Nm" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) -"Nr" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/stack/sheet/mineral/gold{ - amount = 30 - }, -/obj/item/stack/sheet/mineral/silver{ - amount = 30 - }, -/obj/machinery/light/small/directional/south, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) -"Of" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/disk/surgery/forgottenship, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "Ox" = ( /obj/machinery/atmospherics/components/unary/vent_pump, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) +"OB" = ( +/obj/machinery/door/password/voice/sfc{ + password = null + }, +/obj/structure/fans/tiny, +/obj/machinery/door/airlock/grunge{ + desc = "Vault airlock preventing air from going out."; + name = "Syndicate Vault Airlock" + }, +/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "OH" = ( /obj/structure/cable, /obj/structure/table/reinforced, @@ -1006,18 +1020,6 @@ /obj/structure/sign/warning/vacuum/external/directional/south, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) -"PR" = ( -/obj/machinery/door/password/voice/sfc{ - password = null - }, -/obj/structure/fans/tiny, -/obj/machinery/door/airlock/grunge{ - desc = "Vault airlock preventing air from going out."; - name = "Syndicate Vault Airlock" - }, -/obj/effect/mapping_helpers/airlock/access/all/syndicate/general, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "Qg" = ( /obj/machinery/suit_storage_unit/syndicate{ helmet_type = /obj/item/clothing/head/helmet/space/syndicate/black; @@ -1109,6 +1111,12 @@ "TB" = ( /turf/closed/indestructible/syndicate, /area/virtual_domain/protected_space) +"TM" = ( +/obj/structure/closet/crate/secure/gear{ + req_access = list("syndicate") + }, +/turf/open/floor/mineral/plastitanium/pod/dark, +/area/virtual_domain/protected_space) "UQ" = ( /obj/structure/sign/poster/contraband/syndicate_recruitment, /turf/closed/wall/r_wall/plastitanium/syndicate, @@ -1158,14 +1166,6 @@ /obj/machinery/light/directional/north, /turf/open/floor/mineral/plastitanium, /area/virtual_domain) -"Yb" = ( -/obj/structure/closet/crate/secure/gear{ - req_access = list("syndicate") - }, -/obj/item/clothing/head/helmet/space/syndicate/black/engie, -/obj/item/clothing/suit/space/syndicate/black/engie, -/turf/open/floor/pod/dark, -/area/virtual_domain/protected_space) "Yi" = ( /obj/effect/landmark/bitrunning/cache_spawn, /turf/open/floor/plastic, @@ -2356,8 +2356,8 @@ nB nB TB TB -Yb -Yb +JU +JU TB Bm nB @@ -2389,7 +2389,7 @@ qx qx qx ru -yT +bt Kz ru Yu @@ -2413,10 +2413,10 @@ nB QG nB TB -aN -bG -bG -sK +lQ +Lx +Lx +uy TB nB qx @@ -2471,10 +2471,10 @@ nB we nB TB -iU -bG -bG -Nr +dq +Lx +Lx +wX TB nB we @@ -2529,10 +2529,10 @@ qx nB nB TB -Nm -bG -bG -Of +TM +Lx +Lx +xj TB nB nB @@ -2588,7 +2588,7 @@ nB nB TB TB -PR +OB TB TB TB diff --git a/_maps/virtual_domains/vaporwave.dmm b/_maps/virtual_domains/vaporwave.dmm index 569a65a812ba..26cb10748f95 100644 --- a/_maps/virtual_domains/vaporwave.dmm +++ b/_maps/virtual_domains/vaporwave.dmm @@ -248,7 +248,7 @@ "XJ" = ( /obj/structure/fans/tiny, /obj/machinery/door/airlock/hatch, -/turf/open/floor/pod/dark, +/turf/open/floor/mineral/plastitanium/pod/dark, /area/virtual_domain) "Yo" = ( /obj/structure/statue/sandstone/venus{ diff --git a/_maps/westfieldmall.json b/_maps/westfieldmall.json index 69d78a767557..25f3df954b81 100644 --- a/_maps/westfieldmall.json +++ b/_maps/westfieldmall.json @@ -69,6 +69,10 @@ "spawn_positions": 3, "total_positions": 3 }, + "I Nonni": { + "spawn_positions": 1, + "total_positions": 1 + }, "La Squadra": { "spawn_positions": 5, "total_positions": 5 diff --git a/build/behavior_trees/code/datums/ai/babies/make_babies.bt.compiled.json b/build/behavior_trees/code/datums/ai/babies/make_babies.bt.compiled.json new file mode 100644 index 000000000000..d4788d520ca8 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/babies/make_babies.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_babies_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/make_babies","target_key":"BB_babies_target","child_types_key":"BB_babies_child"}]}],"observer_abort":3,"key":"BB_babies_target"} diff --git a/build/behavior_trees/code/datums/ai/bane/bane.bt.compiled.json b/build/behavior_trees/code/datums/ai/bane/bane.bt.compiled.json new file mode 100644 index 000000000000..4984062567c1 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/bane/bane.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_bane_batman","required_dist":1},{"type":"/datum/bt_node/ai_behavior/break_spine/bane","target_key":"BB_bane_batman"}]}],"key":"BB_bane_batman"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_bane_batman","target_source":"/datum/target_source/oview_single_type/living_mob","targeting_strategy":"/datum/targeting_strategy/conscious_mob","vision_range":7}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.compiled.json new file mode 100644 index 000000000000..13ab7c355003 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_in_vent","children":[{"type":"/datum/bt_node/ai_behavior/exit_vent"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_entry_vent_target"},{"type":"/datum/bt_node/ai_behavior/enter_vent"}]}],"cooldown_key":"Venting Cooldown","cooldown_duration":"BB_ventcrawl_cooldown"}],"observer_abort":3,"key":"BB_entry_vent_target"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.compiled.json new file mode 100644 index 000000000000..43697a9ca3a2 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/buckle_target_dangerous","children":[{"type":"/datum/bt_node/ai_behavior/break_out_of_object/from_bb","target_key":"BB_basic_mob_escape_target"}]},{"type":"/datum/bt_node/ai_behavior/resist"}]}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_contained_in_obj","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/container_attackable","children":[{"type":"/datum/bt_node/ai_behavior/break_out_of_object/from_bb","target_key":"BB_basic_mob_escape_target"}]},{"type":"/datum/bt_node/ai_behavior/resist"}]}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.compiled.json new file mode 100644 index 000000000000..c5f1fb7297a8 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_contained_in_obj","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.compiled.json new file mode 100644 index 000000000000..d0785f788d0e --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/write_on_paper","paper_key":"BB_SIMPLE_CARRY_ITEM","writing_list_key":"BB_writing_list"}],"observer_abort":1,"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","time_between_perform":4.0,"target_key":"BB_found_paper","can_attack_turfs":true,"can_attack_dense_objects":true},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_paper","required_dist":0}]},{"type":"/datum/bt_node/ai_behavior/pick_up_item_virtual","target_key":"BB_found_paper","storage_key":"BB_SIMPLE_CARRY_ITEM"}]}],"success_policy":1,"failure_policy":1}],"observer_abort":2,"key":"BB_found_paper"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.compiled.json new file mode 100644 index 000000000000..8f225ec19aae --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"swim_alternate_turf","required_dist":0,"finish_on_arrival":true}],"observer_abort":3,"key":"swim_alternate_turf"},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"key_swimmer_cooldown","cooldown_duration":300},{"type":"/datum/bt_node/ai_behavior/wait","duration":300},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"swim_alternate_turf","targeting_strategy":"/datum/targeting_strategy/walkable_turf","target_source":"/datum/target_source/oview_land_turfs"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"swim_alternate_turf","required_dist":0,"finish_on_arrival":true}]},{"type":"/datum/bt_node/ai_behavior/swim_splash"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.compiled.json new file mode 100644 index 000000000000..54f984a39302 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/subplan","children":[{"type":"$bqdqne64"}],"__bindings":{"bqdqne64":{"label":"speech_behavior","default":"/datum/bt_node/ai_behavior/random_speech_blackboard"}},"success_policy":1,"failure_policy":1,"loop_delay":10} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.compiled.json new file mode 100644 index 000000000000..50b8c06f73fb --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/find_flee_location","target_key":"$byk9gqj4","hiding_location_key":"Current Target Hiding Location","destination_key":"BB_flee_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_flee_location","required_dist":0,"finish_on_arrival":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_basic_stop_fleeing","invert":true,"observer_abort":3,"__bindings":{"byk9gqj4":{"label":"target_key","default":"Current Target"}}} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.compiled.json new file mode 100644 index 000000000000..84bba04be6c1 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/find_flee_location","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location","destination_key":"BB_flee_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_flee_location","required_dist":0,"finish_on_arrival":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_basic_stop_fleeing","invert":true,"observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.compiled.json new file mode 100644 index 000000000000..2b42b931e7b0 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"target_key":"Current Target","maximum_distance":9},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.compiled.json new file mode 100644 index 000000000000..421d1326ffb9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/tipped_reaction"}],"key":"BB_basic_tip_reacting","observer_abort":2} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.compiled.json new file mode 100644 index 000000000000..4e3709d18c0d --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.compiled.json new file mode 100644 index 000000000000..6b9268de1e27 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack/dog","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.compiled.json new file mode 100644 index 000000000000..fcc3c7492416 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/minebot","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.compiled.json new file mode 100644 index 000000000000..3e8b1d36b36e --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.compiled.json new file mode 100644 index 000000000000..114dd3ec43f9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_home","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/enter_exit_hive"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.compiled.json new file mode 100644 index 000000000000..fdd18ae54188 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_in_typelist","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/keys_different_gender","children":[{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"Seems confused"}],"invert":true,"key_a":"Literally me","key_b":"BB_current_pet_target"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_current_pet_target","combat_mode":false}]},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]}],"key":"BB_current_pet_target","typelist_key":"BB_babies_partner"}],"key":"BB_current_pet_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.compiled.json new file mode 100644 index 000000000000..7c7d8905d139 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/forget_failed_fetches"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/fetch_seek","target_key":"BB_current_pet_target"},{"type":"/datum/bt_node/ai_behavior/pick_up_item_virtual","target_key":"BB_current_pet_target","storage_key":"BB_SIMPLE_CARRY_ITEM"}]}],"key":"BB_current_pet_target","observer_abort":3}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_FETCH_DELIVER_TO","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/pass_item_virtual","delivery_key":"BB_FETCH_DELIVER_TO","storage_key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]}],"key":"BB_FETCH_DELIVER_TO","observer_abort":3}],"key":"BB_SIMPLE_CARRY_ITEM","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/wait"}],"observer_abort":3,"invert":true,"key":"BB_current_pet_target"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.compiled.json new file mode 100644 index 000000000000..7483308b6652 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_current_pet_target","combat_mode":false}]}],"success_policy":1,"failure_policy":0}],"key":"BB_current_pet_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.compiled.json new file mode 100644 index 000000000000..7c56af271fcc --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":true}],"success_policy":1,"failure_policy":0}],"key":"BB_current_pet_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.compiled.json new file mode 100644 index 000000000000..24ce4902b7b4 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/mine_wall","target_key":"BB_current_pet_target"}]}],"key":"BB_current_pet_target","observer_abort":3},{"type":"/datum/bt_node/ai_behavior/find_mineral_wall","target_key":"BB_current_pet_target"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.compiled.json new file mode 100644 index 000000000000..0099a4de932e --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":0,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]}],"key":"BB_current_pet_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.compiled.json new file mode 100644 index 000000000000..169d6c0c5df9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/play_dead"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.compiled.json new file mode 100644 index 000000000000..97379ae486df --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/protect_owner_check"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.compiled.json new file mode 100644 index 000000000000..74c77d8f597c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/protect_owner_check"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.compiled.json new file mode 100644 index 000000000000..bcea164844b4 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_current_pet_target"}},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.compiled.json new file mode 100644 index 000000000000..0f54e4df546f --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/ai_behavior/wait","duration":0} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.compiled.json new file mode 100644 index 000000000000..e961a273341d --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"BB_current_pet_target","targeting_strategy":"BB_pet_targeting","hiding_location_key":"BB_pet_attack_hiding_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/swirl_around_target"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_swirl_turf","required_dist":0,"finish_on_arrival":true}]}]}],"success_policy":1,"failure_policy":1}],"key":"BB_swarm_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.compiled.json new file mode 100644 index 000000000000..832baf026542 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"BB_current_pet_target"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.compiled.json new file mode 100644 index 000000000000..53f2f98fbcef --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_pet_active_ability"},{"type":"/datum/bt_node/ai_behavior/clear_pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability.bt.compiled.json new file mode 100644 index 000000000000..cc9a591751e9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ability_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_combat.bt.compiled.json new file mode 100644 index 000000000000..d1bc58148b43 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_targeted_spell","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_secondary_spell","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_blink_spell","target_key":"Current Target"}]}],"observer_abort":2,"cooldown_key":"BB_wizard_spell_cooldown","cooldown_duration":10}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":3,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"invert":false,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee.bt.compiled.json new file mode 100644 index 000000000000..a3512dbdaa41 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ability_melee_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.compiled.json new file mode 100644 index 000000000000..c45a6bf99cc1 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged.bt.compiled.json new file mode 100644 index 000000000000..abae9529636f --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ability_ranged_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.compiled.json new file mode 100644 index 000000000000..3d639c95fad2 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.compiled.json new file mode 100644 index 000000000000..44dfc11a2042 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ability_retaliate_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.compiled.json new file mode 100644 index 000000000000..8084930f4828 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":3,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"BB_basic_mob_shitlist"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious.bt.compiled.json new file mode 100644 index 000000000000..b1fed32e89ab --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_capricious_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious_combat.bt.compiled.json new file mode 100644 index 000000000000..e6a1c4c95d0c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_capricious_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"BB_basic_mob_shitlist"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/capricious_pick_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful.bt.compiled.json new file mode 100644 index 000000000000..fe0b4f4783e2 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/simple_fearful_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful_combat.bt.compiled.json new file mode 100644 index 000000000000..335e6be98891 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_fearful_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/subtree/random_walk"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_goon.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_goon.bt.compiled.json new file mode 100644 index 000000000000..339d5cc36873 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_goon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile.bt.compiled.json new file mode 100644 index 000000000000..c99cae59bca1 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_hostile_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat.bt.compiled.json new file mode 100644 index 000000000000..98eedae80588 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"$bbrbyj7y"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk","bindings":{"bf07i8ep":"$bp3p5vvb"}}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"__bindings":{"bbrbyj7y":{"label":"combat_speech_behavior","default":""},"bp3p5vvb":{"label":"walk_chance","default":25}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.compiled.json new file mode 100644 index 000000000000..74f3e952fc8c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":0,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]},{"type":"$bhvqd9fh"}],"__bindings":{"bhvqd9fh":{"label":"speech loop subtree","default":"/datum/bt_node/subtree"}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.compiled.json new file mode 100644 index 000000000000..70dffa4e2a65 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/subtree/pick_retaliate_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.compiled.json new file mode 100644 index 000000000000..ace2dc0e2dff --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":0,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"$b95z0f0c"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"$by235tlw"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"$bqul7l8t"}],"__bindings":{"b95z0f0c":{"label":"while_attacking_subtree","default":""},"by235tlw":{"label":"cant_attack_subtree","default":""},"bqul7l8t":{"label":"speech_subtree","default":"/datum/bt_node/subtree"}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged.bt.compiled.json new file mode 100644 index 000000000000..39ce20f83c62 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ranged_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_combat.bt.compiled.json new file mode 100644 index 000000000000..d5b107b7d303 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","time_between_perform":6.0,"target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target","approach_movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"$bsjjwub2"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"__bindings":{"bsjjwub2":{"label":"idle_behavior","default":"/datum/bt_node/subtree/random_walk"}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.compiled.json new file mode 100644 index 000000000000..ab4d970ead90 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ranged_retaliate_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.compiled.json new file mode 100644 index 000000000000..8b83bd02e122 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate.bt.compiled.json new file mode 100644 index 000000000000..15c3371800ee --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_retaliate_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.compiled.json new file mode 100644 index 000000000000..69e93aa3570b --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":"$b2jvnm5d"}],"__bindings":{"b2jvnm5d":{"label":"check_faction","default":0}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher.bt.compiled.json new file mode 100644 index 000000000000..7d76a8bf6c31 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_skirmisher_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.compiled.json new file mode 100644 index 000000000000..44403a4ba802 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish.bt.compiled.json new file mode 100644 index 000000000000..1232523f9f76 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/simple_skittish_combat"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish_combat.bt.compiled.json new file mode 100644 index 000000000000..a57400770d32 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/simple_skittish_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/subtree/random_walk"}]} diff --git a/build/behavior_trees/code/datums/ai/basic_mobs/talk.bt.compiled.json b/build/behavior_trees/code/datums/ai/basic_mobs/talk.bt.compiled.json new file mode 100644 index 000000000000..7f8b669005c7 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/basic_mobs/talk.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/random_speech_loop"} diff --git a/build/behavior_trees/code/datums/ai/bots/bot_patrol.bt.compiled.json b/build/behavior_trees/code/datums/ai/bots/bot_patrol.bt.compiled.json new file mode 100644 index 000000000000..000666dd21ea --- /dev/null +++ b/build/behavior_trees/code/datums/ai/bots/bot_patrol.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/bot_mode_flag","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_next_beacon_target","target_key":"beacon_target"}],"key":"previous_beacon_target"},{"type":"/datum/bt_node/ai_behavior/find_first_beacon_target","target_key":"beacon_target"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"beacon_target","required_dist":0,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/arrive_at_beacon","target_key":"beacon_target"}]}],"key":"beacon_target"}]}],"observer_abort":2,"flag":2}],"observer_abort":2,"cooldown_key":"bot_beacon_cooldown"},{"type":"/datum/bt_node/ai_behavior/wait","duration":50}]} diff --git a/build/behavior_trees/code/datums/ai/bots/bot_respond_to_summon.bt.compiled.json b/build/behavior_trees/code/datums/ai/bots/bot_respond_to_summon.bt.compiled.json new file mode 100644 index 000000000000..7c68c683504c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/bots/bot_respond_to_summon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"bot_summon_target","required_dist":0,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/complete_summon_travel","target_key":"bot_summon_target"}]}],"key":"bot_summon_target"} diff --git a/build/behavior_trees/code/datums/ai/bots/bot_salute_authority.bt.compiled.json b/build/behavior_trees/code/datums/ai/bots/bot_salute_authority.bt.compiled.json new file mode 100644 index 000000000000..d2af26740eb9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/bots/bot_salute_authority.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_valid_authority","target_key":"salute_target"},{"type":"/datum/bt_node/ai_behavior/salute_authority","target_key":"salute_target","salute_keys":"salute_messages"}]}],"cooldown_key":"salute_cooldown","cooldown_duration":600} diff --git a/build/behavior_trees/code/datums/ai/cursed/cursed.bt.compiled.json b/build/behavior_trees/code/datums/ai/cursed/cursed.bt.compiled.json new file mode 100644 index 000000000000..c5851cf082c8 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/cursed/cursed.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/throw_attack/haunted","target_key":"BB_haunt_target","throw_count_key":"BB_haunted_throw_attempt_count","haunt_list_key":"BB_to_haunt_list"}],"success_policy":1,"failure_policy":0,"loop_delay":20},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_haunt_target","required_dist":3,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"BB_haunt_target"},{"type":"/datum/bt_node/ai_behavior/idle_ghost_item"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_haunt_target","target_source":"/datum/target_source/oview_single_type/carbon_mob","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/dog/dog.bt.compiled.json b/build/behavior_trees/code/datums/ai/dog/dog.bt.compiled.json new file mode 100644 index 000000000000..fc346204445e --- /dev/null +++ b/build/behavior_trees/code/datums/ai/dog/dog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/dog_harassment"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_dog"}],"success_policy":1,"failure_policy":0},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/datums/ai/dog/dog_corgi.bt.compiled.json b/build/behavior_trees/code/datums/ai/dog/dog_corgi.bt.compiled.json new file mode 100644 index 000000000000..475f8dcb8367 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/dog/dog_corgi.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/dog_harassment"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_dog"}],"success_policy":1,"failure_policy":0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_partner"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/datums/ai/dog/dog_harassment.bt.compiled.json b/build/behavior_trees/code/datums/ai/dog/dog_harassment.bt.compiled.json new file mode 100644 index 000000000000..00d36999427f --- /dev/null +++ b/build/behavior_trees/code/datums/ai/dog/dog_harassment.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_hated_dog_target","target_key":"BB_DOG_HARASS_TARGET","targeting_strategy":"BB_pet_targeting"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_DOG_HARASS_TARGET","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack/dog","target_key":"BB_DOG_HARASS_TARGET","targeting_strategy":"BB_pet_targeting","hiding_location_key":"Current Target Hiding Location"}]}],"chance":0.1} diff --git a/build/behavior_trees/code/datums/ai/generic_hunger.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_hunger.bt.compiled.json new file mode 100644 index 000000000000..06cf4dc331d6 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_hunger.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/consume","target_key":"bb_food_target","hunger_timer_key":"BB_NEXT_HUNGRY"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"bb_food_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/pick_up","target_key":"bb_food_target","drop_held":true}]}]}],"key":"bb_food_target"} diff --git a/build/behavior_trees/code/datums/ai/generic_play_instrument.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_play_instrument.bt.compiled.json new file mode 100644 index 000000000000..c3b4514f642d --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_play_instrument.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/is_holding_target","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_SONG_INSTRUMENT"},{"type":"/datum/bt_node/ai_behavior/pick_up","target_key":"BB_SONG_INSTRUMENT"}]}],"observer_abort":2,"invert":true,"key":"BB_SONG_INSTRUMENT"}],"invert":true,"key":"Has its instrument up its ass"},{"type":"/datum/bt_node/ai_behavior/keep_playing_instrument","song_instrument_key":"BB_SONG_INSTRUMENT"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/setup_instrument","song_instrument_key":"BB_SONG_INSTRUMENT","song_lines_key":"song_lines"},{"type":"/datum/bt_node/ai_behavior/play_instrument","song_instrument_key":"BB_SONG_INSTRUMENT","volume":"$bulu13xf"}]}]}],"key":"BB_SONG_INSTRUMENT","__bindings":{"bulu13xf":{"label":"volume","default":50}}} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/basic_find_target.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/basic_find_target.bt.compiled.json new file mode 100644 index 000000000000..c0c7d81a31e7 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/basic_find_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/capricious_pick_target.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/capricious_pick_target.bt.compiled.json new file mode 100644 index 000000000000..700ad03cb5d2 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/capricious_pick_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/capricious_retaliate","targeting_strategy":"targeting_strategy","ignore_faction":true},{"type":"/datum/bt_node/subtree/pick_retaliate_target"}]} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/climb_tree.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/climb_tree.bt.compiled.json new file mode 100644 index 000000000000..8fc223ff1b82 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/climb_tree.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_climbed_tree"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_climbed_tree"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_climbed_tree"}]}],"observer_abort":3,"key":"BB_climbed_tree"}],"observer_abort":2,"cooldown_key":"Tree Climbing Cooldown","cooldown_duration":"$bb0lcfol","__bindings":{"bb0lcfol":{"label":"tree_climbing_cooldown","default":300}}} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/find_food.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/find_food.bt.compiled.json new file mode 100644 index 000000000000..ab6b56f72f5f --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/find_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"BB_TARGET_FOOD","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_typed/from_bb_key/basic_foods","revalidation_mode":1}],"observer_abort":1,"cooldown_key":"BB_next_food_eat"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/find_partner.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/find_partner.bt.compiled.json new file mode 100644 index 000000000000..1edff728d4e1 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/find_partner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/find_partner","target_key":"BB_babies_target","partner_types_key":"BB_babies_partner","child_types_key":"BB_babies_child"}],"cooldown_key":"BB_partner_search_timeout"}],"key":"BB_breed_ready"}],"observer_abort":2,"key":"can we fuck?"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/find_stealable_object.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/find_stealable_object.bt.compiled.json new file mode 100644 index 000000000000..d19c824e1cb9 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/find_stealable_object.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_dragging","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/random_chance_from_key","children":[{"type":"/datum/bt_node/ai_behavior/stop_dragging"}],"chance_key":"guilty_concious_rate"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}]},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/random_chance_from_key","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_WANTS_TO_COMMIT_THEFT","value":true}],"chance_key":"steal_chance"}],"invert":true,"key":"BB_WANTS_TO_COMMIT_THEFT"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/succeed"}],"invert":true,"key":"BB_WANTS_TO_COMMIT_THEFT"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"item_to_steal","targeting_strategy":"/datum/targeting_strategy/pickup_item/stealable_item","target_source":"/datum/target_source/oview","must_be_reachable":true},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_WANTS_TO_COMMIT_THEFT","value":false}]}]} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.compiled.json new file mode 100644 index 000000000000..2b9df392144c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_basic_mob_shitlist","observer_abort":1},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/random_walk"}],"key":"BB_disable_idle","invert":true}]},{"type":"$z4n9bk7p"},{"type":"$q8w3rtv1"}],"__bindings":{"q8w3rtv1":{"label":"Food Finder","default":"/datum/bt_node/subtree/find_food"},"z4n9bk7p":{"label":"Retaliate Manager","default":"/datum/bt_node/subtree/capricious_pick_target"}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_eat.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_eat.bt.compiled.json new file mode 100644 index 000000000000..1eb1dcf2626c --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_eat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_TARGET_FOOD","required_dist":1},{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"eats up happily!"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_TARGET_FOOD"}]}],"observer_abort":3,"key":"BB_TARGET_FOOD"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.compiled.json new file mode 100644 index 000000000000..b1a430d4014d --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"$b3y599q4","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"$brrasnah","always_reset_target":"$b3cnse9r","hunt_cooldown":"$bqwjf4id","cooldown_key":"$bm3y5m55","behavior_combat_mode":"$bd1towgc"}]}],"observer_abort":3,"key":"$bvtz06kb","__bindings":{"bvtz06kb":{"label":"hunting_target","default":"BB_low_priority_hunting_target"},"b3y599q4":{"label":"hunting_target","default":"BB_low_priority_hunting_target"},"brrasnah":{"label":"hunting_target","default":"BB_low_priority_hunting_target"},"bqwjf4id":{"label":"hunt_cooldown","default":50},"bm3y5m55":{"label":"cooldown_key","default":""},"bd1towgc":{"label":"behavior_combat_mode","default":1},"b3cnse9r":{"label":"always_reset_target","default":1}}} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_reinforce.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_reinforce.bt.compiled.json new file mode 100644 index 000000000000..486a465bbc03 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/move_to_reinforce.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_basic_mob_reinforcement_target","required_dist":0,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_basic_mob_reinforcement_target"}]}],"observer_abort":2,"key":"BB_basic_mob_reinforcement_target"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.compiled.json new file mode 100644 index 000000000000..ad3e6b31cb16 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/random_walk.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/random_walk.bt.compiled.json new file mode 100644 index 000000000000..4e754abf9f0a --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/random_walk.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk","walk_chance":"$bf07i8ep"}],"__bindings":{"bf07i8ep":{"label":"walk_chance","default":25}},"success_policy":1,"failure_policy":0,"loop_delay":15.0} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/skittish_and_speak.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/skittish_and_speak.bt.compiled.json new file mode 100644 index 000000000000..908394cb4b42 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/skittish_and_speak.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/subtree/simple_skittish_combat"}],"success_policy":1,"failure_policy":1},{"type":"$f17iiafz"}],"__bindings":{"f17iiafz":{"label":"Speech Behavior","default":"/datum/bt_node/ai_behavior/random_speech_blackboard"}},"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/generic_subtrees/steal_and_flee.bt.compiled.json b/build/behavior_trees/code/datums/ai/generic_subtrees/steal_and_flee.bt.compiled.json new file mode 100644 index 000000000000..65ded70c2dee --- /dev/null +++ b/build/behavior_trees/code/datums/ai/generic_subtrees/steal_and_flee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"item_to_steal"},{"type":"/datum/bt_node/ai_behavior/drag_target","target_key":"item_to_steal"},{"type":"/datum/bt_node/ai_behavior/copy_bb_key","dest_key":"BB_LAST_STOLEN_ITEM","source_key":"item_to_steal"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"item_to_steal"}]}],"observer_abort":3,"key":"item_to_steal"} diff --git a/build/behavior_trees/code/datums/ai/hauntium/haunted.bt.compiled.json b/build/behavior_trees/code/datums/ai/hauntium/haunted.bt.compiled.json new file mode 100644 index 000000000000..c661f45cbaa2 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/hauntium/haunted.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/item_being_held","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/wait","duration":0}],"key":"BB_likes_equipper","observer_abort":1},{"type":"/datum/bt_node/ai_behavior/item_escape_grasp"}]}],"observer_abort":3},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/throw_attack/haunted","target_key":"BB_haunt_target","throw_count_key":"BB_haunted_throw_attempt_count","haunt_list_key":"BB_to_haunt_list"}],"success_policy":1,"failure_policy":0,"loop_delay":20},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_haunt_target","required_dist":3,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"BB_haunt_target"},{"type":"/datum/bt_node/ai_behavior/idle_ghost_item"}]},{"type":"/datum/bt_node/ai_behavior/pick_haunt_target","target_key":"BB_haunt_target","haunt_list_key":"BB_to_haunt_list"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/datums/ai/monkey/monkey.bt.compiled.json b/build/behavior_trees/code/datums/ai/monkey/monkey.bt.compiled.json new file mode 100644 index 000000000000..8f042ed6096f --- /dev/null +++ b/build/behavior_trees/code/datums/ai/monkey/monkey.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/monkey_combat","override_id":"monkey_combat"},{"type":"/datum/bt_node/subtree/monkey_serve_food"},{"type":"/datum/bt_node/subtree/generic_hunger"},{"type":"/datum/bt_node/subtree/generic_play_instrument"},{"type":"/datum/bt_node/subtree/monkey_shenanigans"},{"type":"/datum/bt_node/ai_behavior/monkey_idle"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/monkey_set_combat_target","attack_target_key":"Current Target","enemies_key":"BB_monkey_enemies"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_current_press_target","targeting_strategy":"/datum/targeting_strategy/anything","target_source":"/datum/target_source/monkey_press_target","vision_range":2}],"key":"Monkeys wants to press something"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Monkeys wants to press something","value":true}],"chance":0.2}],"invert":true,"key":"Monkeys wants to press something"},{"type":"/datum/bt_node/ai_behavior/succeed"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_is_holding_item","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_current_give_target","targeting_strategy":"/datum/targeting_strategy/anything","target_source":"/datum/target_source/oview_single_type/human_mob","vision_range":2}],"key":"Literally me"},{"type":"/datum/bt_node/decorator/target_is_holding_item","children":[{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_monkey_current_give_target"}],"invert":true,"key":"Literally me"},{"type":"/datum/bt_node/ai_behavior/succeed"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_holding_target","children":[{"type":"/datum/bt_node/ai_behavior/succeed"}],"key":"BB_SONG_INSTRUMENT"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_SONG_INSTRUMENT","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/held_items_typed/instrument"}],"key":"BB_monkey_tamed"},{"type":"/datum/bt_node/ai_behavior/succeed"}]},{"type":"/datum/bt_node/decorator/pawn_nutrition_below","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"bb_food_target","targeting_strategy":"/datum/targeting_strategy/pickup_item/food_or_drink","target_source":"/datum/target_source/held_items_then_oview","vision_range":2}],"cooldown_key":"BB_NEXT_HUNGRY"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon","target_key":"BB_monkey_pickuptarget","target_source":"/datum/target_source/monkey_weapon_upgrade","targeting_strategy":"/datum/targeting_strategy/monkey_weapon_upgrade","vision_range":5}],"invert":true,"key":"BB_monkey_tamed"}]}]}],"key":"Current Target","invert":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon","target_key":"BB_monkey_pickuptarget","target_source":"/datum/target_source/monkey_weapon_upgrade","targeting_strategy":"/datum/targeting_strategy/monkey_weapon_upgrade","vision_range":5}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/datums/ai/monkey/monkey_combat.bt.compiled.json b/build/behavior_trees/code/datums/ai/monkey/monkey_combat.bt.compiled.json new file mode 100644 index 000000000000..1e5b39173484 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/monkey/monkey_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}],"observer_abort":2,"invert":true,"target_key":"Current Target","maximum_distance":10,"require_reach":false},{"type":"/datum/bt_node/decorator/pawn_health_below","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"health_threshold":40},{"type":"/datum/bt_node/subtree/monkey_find_weapon"},{"type":"/datum/bt_node/decorator/mob_stat_at_least","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_target_disposal","target_source":"/datum/target_source/oview_single_type/disposal_unit","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":9},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/grab_target","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_target_disposal","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/stuff_in_disposal","attack_target_key":"Current Target","disposal_target_key":"BB_monkey_target_disposal"},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}]},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}]}],"observer_abort":2,"invert":false,"key":"Current Target","min_stat":1},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_can_be_shove_stunned","children":[{"type":"/datum/bt_node/ai_behavior/shove_target","target_key":"Current Target"}],"target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/monkey_attack_mob","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/agile_melee_movement","target_key":"Current Target","standoff_max":4}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/recruit_monkeys","target_key":"Current Target"}],"cooldown_key":"BB_monkey_recruit_cooldown","cooldown_duration":10}],"success_policy":1,"failure_policy":1,"loop_delay":50},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/battle_screech/monkey"}],"chance":0.25}],"cooldown_key":"Battle Screech Cooldown","cooldown_duration":5}],"success_policy":1,"failure_policy":1,"loop_delay":10},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon","time_between_perform":0,"target_key":"BB_monkey_pickuptarget","targeting_strategy":"/datum/targeting_strategy/monkey_weapon_upgrade","target_source":"/datum/target_source/monkey_weapon_upgrade","vision_range":5}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":2.0,"finish_on_primary":true}],"key":"Current Target","observer_abort":2} diff --git a/build/behavior_trees/code/datums/ai/monkey/monkey_find_weapon.bt.compiled.json b/build/behavior_trees/code/datums/ai/monkey/monkey_find_weapon.bt.compiled.json new file mode 100644 index 000000000000..a9d52200befd --- /dev/null +++ b/build/behavior_trees/code/datums/ai/monkey/monkey_find_weapon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_pickuptarget","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/monkey_equip/pickpocket","target_key":"BB_monkey_pickuptarget"}],"key":"BB_monkey_pickup_is_pickpocket"},{"type":"/datum/bt_node/ai_behavior/monkey_equip/ground","target_key":"BB_monkey_pickuptarget"}]}]}],"observer_abort":3,"key":"BB_monkey_pickuptarget"} diff --git a/build/behavior_trees/code/datums/ai/monkey/monkey_serve_food.bt.compiled.json b/build/behavior_trees/code/datums/ai/monkey/monkey_serve_food.bt.compiled.json new file mode 100644 index 000000000000..9fe06ea5404a --- /dev/null +++ b/build/behavior_trees/code/datums/ai/monkey/monkey_serve_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/monkey_find_patrons","patrons_key":"BB_monkey_patrons_nearby","give_target_key":"BB_monkey_current_give_target"}],"cooldown_key":"Monkeys can look for patron","cooldown_duration":10,"lock_on_succeed":false},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_holding_target","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_current_give_target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/give","target_key":"BB_monkey_current_give_target"}]}],"key":"BB_monkey_current_served_item"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_current_served_item","targeting_strategy":"/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks","target_source":"/datum/target_source/held_items_then_oview","vision_range":2,"must_be_reachable":true},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_current_served_item","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/pick_up","target_key":"BB_monkey_current_served_item","drop_held":true}]}],"invert":false,"key":"BB_monkey_current_served_item"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_current_served_item","targeting_strategy":"/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks","target_source":"/datum/target_source/held_items_then_oview","vision_range":2,"must_be_reachable":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]}]}],"key":"BB_monkey_tamed"} diff --git a/build/behavior_trees/code/datums/ai/monkey/monkey_shenanigans.bt.compiled.json b/build/behavior_trees/code/datums/ai/monkey/monkey_shenanigans.bt.compiled.json new file mode 100644 index 000000000000..e8beeaa46257 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/monkey/monkey_shenanigans.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_is_holding_item","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/use_in_hand"}],"chance":0.05}],"key":"Literally me"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_current_press_target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/use_on_object","target_key":"BB_monkey_current_press_target"},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_monkey_current_press_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"Monkeys wants to press something"}]}],"key":"BB_monkey_current_press_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/random_chance_from_key","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_current_give_target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/give","target_key":"BB_monkey_current_give_target"}]}],"chance_key":"BB_monkey_give_chance"}],"key":"BB_monkey_current_give_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/monkey_find_weapon"}],"key":"BB_monkey_tamed"}]} diff --git a/build/behavior_trees/code/datums/ai/objects/vending_machines/vending_machine.bt.compiled.json b/build/behavior_trees/code/datums/ai/objects/vending_machines/vending_machine.bt.compiled.json new file mode 100644 index 000000000000..dde504f3c336 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/objects/vending_machines/vending_machine.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/vending_is_tilted","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/vendor_rise_up"}],"cooldown_key":"BB_vending_untilt_cooldown"}]},{"type":"/datum/bt_node/decorator/vending_is_tilted","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_vendor_target","target_key":"BB_vending_current_target","vision_range":7},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_vending_current_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/vendor_crush","target_key":"BB_vending_current_target"}]}],"cooldown_key":"BB_vending_tilt_cooldown"}],"invert":true}]} diff --git a/build/behavior_trees/code/datums/ai/robot_customer/robot_customer.bt.compiled.json b/build/behavior_trees/code/datums/ai/robot_customer/robot_customer.bt.compiled.json new file mode 100644 index 000000000000..55b0e771a316 --- /dev/null +++ b/build/behavior_trees/code/datums/ai/robot_customer/robot_customer.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/robot_customer/find_exit_portal"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_customer_exit_portal","required_dist":0},{"type":"/datum/bt_node/ai_behavior/robot_customer/leave_venue"}]}],"key":"BB_customer_leaving","observer_abort":2},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_customer_current_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/break_spine","target_key":"BB_customer_current_target"}]}],"key":"BB_customer_current_target","observer_abort":2},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/robot_customer/find_seat"}],"cooldown_key":"BB_customer_find_seat_cooldown","cooldown_duration":80,"lock_on_succeed":false}],"key":"BB_customer_my_seat","invert":true},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_customer_my_seat","required_dist":0},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/robot_customer/order_food"}],"key":"BB_customer_current_order","invert":true},{"type":"/datum/bt_node/ai_behavior/robot_customer/wait_for_food"}]}]}],"key":"BB_customer_my_seat"}]} diff --git a/build/behavior_trees/code/modules/bitrunning/antagonists/netguardian.bt.compiled.json b/build/behavior_trees/code/modules/bitrunning/antagonists/netguardian.bt.compiled.json new file mode 100644 index 000000000000..c43cd3695dc4 --- /dev/null +++ b/build/behavior_trees/code/modules/bitrunning/antagonists/netguardian.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"netguardian_rocket","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/avoid_friendly_fire","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","time_between_perform":10},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman.bt.compiled.json b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman.bt.compiled.json new file mode 100644 index 000000000000..694c63452df9 --- /dev/null +++ b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.compiled.json b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.compiled.json new file mode 100644 index 000000000000..644c7eefb550 --- /dev/null +++ b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.compiled.json b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.compiled.json new file mode 100644 index 000000000000..c05b838e02b1 --- /dev/null +++ b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.compiled.json b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.compiled.json new file mode 100644 index 000000000000..48e54e5e9c8d --- /dev/null +++ b/build/behavior_trees/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/alien.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/alien.bt.compiled.json new file mode 100644 index 000000000000..55d0d44b70e5 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/alien.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_alien","bindings":{"buewuqr6":"/datum/bt_node/subtree/melee_alien_combat","bkzbx73d":"/datum/bt_node/subtree/lay_alien_egg"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/basic_alien.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/basic_alien.bt.compiled.json new file mode 100644 index 000000000000..1b0824cb9919 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/basic_alien.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"$buewuqr6"}],"observer_abort":3,"key":"Current Target"},{"type":"$bkzbx73d"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"__bindings":{"buewuqr6":{"label":"Combat Subtree","default":"/datum/bt_node/subtree"},"bkzbx73d":{"label":"Idle Behavior","default":"/datum/bt_node/subtree/random_walk"}}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/drone.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/drone.bt.compiled.json new file mode 100644 index 000000000000..d3adf7228ef9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/drone.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_alien","bindings":{"buewuqr6":"/datum/bt_node/subtree/melee_alien_combat","bkzbx73d":"/datum/bt_node/ai_behavior/plant_alien_weeds"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/lay_alien_egg.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/lay_alien_egg.bt.compiled.json new file mode 100644 index 000000000000..18caaa98520b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/lay_alien_egg.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/lay_alien_egg"}],"observer_abort":2,"cooldown_key":"BB_ALIEN_PLANT_COOLDOWN","cooldown_duration":300} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/melee_alien_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/melee_alien_combat.bt.compiled.json new file mode 100644 index 000000000000..7fe5ec46d8ae --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/melee_alien_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.compiled.json new file mode 100644 index 000000000000..17a0c23cebbe --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/plant_alien_weeds"}],"observer_abort":2,"cooldown_key":"BB_ALIEN_PLANT_COOLDOWN","cooldown_duration":300},{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/queen.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/queen.bt.compiled.json new file mode 100644 index 000000000000..eaf72e8cb15b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/queen.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_alien","bindings":{"buewuqr6":"/datum/bt_node/subtree/ranged_alien_combat","bkzbx73d":"/datum/bt_node/ai_behavior/lay_alien_egg"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.compiled.json new file mode 100644 index 000000000000..2924f00ec158 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","time_between_perform":30,"target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","avoid_friendly_fire":true}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target","approach_movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/alien/sentinel.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/alien/sentinel.bt.compiled.json new file mode 100644 index 000000000000..65bf067bf1ce --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/alien/sentinel.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_alien","bindings":{"buewuqr6":"/datum/bt_node/subtree/ranged_alien_combat"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_spore.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_spore.bt.compiled.json new file mode 100644 index 000000000000..9e00321a5439 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_spore.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/move_to_and_hunt","bindings":{"bvtz06kb":"BB_current_hunting_target","b3y599q4":"BB_current_hunting_target","brrasnah":"BB_current_hunting_target"}},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_travel_destination","required_dist":0},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_travel_destination"}]}],"observer_abort":3,"key":"BB_travel_destination"},{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","targeting_strategy":"/datum/targeting_strategy/dead_mob","target_source":"/datum/target_source/oview_single_type/human_mob"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.compiled.json new file mode 100644 index 000000000000..04e9fa2cdc21 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_travel_destination","required_dist":0},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_travel_destination"}]}],"observer_abort":3,"key":"BB_travel_destination"},{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.compiled.json new file mode 100644 index 000000000000..2d83f85aebc4 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.compiled.json new file mode 100644 index 000000000000..89cd89c9ea74 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/blood_drunk_miner_combat"},{"type":"/datum/bt_node/ai_behavior/wait","duration":0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.compiled.json new file mode 100644 index 000000000000..d1701ec34c49 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/mob_stat_at_least","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_bdm_kinetic_accelerator_ability","target_key":"Current Target","maximum_distance":3},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_bdm_transform_weapon_ability","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}]}],"key":"Current Target"},{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_bdm_dash_attack_ability","target_key":"Current Target"}],"invert":false,"target_key":"Current Target","min_distance":3},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_bdm_transform_weapon_ability","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}]}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.compiled.json new file mode 100644 index 000000000000..e7dd1dadd498 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/pawn_has_trait_from","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_THETHING_MELEEMODE","value":true},{"type":"/datum/bt_node/ai_behavior/pick_random_ability","ability_keys":["BB_THETHING_DECIMATE","BB_THETHING_BIGTENDRILS","BB_THETHING_CARDTENDRILS","BB_THETHING_ACIDSPIT"],"last_used_key":"BB_THETHING_LASTAOE","result_key":"BB_generic_action"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_generic_action","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_THETHING_CHARGE","target_key":"Current Target"}],"chance":0.6}]}],"observer_abort":1,"invert":true,"trait":"immobilized","source":"megafauna"}],"invert":true,"key":"BB_THETHING_MELEEMODE"}],"invert":true,"key":"BB_THETHING_NOAOE"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_boss.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_boss.bt.compiled.json new file mode 100644 index 000000000000..a85c8dc2f810 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_boss.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/thing_aoe"},{"type":"/datum/bt_node/subtree/thing_melee"}]}],"success_policy":1,"failure_policy":1}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait","duration":0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","vision_range":6}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_melee.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_melee.bt.compiled.json new file mode 100644 index 000000000000..dc2607af78cf --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/boss/thing/thing_melee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/true_for_time","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_THETHING_MELEEMODE","value":false},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_THETHING_SHRIEK","target_key":"Current Target","maximum_distance":2},{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_THETHING_CHARGE","target_key":"Current Target"}],"target_key":"Current Target","min_distance":3},{"type":"/datum/bt_node/decorator/pawn_has_trait_from","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":1,"invert":true,"trait":"immobilized","source":"megafauna"}]}]}],"observer_abort":1,"duration":50} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/bot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/bot.bt.compiled.json new file mode 100644 index 000000000000..b1a4e2b000f9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/bot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/subtree/bot_salute_authority"},{"type":"/datum/bt_node/subtree/bot_patrol"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.compiled.json new file mode 100644 index 000000000000..67a0637e1734 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/execute_clean","target_key":"BB_current_pet_target"}]}],"observer_abort":3,"key":"BB_current_pet_target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.compiled.json new file mode 100644 index 000000000000..3175d05dd4e3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/decorator/override_id_set","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"}],"observer_abort":2,"override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bot_is_emagged","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/execute_clean","target_key":"Current Target"}]}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"friendly_janitor","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"friendly_janitor","befriend_message":"friendly_message"}]}],"observer_abort":3,"key":"friendly_janitor"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"friendly_janitor","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/capable_human/cleanbot_whisperer","vision_range":5,"time_between_perform":300}],"key":"friendly_janitor","invert":true}]},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/subtree/bot_patrol"}],"observer_abort":0,"cooldown_key":"post_clean_cooldown"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","time_between_perform":30,"target_key":"Current Target","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/cleanbot_cleanables","vision_range":5,"ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"reach_distance":15},{"type":"/datum/bt_node/subtree/bot_salute_authority"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"invert":true},{"type":"/datum/bt_node/decorator/bot_is_emagged","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"acid_spray_target","required_dist":0,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/execute_clean","target_key":"acid_spray_target"}]}],"observer_abort":3,"key":"acid_spray_target"}],"observer_abort":2,"cooldown_key":"acid_spray_cooldown","cooldown_duration":30},{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"cleanbot_foam"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/subtree/bot_patrol"}],"cooldown_key":"post_clean_cooldown"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"acid_spray_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/capable_human","ignore_list_key":"temporary_ignore_list","vision_range":5,"time_between_perform":300}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/dedbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/dedbot.bt.compiled.json new file mode 100644 index 000000000000..6329cd4a1085 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/dedbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/melee","ability_key":"BB_dedbot_exenterate","target_key":"Current Target"}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209.bt.compiled.json new file mode 100644 index 000000000000..3134c05c97b6 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_target_stunned","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","max_range":9}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target","approach_movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":false,"repeat_secondary_delay":10,"finish_on_primary":false}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.compiled.json new file mode 100644 index 000000000000..b19188387243 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/subtree/simple_ranged_combat","bindings":{"bsjjwub2":"/datum/bt_node/subtree/bot_patrol"}}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/firebot/firebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/firebot/firebot.bt.compiled.json new file mode 100644 index 000000000000..cd1dc47b6f43 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/firebot/firebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/announce_fire_detected"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/bot_interact/extinguish","target_key":"Current Target"}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"Current Target","target_source":"/datum/target_source/firebot_targets","targeting_strategy":"/datum/targeting_strategy/extinguishable_person","ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"vision_range":5},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"Current Target","target_source":"/datum/target_source/range_turfs/firebot_hotspots","targeting_strategy":"/datum/targeting_strategy/burning_hotspot","ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"vision_range":5}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/handle_firebot_speech"},{"type":"/datum/bt_node/subtree/bot_salute_authority"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.compiled.json new file mode 100644 index 000000000000..bc66e93cd889 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/honkbot_slip"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/play_with_clown","target_key":"clown_friend"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"clown_friend","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"clown_friend"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"slippery_target","target_source":"/datum/target_source/honkbot_slippery","targeting_strategy":"/datum/targeting_strategy/can_see","ignore_list_key":"temporary_ignore_list","vision_range":5,"time_between_perform":50},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"slip_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/can_see/slip_victim","ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"vision_range":5}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"clown_friend","target_source":"/datum/target_source/oview_single_type/living_mob","targeting_strategy":"/datum/targeting_strategy/clown_friend","ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"vision_range":5,"time_between_perform":50}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]},{"type":"/datum/bt_node/subtree/bot_salute_authority"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.compiled.json new file mode 100644 index 000000000000..dd807a874d2f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/can_see_target","children":[{"type":"/datum/bt_node/decorator/can_see_target","children":[{"type":"/datum/bt_node/decorator/pawn_has_gravity","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"slip_target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/grab_target","target_key":"slip_target"},{"type":"/datum/bt_node/decorator/is_grabbing_target","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"slippery_target","required_dist":0,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"}],"observer_abort":1,"key":"slip_target"},{"type":"/datum/bt_node/ai_behavior/release_and_slip","victim_key":"slip_target"},{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"flip"}]}]}],"observer_abort":3,"key":"slip_target","range":5}],"observer_abort":3,"key":"slippery_target","range":5} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.compiled.json new file mode 100644 index 000000000000..e4eb675dfe54 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/ai_behavior/wash_target","target_key":"wash_target"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"wash_target","required_dist":0,"finish_on_arrival":false,"movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/commence_trashtalk","target_key":"wash_target"}],"observer_abort":0,"cooldown_key":"trash_talk_cooldown","cooldown_duration":40}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"wash_target"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/hygiene_wash","target_key":"wash_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/capable_human/washable_human","ignore_list_key":"temporary_ignore_list","vision_range":5,"time_between_perform":50},{"type":"/datum/bt_node/subtree/bot_salute_authority"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot.bt.compiled.json new file mode 100644 index 000000000000..4542085b09d3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bot_medical_flag","children":[{"type":"/datum/bt_node/subtree/medbot_find_and_announce_crit"}],"observer_abort":3,"flag":1},{"type":"/datum/bt_node/decorator/bot_medical_flag","children":[{"type":"/datum/bt_node/subtree/medbot_treat_patient"}],"observer_abort":3,"invert":true,"flag":8},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"patient_in_crit","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/crit_patient","vision_range":7},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/medbot_patient","target_key":"Current Target","target_source":"/datum/target_source/oview_single_type/human_mob/medbot_patient","targeting_strategy":"/datum/targeting_strategy/treatable_patient","ignore_list_key":"temporary_ignore_list","must_be_reachable":true,"reach_distance":20,"vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":false,"repeat_secondary_delay":10,"finish_on_primary":false},{"type":"/datum/bt_node/subtree/bot_salute_authority"},{"type":"/datum/bt_node/decorator/bot_medical_flag","children":[{"type":"/datum/bt_node/ai_behavior/handle_medbot_speech","announce_key":"announce_ability"}],"flag":4}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":false}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.compiled.json new file mode 100644 index 000000000000..80720b96b5a3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/announce_patient","target_key":"patient_in_crit"}],"observer_abort":3,"key":"patient_in_crit"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.compiled.json new file mode 100644 index 000000000000..47d46973cc8a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/tend_to_patient","target_key":"Current Target"}]}],"observer_abort":3,"key":"Current Target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.compiled.json new file mode 100644 index 000000000000..73f497e6cb3a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"mulebot_travel_target","required_dist":0,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/handle_delivery","target_key":"mulebot_travel_target"}]}],"key":"mulebot_travel_target"},{"type":"/datum/bt_node/decorator/bot_wire_cut","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bot_mode","children":[{"type":"/datum/bt_node/ai_behavior/find_delivery_beacon","target_key":"mulebot_travel_target","tag_key":"mulebot_destination"}],"mode":"Delivering"},{"type":"/datum/bt_node/decorator/bot_mode","children":[{"type":"/datum/bt_node/ai_behavior/find_delivery_beacon","target_key":"mulebot_travel_target","tag_key":"mulebot_home_beacon"}],"mode":"Returning"}]}],"key":"mulebot_travel_target","invert":true}],"wire":"Beacon","invert":true}]},{"type":"/datum/bt_node/subtree/bot_salute_authority"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.compiled.json new file mode 100644 index 000000000000..44f369334f47 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/repairbot_repair_target"},{"type":"/datum/bt_node/subtree/repairbot_find_target"},{"type":"/datum/bt_node/subtree/bot_salute_authority"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":5.0,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.compiled.json new file mode 100644 index 000000000000..4128c88eac77 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"robot target","required_dist":0,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/bot_interact/tip_robot","target_key":"robot target"},{"type":"/datum/bt_node/ai_behavior/grab_target","target_key":"robot target"}]}],"key":"robot target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deconstruct_target","required_dist":0,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"},{"type":"/datum/bt_node/ai_behavior/bot_interact","target_key":"deconstruct_target"}]}],"key":"deconstruct_target"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.compiled.json new file mode 100644 index 000000000000..460614e4c077 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bot_is_emagged","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/bot_search/valid_robot","time_between_perform":0,"target_key":"robot target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/deconstructable","target_key":"deconstruct_target","minimum_distance":1}]}],"invert":false},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/breached","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/refillable_target","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/valid_grille_target","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/valid_window_fix","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/bot_search/valid_girder","time_between_perform":0,"target_key":"Current Target","minimum_distance":1}]},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"interaction_type","value":1},{"type":"/datum/bt_node/ai_behavior/cancel_current_plan"}]},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/bot_search/valid_wall_target","time_between_perform":0,"target_key":"Current Target","minimum_distance":1},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"interaction_type","value":2},{"type":"/datum/bt_node/ai_behavior/cancel_current_plan"}]}]}],"observer_abort":0,"invert":true,"key":"Current Target"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.compiled.json new file mode 100644 index 000000000000..f9f10e0da5f1 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bot_is_emagged","children":[{"type":"/datum/bt_node/subtree/repairbot_emagged"}],"observer_abort":3},{"type":"/datum/bt_node/decorator/bot_is_emagged","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/ai_behavior/bot_interact","target_key":"Current Target"}],"key":"interaction_type","value":1},{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/build_girder","ability_key":"girder_build_ability","target_key":"Current Target"}],"key":"interaction_type","value":2}]}]}],"observer_abort":1,"key":"Current Target"}],"observer_abort":1,"invert":true},{"type":"/datum/bt_node/subtree/bot_patrol"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/secbot/secbot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/secbot/secbot.bt.compiled.json new file mode 100644 index 000000000000..1437099e8764 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/secbot/secbot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.compiled.json new file mode 100644 index 000000000000..7ec022de0b24 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/bot_respond_to_summon"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"party_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/vibebot_party","ability_key":"party_ability","target_key":"party_target"}]}],"key":"party_target"},{"type":"/datum/bt_node/subtree/bot_patrol"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"party_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/capable_human/party_friend","ignore_list_key":"temporary_ignore_list","vision_range":5,"time_between_perform":50}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/clown/clown.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/clown/clown.bt.compiled.json new file mode 100644 index 000000000000..1722ad983fc5 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/clown/clown.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/simple_retaliate_combat"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/artificer.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/artificer.bt.compiled.json new file mode 100644 index 000000000000..eceda0c7f563 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/artificer.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.compiled.json new file mode 100644 index 000000000000..c4e166a3dbd3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/proteon.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/proteon.bt.compiled.json new file mode 100644 index 000000000000..5895d2498f92 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/proteon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/skittish_brawler_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/wraith.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/wraith.bt.compiled.json new file mode 100644 index 000000000000..1f1bc0d98f43 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/cult/constructs/wraith.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/cytology/vatbeast.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/cytology/vatbeast.bt.compiled.json new file mode 100644 index 000000000000..98d46260ed31 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/cytology/vatbeast.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_at_least","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/melee","ability_key":"BB_generic_action","target_key":"Current Target"}],"observer_abort":2,"key":"BB_basic_mob_has_target_time","minimum":100},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_TARGET_FOOD","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_typed/from_bb_key/basic_foods"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/bee.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/bee.bt.compiled.json new file mode 100644 index 000000000000..ce0187ca3c23 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/bee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/find_hive"},{"type":"/datum/bt_node/subtree/transition_hive_status"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/pollinate_target"},{"type":"/datum/bt_node/subtree/simple_hostile_combat"}]},{"type":"/datum/bt_node/ai_behavior/enter_exit_hive"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_target_hydro","target_source":"/datum/target_source/oview_single_type/hydroponics","targeting_strategy":"/datum/targeting_strategy/pollinatable_hydro","vision_range":10,"time_between_perform":100}],"chance":0.85}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_hive"}],"invert":true,"key":"BB_target_home"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.compiled.json new file mode 100644 index 000000000000..383021ff26e8 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_home","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/inhabit_hive"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_target_home"}]}],"key":"BB_current_home","invert":true}],"observer_abort":2,"invert":false,"key":"BB_target_home"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.compiled.json new file mode 100644 index 000000000000..80f66c96bedc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_hydro","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/pollinate_hydro"}]}],"observer_abort":2,"key":"BB_target_hydro"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.compiled.json new file mode 100644 index 000000000000..5e4ce721119e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/find_hive"},{"type":"/datum/bt_node/subtree/pollinate_target"},{"type":"/datum/bt_node/subtree/transition_hive_status"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/simple_hostile_combat"},{"type":"/datum/bt_node/ai_behavior/enter_exit_hive/queen"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_hive"}],"invert":true,"key":"BB_target_home"}],"failure_policy":0,"success_policy":0,"repeat_secondary":false,"repeat_secondary_delay":10,"finish_on_primary":false} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.compiled.json new file mode 100644 index 000000000000..4edb3e6f3760 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_home","required_dist":1},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_current_home"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_wants_to_transition_hive","value":false}]}],"observer_abort":2,"key":"BB_wants_to_transition_hive","value":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.compiled.json new file mode 100644 index 000000000000..2fec6c77ba97 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_mom","required_dist":1,"finish_on_arrival":true}],"target_key":"BB_found_mom","min_distance":2},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/look_to_parent","parent_key":"BB_found_mom"}],"chance":0.15}]}],"key":"BB_found_mom","observer_abort":1},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_mom"}],"key":"BB_found_mom","invert":true},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.compiled.json new file mode 100644 index 000000000000..5f77adc0a39c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/skittish_and_speak"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow.bt.compiled.json new file mode 100644 index 000000000000..c7f3628371bc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/tip_reaction"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.compiled.json new file mode 100644 index 000000000000..772646294be2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/tip_reaction"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.compiled.json new file mode 100644 index 000000000000..26ae961a8a1c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/tip_reaction"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/random_walk"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/deer/deer.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/deer/deer.bt.compiled.json new file mode 100644 index 000000000000..6a062dfbb42d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/deer/deer.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/stop_and_stare"}],"key":"BB_thing_that_made_us_stationary","observer_abort":2},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deer_home","required_dist":1},{"type":"/datum/bt_node/ai_behavior/deer_rest"}]}],"observer_abort":1,"key":"deer_home"}],"cooldown_key":"deer_next_rest_timer"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deer_playfriend","required_dist":1},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"deer_play_cooldown","cooldown_duration":100},{"type":"/datum/bt_node/ai_behavior/deer_play"}]}],"observer_abort":3,"key":"deer_playfriend"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deer_tree_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/deer_mark","target_key":"deer_tree_target","cooldown_key":"deer_mark_cooldown"}]}],"observer_abort":2,"key":"deer_tree_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deer_grass_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/hunt_target/deer_graze","target_key":"deer_grass_target","cooldown_key":"deer_graze_cooldown"}]}],"observer_abort":3,"key":"deer_grass_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"deer_water_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/deer_drink","target_key":"deer_water_target","cooldown_key":"deer_drink_cooldown"}]}],"observer_abort":3,"key":"deer_water_target"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/random_speech_blackboard"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"deer_wants_to_play","value":true}],"chance":0.03}],"cooldown_key":"deer_play_cooldown"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"deer_playfriend","target_source":"/datum/target_source/oview_single_type/deer_animals","targeting_strategy":"/datum/targeting_strategy/playable_deer"},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"deer_wants_to_play","value":false}]}],"observer_abort":3,"key":"deer_wants_to_play"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"deer_tree_target","target_source":"/datum/target_source/oview_single_type/flora_tree","targeting_strategy":"/datum/targeting_strategy/anything"}],"cooldown_key":"deer_mark_cooldown"}],"invert":true,"key":"deer_tree_target"}],"invert":true,"key":"deer_home"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"deer_grass_target","target_source":"/datum/target_source/range_turfs/typecache_visible/deer_grass","targeting_strategy":"/datum/targeting_strategy/anything"}],"cooldown_key":"deer_graze_cooldown"}],"invert":true,"key":"deer_grass_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"deer_water_target","target_source":"/datum/target_source/range_turfs/typecache_visible/deer_water","targeting_strategy":"/datum/targeting_strategy/anything"}],"cooldown_key":"deer_drink_cooldown"}],"invert":true,"key":"deer_water_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_thing_that_made_us_stationary","target_source":"/datum/target_source/oview_typed/from_bb_key/stationary_targets","targeting_strategy":"/datum/targeting_strategy/anything"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goat/goat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goat/goat.bt.compiled.json new file mode 100644 index 000000000000..03e7b196a701 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goat/goat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/subtree/forage_and_retaliate"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.compiled.json new file mode 100644 index 000000000000..cf4d3d55a626 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_TARGET_FOOD","targeting_strategy":"/datum/targeting_strategy/goose_edible","target_source":"/datum/target_source/oview_items","vision_range":1} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose.bt.compiled.json new file mode 100644 index 000000000000..4aa63aee1e5a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/subtree/forage_and_retaliate","bindings":{"q8w3rtv1":"/datum/bt_node/subtree/forage_for_goose_food"}}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.compiled.json new file mode 100644 index 000000000000..3fa72d010554 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/subtree/forage_and_retaliate","bindings":{"q8w3rtv1":"/datum/bt_node/subtree/forage_for_goose_food","z4n9bk7p":"/datum/bt_node/subtree/pick_retaliate_target"}}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.compiled.json new file mode 100644 index 000000000000..a04d0d8fd203 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}],"observer_abort":2,"invert":true,"target_key":"Current Target","maximum_distance":10,"require_reach":false},{"type":"/datum/bt_node/decorator/mob_stat_at_least","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_monkey_target_disposal","target_source":"/datum/target_source/oview_single_type/disposal_unit","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":9},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/grab_target","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_monkey_target_disposal","required_dist":1,"finish_on_arrival":true,"movement_type":"/datum/ai_movement/jps"},{"type":"/datum/bt_node/ai_behavior/stuff_in_disposal","attack_target_key":"Current Target","disposal_target_key":"BB_monkey_target_disposal"},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}]},{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"Current Target"}]}],"observer_abort":2,"invert":false,"key":"Current Target","min_stat":1},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target","can_attack_turfs":true},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1}]},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/drop_all_held_items"},{"type":"/datum/bt_node/decorator/random_chance_from_key","children":[{"type":"/datum/bt_node/ai_behavior/run_emote","emote_key":"BB_emotes"}],"chance_key":"BB_EMOTE_CHANCE"}]}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.compiled.json new file mode 100644 index 000000000000..d43acdd1a6dc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target","can_attack_turfs":true},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/random_chance_from_key","children":[{"type":"/datum/bt_node/ai_behavior/run_emote","emote_key":"BB_emotes"}],"chance_key":"BB_EMOTE_CHANCE"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pig.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pig.bt.compiled.json new file mode 100644 index 000000000000..2c15b53fa014 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pig.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/subtree/skittish_brawler_combat"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pony.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pony.bt.compiled.json new file mode 100644 index 000000000000..cff1f73085db --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/pony.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/simple_skittish_combat"}],"key":"BB_tamed","observer_abort":2},{"type":"/datum/bt_node/subtree/skittish_brawler_combat"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/rabbit.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/rabbit.bt.compiled.json new file mode 100644 index 000000000000..5f77adc0a39c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/rabbit.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/skittish_and_speak"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/farm_animals/sheep.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/sheep.bt.compiled.json new file mode 100644 index 000000000000..5f77adc0a39c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/farm_animals/sheep.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/skittish_and_speak"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/festivus_pole.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/festivus_pole.bt.compiled.json new file mode 100644 index 000000000000..befc9be9d224 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/festivus_pole.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target","target_key":"BB_low_priority_hunting_target","ability_key":"BB_festive_apc"}]}],"observer_abort":3,"key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/subtree/random_walk","bindings":{"bf07i8ep":10}}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/apc","targeting_strategy":"/datum/targeting_strategy/chargeable_apc","vision_range":6}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/heretic/raw_prophet.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/heretic/raw_prophet.bt.compiled.json new file mode 100644 index 000000000000..abae9529636f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/heretic/raw_prophet.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_ability_ranged_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/heretic/rust_walker.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/heretic/rust_walker.bt.compiled.json new file mode 100644 index 000000000000..8635de408191 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/heretic/rust_walker.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/decorator/pawn_turf_has_trait","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability"}],"observer_abort":0,"trait":"rust_trait"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk/rust"}],"success_policy":1,"failure_policy":0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/heretic/stalker.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/heretic/stalker.bt.compiled.json new file mode 100644 index 000000000000..a1bf2a7fd06f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/heretic/stalker.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_inside_mob","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability/shapeshift"}],"observer_abort":2,"invert":true,"key":"Current Target"}],"invert":true},{"type":"/datum/bt_node/decorator/pawn_inside_mob","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_at_least","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability/shapeshift"}],"observer_abort":2,"key":"BB_basic_mob_has_target_time","minimum":80},{"type":"/datum/bt_node/ai_behavior/wait"}]}],"observer_abort":3,"invert":false,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]}],"observer_abort":1},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/heretic/star_gazer.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/heretic/star_gazer.bt.compiled.json new file mode 100644 index 000000000000..e6c3d8359448 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/heretic/star_gazer.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.compiled.json new file mode 100644 index 000000000000..224616f0eb5e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/ice_demon_flee_from_fire"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/ice_demon_combat"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.compiled.json new file mode 100644 index 000000000000..d622cdcd2c8c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/ice_demon_flee_from_fire"},{"type":"/datum/bt_node/subtree/simple_hostile_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.compiled.json new file mode 100644 index 000000000000..83b18fc48efe --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_furthest_turf_from_target","target_key":"Current Target","set_key":"escape_destination","range":7},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"demon_teleport_ability","target_key":"escape_destination"}]}],"ability_key":"demon_teleport_ability"}],"target_key":"Current Target","maximum_distance":1},{"type":"/datum/bt_node/decorator/pawn_health_below","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"demon_clone_ability","target_key":"Current Target"}],"ability_key":"demon_clone_ability"}],"health_threshold":75},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_valid_teleport_location","target_key":"Current Target","set_key":"teleport_destination","range":3},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"demon_teleport_ability","target_key":"teleport_destination"}]}],"ability_key":"demon_teleport_ability"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","max_range":9}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance","target_key":"Current Target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.compiled.json new file mode 100644 index 000000000000..d19a08d6d05d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/target_holding_lit_item","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"demon_slip_ability"}],"ability_key":"demon_slip_ability"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target","item_types_key":"list_scary_items"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.compiled.json new file mode 100644 index 000000000000..384b14d072c4 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_whelp_widespread_fire","target_key":"Current Target","maximum_distance":3},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_whelp_straightline_fire","target_key":"Current Target","maximum_distance":7},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_cannibal","required_dist":1},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_target_cannibal"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_target_cannibal"}]}],"observer_abort":2,"key":"BB_target_cannibal"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_rock","required_dist":0},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_target_rock","combat_mode":false},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"BB_whelp_sculpt_cooldown","cooldown_duration":3000},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_target_rock"}]}],"observer_abort":2,"key":"BB_target_rock"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_tree","required_dist":2},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"BB_whelp_straightline_fire","target_key":"BB_target_tree","maximum_distance":2},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"BB_whelp_burn_cooldown","cooldown_duration":1200}]}],"observer_abort":2,"key":"BB_target_tree"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_target_cannibal","target_source":"/datum/target_source/oview_single_type/ice_whelp","targeting_strategy":"/datum/targeting_strategy/dead_mob/not_pulled","vision_range":10},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_target_rock","target_source":"/datum/target_source/oview_single_type/icy_rock","targeting_strategy":"/datum/targeting_strategy/anything"}],"cooldown_key":"BB_whelp_sculpt_cooldown"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_target_tree","target_source":"/datum/target_source/oview_single_type/flora_tree","targeting_strategy":"/datum/targeting_strategy/non_stump_tree","vision_range":9}],"ability_key":"BB_whelp_straightline_fire"}],"cooldown_key":"d"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.compiled.json new file mode 100644 index 000000000000..d2df8a3d4e1c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/simple_hostile_combat","bindings":{"bbrbyj7y":"/datum/bt_node/ai_behavior/random_speech/bear"}},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/enrage"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.compiled.json new file mode 100644 index 000000000000..41477912f9c8 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/illusion/escape.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/illusion/escape.bt.compiled.json new file mode 100644 index 000000000000..86f5a41b8cd6 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/illusion/escape.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/subtree/pick_retaliate_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/illusion/retaliate.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/illusion/retaliate.bt.compiled.json new file mode 100644 index 000000000000..86f5a41b8cd6 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/illusion/retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/subtree/pick_retaliate_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/human_trap.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/human_trap.bt.compiled.json new file mode 100644 index 000000000000..56a99511c05d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/human_trap.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_ability_ranged_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/leaper/leaper.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/leaper/leaper.bt.compiled.json new file mode 100644 index 000000000000..8c71682d87ca --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/leaper/leaper.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"leaper_bubble","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"leaper_flop","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"leaper_volley","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"leaper_summon","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.compiled.json new file mode 100644 index 000000000000..da6c3f0f363e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/mega_arachnid_combat"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_surveillance_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_surveillance_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_surveillance_target"}]}],"key":"BB_surveillance_target","observer_abort":1},{"type":"/datum/bt_node/subtree/climb_tree"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_surveillance_target","target_source":"/datum/target_source/oview_typed/surveillance_equipment","targeting_strategy":"/datum/targeting_strategy/working_machine","vision_range":7},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_climbed_tree","target_source":"/datum/target_source/oview_single_type/flora_tree","targeting_strategy":"/datum/targeting_strategy/anything"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.compiled.json new file mode 100644 index 000000000000..3e7b12c6752f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_arachnid_slip"}],"ability_key":"BB_arachnid_slip"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_is_type","children":[{"type":"/datum/bt_node/decorator/target_legcuffed","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_arachnid_restrain","target_key":"Current Target"}],"ability_key":"BB_arachnid_restrain"}],"invert":true}],"target_type":"/mob/living/carbon/human"},{"type":"/datum/bt_node/decorator/target_is_type","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"target_type":"/mob/living/carbon/human","invert":true},{"type":"/datum/bt_node/decorator/target_legcuffed","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]},{"type":"/datum/bt_node/decorator/target_health_below_fraction","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"key":"Current Target","fraction":0.5}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"key":"Current Target","observer_abort":3} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling.bt.compiled.json new file mode 100644 index 000000000000..cb86ef52d36d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/decorator/override_id_set","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"}],"observer_abort":2,"override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_has_reagent","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"hydroplant_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"hydroplant_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"hydroplant_target"}]}],"key":"hydroplant_target","observer_abort":3}],"invert":false,"key":"watercan_target","reagent_type":"/datum/reagent/water"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"beamable_hydroplant_target","required_dist":2},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"solarbeam_ability","target_key":"beamable_hydroplant_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"beamable_hydroplant_target"}]}],"key":"beamable_hydroplant_target","observer_abort":3}],"ability_key":"solarbeam_ability","observer_abort":2},{"type":"/datum/bt_node/decorator/item_inside_pawn","children":[{"type":"/datum/bt_node/decorator/target_has_reagent","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_low_priority_hunting_target"}]}],"observer_abort":3,"key":"BB_low_priority_hunting_target"}],"invert":true,"key":"watercan_target","reagent_type":"/datum/reagent/water"}],"key":"watercan_target"},{"type":"/datum/bt_node/decorator/item_inside_pawn","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"watercan_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"watercan_target"}]}],"key":"watercan_target","observer_abort":3}],"invert":true,"key":"watercan_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/decorator/item_inside_pawn","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"watercan_target","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_single_type/watering_can"}],"invert":true,"key":"watercan_target"},{"type":"/datum/bt_node/decorator/item_inside_pawn","children":[{"type":"/datum/bt_node/decorator/target_has_reagent","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview","targeting_strategy":"/datum/targeting_strategy/water_dispenser"}],"invert":true,"key":"watercan_target","reagent_type":"/datum/reagent/water"}],"key":"watercan_target"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"hydroplant_target","target_source":"/datum/target_source/oview","targeting_strategy":"/datum/targeting_strategy/treatable_hydro","time_between_perform":50},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"beamable_hydroplant_target","target_source":"/datum/target_source/oview","targeting_strategy":"/datum/targeting_strategy/beamable_hydro","time_between_perform":40}],"ability_key":"solarbeam_ability"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.compiled.json new file mode 100644 index 000000000000..b69f6a865d4f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"rapidseeds_ability","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"solarbeam_ability","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.compiled.json new file mode 100644 index 000000000000..77df6cb13d44 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/decorator/target_has_trait","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","max_range":9}],"min_distance":2},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1}],"observer_abort":3,"invert":true,"key":"watcher_overwatched"},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.compiled.json new file mode 100644 index 000000000000..9cceffa009a0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/decorator/mob_stat_at_least","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"BB_bileworm_devour","target_key":"BB_basic_execution_target","maximum_distance":16}],"observer_abort":2,"invert":false,"key":"BB_basic_execution_target","min_stat":"UNCONSCIOUS"}],"ability_key":"BB_bileworm_devour"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/decorator/bileworm_should_resurface","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute","ability_key":"BB_bileworm_resurface","target_key":"Current Target"}],"target_key":"Current Target"}],"ability_key":"BB_bileworm_resurface"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute","ability_key":"BB_bileworm_spew_bile","target_key":"Current Target"}],"ability_key":"BB_bileworm_spew_bile"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":false}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.compiled.json new file mode 100644 index 000000000000..19d1f2b0032d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/brimbeam","ability_key":"BB_TARGETED_action","target_key":"Current Target","maximum_distance":9}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_cardinal","target_key":"Current Target","minimum_distance":2,"maximum_distance":9},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.compiled.json new file mode 100644 index 000000000000..8fdab6836a28 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/ai_behavior/dig_away_from_danger"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/grub_eat_target","bindings":{"bjwb8dxm":"BB_ore_target","bj13tsp3":"BB_ore_target","b6t594ql":"BB_ore_target"}},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_mom","required_dist":1,"finish_on_arrival":true}],"observer_abort":3,"key":"BB_found_mom"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_ore"},{"type":"/datum/bt_node/ai_behavior/find_mom"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.compiled.json new file mode 100644 index 000000000000..3c4f0195dc2c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]},{"type":"","override_id":"pet_command"},{"type":"/datum/bt_node/ai_behavior/dig_away_from_danger"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/burrow_through_ground"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/grub_eat_target","bindings":{"b6t594ql":"BB_boulder_target","bj13tsp3":"BB_boulder_target","bjwb8dxm":"BB_BOULDER_TARGETBB_BOULDER_TARGET"}},{"type":"/datum/bt_node/subtree/grub_eat_target","bindings":{"b6t594ql":"BB_ore_target","bj13tsp3":"BB_ore_target","bjwb8dxm":"BB_ore_target"}},{"type":"/datum/bt_node/subtree/grub_eat_target","bindings":{"b6t594ql":"BB_vent_target","bj13tsp3":"BB_vent_target","bjwb8dxm":"BB_vent_target"}},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_mineral_wall"},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"Mining Cooldown","cooldown_duration":40},{"type":"/datum/bt_node/ai_behavior/mine_wall","target_key":"BB_target_mineral_wall"}]}],"observer_abort":3,"key":"BB_target_mineral_wall"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/grab_target","target_key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_low_priority_hunting_target"}]}],"key":"BB_low_priority_hunting_target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_ore"},{"type":"/datum/bt_node/ai_behavior/find_boulder"},{"type":"/datum/bt_node/ai_behavior/find_ore_vent"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/find_mineral_wall","target_key":"BB_target_mineral_wall"}],"cooldown_key":"Mining Cooldown"},{"type":"/datum/bt_node/ai_behavior/find_grub_egg"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.compiled.json new file mode 100644 index 000000000000..dc90fd61f095 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"$bj13tsp3","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/grub_eat","target_key":"$bjwb8dxm"}]}],"observer_abort":3,"key":"$b6t594ql","__bindings":{"b6t594ql":{"label":"target_key","default":null},"bj13tsp3":{"label":"target_key","default":""},"bjwb8dxm":{"label":"target_key","default":""}}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.compiled.json new file mode 100644 index 000000000000..a28028fef51f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/goliath_tentacles","ability_key":"BB_goliath_tentacles","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining","target_key":"Current Target"}],"key":"Current Target"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/goliath_find_diggable_turf","target_key":"BB_goliath_hole"}],"key":"BB_goliath_hole","invert":true},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_goliath_hole","required_dist":0},{"type":"/datum/bt_node/ai_behavior/goliath_dig","target_key":"BB_goliath_hole"}]},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location","check_faction":true}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.compiled.json new file mode 100644 index 000000000000..e96fbf26a777 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_mom","required_dist":1,"finish_on_arrival":true}],"observer_abort":3,"key":"BB_found_mom"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":false},{"type":"/datum/bt_node/ai_behavior/find_parent","mom_types_key":"BB_find_mom_types","found_mom_key":"BB_found_mom"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.compiled.json new file mode 100644 index 000000000000..f659cd1d9afe --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"trough_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target/food_trough","target_key":"trough_target"}]}],"observer_abort":3,"key":"trough_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":false},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"trough_target","target_source":"/datum/target_source/oview_single_type/gutlunch_trough","targeting_strategy":"/datum/targeting_strategy/trough_with_ore","vision_range":9}],"key":"BB_check_hungry"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.compiled.json new file mode 100644 index 000000000000..f501a0c0e197 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/subtree/simple_retaliate_combat","bindings":{"b2jvnm5d":1}}]},{"type":"/datum/bt_node/ai_behavior/befriend_ashwalkers"},{"type":"/datum/bt_node/subtree/find_partner"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":50,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.compiled.json new file mode 100644 index 000000000000..56a99511c05d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_ability_ranged_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion.bt.compiled.json new file mode 100644 index 000000000000..dc586bdcd46c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/random_speech/legion"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.compiled.json new file mode 100644 index 000000000000..5ea2779a29c2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.compiled.json new file mode 100644 index 000000000000..a6f3af26ff19 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/consider_venting"},{"type":"/datum/bt_node/subtree/random_walk","bindings":{"bf07i8ep":10}}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_entry_vent_target","target_source":"/datum/target_source/oview_single_type/vent_pump","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":7},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/random_speech/legion"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.compiled.json new file mode 100644 index 000000000000..2a1862e533d4 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_TARGETED_action","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.compiled.json new file mode 100644 index 000000000000..49b0c7917e7d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/simple_retaliate_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.compiled.json new file mode 100644 index 000000000000..cfdc3e730a6c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/simple_capricious_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard.bt.compiled.json new file mode 100644 index 000000000000..d2c4f973b830 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/generic_mook_behavior","bindings":{"b1kiiayf":"/datum/bt_node/subtree/bard_play_music","b0ymbjo1":"/datum/bt_node/subtree/bard_find_targets"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.compiled.json new file mode 100644 index 000000000000..f50201b1489a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mook_tribal_chief","target_source":"/datum/target_source/oview_single_type/tribal_chief","targeting_strategy":"/datum/targeting_strategy/anything"},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"mook_tribal_chief","long_range_friendship":true,"forget_target":false}]}],"invert":true,"key":"mook_tribal_chief"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"music_audience","target_source":"/datum/target_source/near_village_humans","targeting_strategy":"/datum/targeting_strategy/capable_human","time_between_perform":10}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.compiled.json new file mode 100644 index 000000000000..74fa5b9cfed8 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/generic_play_instrument","bindings":{"bulu13xf":75}},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"music_audience","required_dist":2,"finish_on_arrival":false}],"observer_abort":2,"key":"music_audience"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target","target_key":"home_village","walk_chance":75}],"success_policy":1,"failure_policy":0,"loop_delay":10}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.compiled.json new file mode 100644 index 000000000000..312234cbe87d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_deposit_position"}],"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_ore","time_between_perform":10,"range":1}],"invert":true,"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"material_stand_target","target_source":"/datum/target_source/oview_single_type/ore_stand","targeting_strategy":"/datum/targeting_strategy/anything"}],"invert":true,"key":"material_stand_target"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.compiled.json new file mode 100644 index 000000000000..9874acfc3716 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/issue_pet_command","command_list_key":"BB_mook_commands","command_type":"/datum/pet_command/attack","target_key":"Current Target","commandable_mob_type":"/mob/living/basic/mining/mook","command_distance":7}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/issue_pet_command","command_list_key":"BB_mook_commands","command_type":"/datum/pet_command/fetch","target_key":"BB_ore_target","commandable_mob_type":"/mob/living/basic/mining/mook","command_distance":7}],"observer_abort":3,"key":"BB_ore_target"}]}],"cooldown_key":"command cooldown","cooldown_duration":100,"lock_on_succeed":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.compiled.json new file mode 100644 index 000000000000..c3fb056077b2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_deposit_position"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_deposit_position"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"material_stand_target"}]}],"observer_abort":2,"key":"BB_deposit_position"}],"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"bonfire_target"},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"bonfire_target","always_reset_target":true,"behavior_combat_mode":false}]}],"observer_abort":2,"invert":false,"key":"bonfire_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_ore_target"},{"type":"/datum/bt_node/ai_behavior/pick_up_item_virtual","target_key":"BB_ore_target","storage_key":"BB_SIMPLE_CARRY_ITEM"}]}],"observer_abort":2,"invert":false,"key":"BB_ore_target"}],"observer_abort":2,"invert":true,"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target","target_key":"home_village","walk_chance":75}],"success_policy":1,"failure_policy":0,"loop_delay":10}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"bonfire_target","target_source":"/datum/target_source/oview_typed/from_bb_key/bonfire_targets","targeting_strategy":"/datum/targeting_strategy/unlit_bonfire"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.compiled.json new file mode 100644 index 000000000000..374bad58e688 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_village"}],"invert":true,"key":"home_village"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_same_z_as_key","children":[{"type":"/datum/bt_node/decorator/pawn_farther_than_from_key","children":[{"type":"/datum/bt_node/decorator/mook_has_flee_reason","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/clear_key","key":"injured_mook"},{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"mook_jump_ability"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_target_mineral_wall"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"music_audience"}]}],"observer_abort":2}],"anchor_key":"home_village","distance_key":"maximum_distance_to_village"}],"key":"home_village"},{"type":"$bk7ixti3"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"mook_leap_ability","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"$b1kiiayf"}]},{"type":"$b0ymbjo1"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"__bindings":{"b1kiiayf":{"label":"Idle Behavior","default":"/datum/bt_node/subtree"},"b0ymbjo1":{"label":"Targetting Behavior","default":"/datum/bt_node/subtree"},"bk7ixti3":{"label":"High Priority Behavior","default":"/datum/bt_node/subtree"}}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.compiled.json new file mode 100644 index 000000000000..b7010694ca12 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_deposit_position","required_dist":0},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_deposit_position"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"material_stand_target"}]}],"observer_abort":2,"key":"BB_deposit_position"}],"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_ore_target"},{"type":"/datum/bt_node/ai_behavior/pick_up_item_virtual","target_key":"BB_ore_target","storage_key":"BB_SIMPLE_CARRY_ITEM"}]}],"observer_abort":2,"invert":false,"key":"BB_ore_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_target_mineral_wall"},{"type":"/datum/bt_node/ai_behavior/mine_wall","target_key":"BB_target_mineral_wall","time_between_perform":10},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"mining cooldown","cooldown_duration":100},{"type":"/datum/bt_node/ai_behavior/find_ore","time_between_perform":5.0}]}],"observer_abort":2,"invert":false,"key":"BB_target_mineral_wall"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/calculate_wander_destination"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_wander_destination","required_dist":0}]},{"type":"/datum/bt_node/subtree/random_walk"}]}],"observer_abort":2,"invert":true,"key":"BB_SIMPLE_CARRY_ITEM"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_deposit_position"}],"key":"BB_SIMPLE_CARRY_ITEM"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/find_ore"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/find_mineral_wall/mook","target_key":"BB_target_mineral_wall"}],"cooldown_key":"mining cooldown"}]}],"invert":true,"key":"BB_SIMPLE_CARRY_ITEM"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.compiled.json new file mode 100644 index 000000000000..c70b3d625f1a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"injured_mook"},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","always_reset_target":true,"target_key":"injured_mook"}]}],"observer_abort":3,"key":"injured_mook"},{"type":"/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target","target_key":"home_village","walk_chance":75}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/mook.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/mook.bt.compiled.json new file mode 100644 index 000000000000..eb69e776b453 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/mook.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/generic_mook_behavior","bindings":{"b0ymbjo1":"/datum/bt_node/subtree/worker_find_targets","b1kiiayf":"/datum/bt_node/subtree/go_mining"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support.bt.compiled.json new file mode 100644 index 000000000000..e82ee7b19c71 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/generic_mook_behavior","bindings":{"b1kiiayf":"/datum/bt_node/subtree/heal_injured","b0ymbjo1":"/datum/bt_node/subtree/support_find_targets"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.compiled.json new file mode 100644 index 000000000000..995d92aea784 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mook_tribal_chief","target_source":"/datum/target_source/oview_single_type/tribal_chief","targeting_strategy":"/datum/targeting_strategy/anything"},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"mook_tribal_chief","long_range_friendship":true,"forget_target":false}]}],"invert":true,"key":"mook_tribal_chief"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"injured_mook","target_source":"/datum/target_source/oview_typed/from_bb_key/mook_heal_targets","targeting_strategy":"/datum/targeting_strategy/injured_mob"}],"invert":true,"key":"BB_storm_approaching"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.compiled.json new file mode 100644 index 000000000000..47b9fe22a20b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/generic_mook_behavior","bindings":{"b0ymbjo1":"/datum/bt_node/subtree/chief_find_targets","b1kiiayf":"/datum/bt_node/subtree/chief_manage_village","bk7ixti3":"/datum/bt_node/subtree/chief_issue_commands"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.compiled.json new file mode 100644 index 000000000000..22d81cd33cca --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"material_stand_target","target_source":"/datum/target_source/oview_single_type/ore_stand","targeting_strategy":"/datum/targeting_strategy/anything"}],"invert":true,"key":"material_stand_target"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mook_tribal_chief","target_source":"/datum/target_source/oview_single_type/tribal_chief","targeting_strategy":"/datum/targeting_strategy/anything"},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"mook_tribal_chief","long_range_friendship":true,"forget_target":false}]}],"invert":true,"key":"mook_tribal_chief"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.compiled.json new file mode 100644 index 000000000000..5d4dd01a8694 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_hunting_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/hunt_target/latch_onto","target_key":"BB_current_hunting_target","hunt_cooldown":50}]}],"observer_abort":1,"invert":true,"target_key":"BB_current_hunting_target"}],"key":"BB_current_hunting_target","observer_abort":3}],"success_policy":1,"failure_policy":1,"loop_delay":15.0},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","target_source":"/datum/target_source/oview_single_type/ore_vent","targeting_strategy":"/datum/targeting_strategy/ore_vent_unclaimed","vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.compiled.json new file mode 100644 index 000000000000..64de9c0a6b5f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/express_happiness"}],"observer_abort":3,"chance":0.3},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_mom"},{"type":"/datum/bt_node/ai_behavior/look_to_parent"}]}],"observer_abort":3,"key":"BB_found_mom"}],"observer_abort":3,"chance":0.15},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/raptor_find_food"},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_mom"}],"invert":true,"key":"BB_found_mom"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.compiled.json new file mode 100644 index 000000000000..4b4a289ebbc1 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"raptor_baby"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"raptor_baby","combat_mode":false},{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"grooms its baby!"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"raptor_baby"}]}],"chance":0.6}],"observer_abort":3,"key":"raptor_baby"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.compiled.json new file mode 100644 index 000000000000..67a67cd3559f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/subtree/raptor_flee"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/raptor_heal_injured"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/raptor_food_trough"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/subtree/play_with_owner"}],"observer_abort":0,"chance":0.4},{"type":"/datum/bt_node/subtree/care_for_young"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/raptor_find_food"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"BB_owner_target","targeting_strategy":"/datum/targeting_strategy/ally_mob","target_source":"/datum/target_source/oview_single_type/living_mob"}],"observer_abort":0,"key":"raptor_playful"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"injured_raptor","targeting_strategy":"/datum/targeting_strategy/injured_mob/not_self/injured_raptor"},{"type":"/datum/bt_node/ai_behavior/retrieve_injured_rider","target_key":"injured_raptor"}]}],"observer_abort":0,"key":"BB_basic_mob_healer"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"raptor_baby","targeting_strategy":"/datum/targeting_strategy/healthy_raptor_baby","target_source":"/datum/target_source/oview_raptor_babies"}],"observer_abort":0,"key":"raptor_motherly"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]},{"type":"/datum/bt_node/subtree/find_partner"},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/express_happiness"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.compiled.json new file mode 100644 index 000000000000..01309e1c72ca --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"raptor_trough_target","targeting_strategy":"/datum/targeting_strategy/raptor_trough","target_source":"/datum/target_source/oview_single_type/raptor_trough"}],"failure_policy":0,"success_policy":0,"repeat_secondary":false,"finish_on_primary":false}],"observer_abort":1,"cooldown_key":"BB_NEXT_EAT_FOOD"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.compiled.json new file mode 100644 index 000000000000..087fea9e73d2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"key":"BB_RAPTOR_COWARDLY"},{"type":"/datum/bt_node/decorator/pawn_health_below","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"health_blackboard_key":"raptor_flee_threshold"},{"type":"/datum/bt_node/decorator/check_rider_stat","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.compiled.json new file mode 100644 index 000000000000..a9af0238bf0f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"injured_raptor"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"injured_raptor","combat_mode":false},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"injured_raptor"}]}],"observer_abort":3,"key":"injured_raptor"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.compiled.json new file mode 100644 index 000000000000..1d2d0523de20 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_owner_target"},{"type":"/datum/bt_node/ai_behavior/hunt_target/play_with_owner","cooldown_key":"BB_owner_target"}]}],"observer_abort":3,"key":"BB_owner_target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.compiled.json new file mode 100644 index 000000000000..bd3184c469a7 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"raptor_trough_target"},{"type":"/datum/bt_node/ai_behavior/ai_interact","target_key":"raptor_trough_target","combat_mode":false},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"raptor_trough_target"}]}],"observer_abort":3,"key":"raptor_trough_target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.compiled.json new file mode 100644 index 000000000000..364faacec07d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"tendril_spikes"},{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"tendril_lash"}]}],"target_key":"Current Target","maximum_distance":7},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"tendril_chaser","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":false}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":true,"vision_range":5},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","target_loss_distance":9,"vision_range":5}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.compiled.json new file mode 100644 index 000000000000..db275f6a561d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/decorator/target_has_trait","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_at_least","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability"}],"key":"BB_basic_mob_has_target_time","minimum":50},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","max_range":9}]}],"success_policy":1,"failure_policy":1}],"observer_abort":3,"invert":true,"key":"watcher_overwatched"},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements/mining"}],"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot.bt.compiled.json new file mode 100644 index 000000000000..c0787dc703fc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/minebot_combat"},{"type":"/datum/bt_node/subtree/minebot_mining"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"miner_friend","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/rock_stoner"},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"miner_friend","long_range_friendship":true}]},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"nearby_dead_miner","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/unconscious_human"}],"cooldown_key":"minebot callcrit cooldown","cooldown_duration":100},{"type":"/datum/bt_node/ai_behavior/send_sos_message","target_key":"nearby_dead_miner"}]}]}],"success_policy":0,"failure_policy":0,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_combat.bt.compiled.json new file mode 100644 index 000000000000..efd6d22309b1 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"minebot_missile_ability","target_key":"minebot_missile_target"}],"observer_abort":2,"key":"minebot_missile_ability"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/minebot","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target","min_dist_key":"minimum_shooting_distance","max_dist_key":"minimum_shooting_distance"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"minebot_missile_target","target_source":"/datum/target_source/oview_single_type/minebot_target","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":7}],"key":"minebot_missile_target","invert":true}],"success_policy":0,"failure_policy":0,"loop_delay":10},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"minebot_landmine_ability"}],"invert":false,"key":"minebot_landmine_ability"}],"success_policy":0,"failure_policy":0,"loop_delay":10}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3} diff --git a/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_mining.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_mining.bt.compiled.json new file mode 100644 index 000000000000..33fa38f1c9e8 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/minebots/minebot_mining.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/target_health_below_fraction","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"defend_drone","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/repair_drone","target_key":"defend_drone"}]}],"observer_abort":3,"key":"defend_drone","fraction":0.75}],"observer_abort":3,"key":"defend_drone"}],"observer_abort":3,"key":"minebot_repair_drone"},{"type":"/datum/bt_node/ai_behavior/befriend_target/check_ally","target_key":"defend_drone","long_range_friendship":true,"forget_target":false}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_ore_target","required_dist":1,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/collect_ore/minebot","target_key":"BB_ore_target"}]}],"observer_abort":3,"key":"BB_ore_target"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"target_mineral_turf","required_dist":2,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/minebot_mine_turf","target_key":"target_mineral_turf"}]}],"observer_abort":2,"key":"target_mineral_turf"}],"observer_abort":2,"key":"automated_mining"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","time_between_perform":50,"target_key":"defend_drone","targeting_strategy":"/datum/targeting_strategy/anything","target_source":"/datum/target_source/oview_single_type/node_drone","vision_range":9,"revalidation_mode":1},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_ore_target","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_single_type/ore","vision_range":2},{"type":"/datum/bt_node/ai_behavior/find_mineral_wall/minebot","target_key":"target_mineral_turf"}]}],"observer_abort":2,"key":"automated_mining"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat.bt.compiled.json new file mode 100644 index 000000000000..555b1850e6d0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/true_for_time","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}}],"duration":30,"succeed_on_timeout":true},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_basic_flee_target"}]}],"key":"BB_basic_flee_target","observer_abort":2},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/cat_reside_in_home"},{"type":"/datum/bt_node/subtree/cat_haul_food"},{"type":"/datum/bt_node/subtree/cat_hunt_mice"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"tresspasser_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/territorial_struggle","target_key":"tresspasser_target","cries_key":"hostile_meows"}]}],"key":"tresspasser_target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3}],"observer_abort":0,"invert":true,"key":"mouse_target"},{"type":"/datum/bt_node/subtree/cat_find_food"},{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/cats"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_partner"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"cat_home","target_source":"/datum/target_source/oview_single_type/cat_house","targeting_strategy":"/datum/targeting_strategy/valid_cat_home"}],"chance":0.05},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/find_cat_tresspasser","target_key":"tresspasser_target"}],"chance":0.05},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mouse_target","target_source":"/datum/target_source/oview_single_type/mouse","targeting_strategy":"/datum/targeting_strategy/huntable_mouse","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"cat_food_target","target_source":"/datum/target_source/oview_typed/from_bb_key/huntable_prey","targeting_strategy":"/datum/targeting_strategy/cat_food","vision_range":9}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_bread.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_bread.bt.compiled.json new file mode 100644 index 000000000000..4f03d5a4ddef --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_bread.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/cat_turn_off_stove"},{"type":"/datum/bt_node/subtree/cat_haul_food"},{"type":"/datum/bt_node/subtree/cat_hunt_mice"},{"type":"/datum/bt_node/subtree/cat_find_food"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/cats"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/find_partner"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mouse_target","target_source":"/datum/target_source/oview_single_type/mouse","targeting_strategy":"/datum/targeting_strategy/huntable_mouse","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"cat_food_target","target_source":"/datum/target_source/oview_typed/from_bb_key/huntable_prey","targeting_strategy":"/datum/targeting_strategy/cat_food","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"stove_target","target_source":"/datum/target_source/oview_single_type/oven","targeting_strategy":"/datum/targeting_strategy/finished_stove","vision_range":9},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"food_to_deliver","target_source":"/datum/target_source/carried_huntable_prey","targeting_strategy":"/datum/targeting_strategy/anything"}],"key":"food_to_deliver","invert":true},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"food_to_deliver","target_source":"/datum/target_source/carried_huntable_prey","targeting_strategy":"/datum/targeting_strategy/anything"}],"key":"food_to_deliver","invert":true}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_cake.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_cake.bt.compiled.json new file mode 100644 index 000000000000..cfc0bf46ee61 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_cake.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/cat_turn_off_stove"},{"type":"/datum/bt_node/subtree/cat_decorate_donuts"},{"type":"/datum/bt_node/subtree/cat_haul_food"},{"type":"/datum/bt_node/subtree/cat_hunt_mice"},{"type":"/datum/bt_node/subtree/cat_find_food"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/cats"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/subtree/find_partner"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"mouse_target","target_source":"/datum/target_source/oview_single_type/mouse","targeting_strategy":"/datum/targeting_strategy/huntable_mouse","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"cat_food_target","target_source":"/datum/target_source/oview_typed/from_bb_key/huntable_prey","targeting_strategy":"/datum/targeting_strategy/cat_food","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"stove_target","target_source":"/datum/target_source/oview_single_type/oven","targeting_strategy":"/datum/targeting_strategy/finished_stove","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"donut_target","target_source":"/datum/target_source/oview_single_type/donut","targeting_strategy":"/datum/targeting_strategy/decorated_donut","vision_range":9},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"food_to_deliver","target_source":"/datum/target_source/carried_huntable_prey","targeting_strategy":"/datum/targeting_strategy/anything"}],"key":"food_to_deliver","invert":true},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"food_to_deliver","target_source":"/datum/target_source/carried_huntable_prey","targeting_strategy":"/datum/targeting_strategy/anything"}],"key":"food_to_deliver","invert":true}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.compiled.json new file mode 100644 index 000000000000..875e7b4fde44 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"donut_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/decorate_donuts","target_key":"donut_target"}]}],"key":"donut_target","observer_abort":3} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.compiled.json new file mode 100644 index 000000000000..51899c21580d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"cat_food_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"cat_food_target","always_reset_target":true}]}],"key":"cat_food_target","observer_abort":3} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.compiled.json new file mode 100644 index 000000000000..a717f60d6e46 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cat_holding_food","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"kitten_to_feed","required_dist":1},{"type":"/datum/bt_node/ai_behavior/deliver_food_to_kitten","target_key":"kitten_to_feed","food_key":"food_to_deliver"}]}],"key":"food_to_deliver"}],"key":"kitten_to_feed","observer_abort":3}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.compiled.json new file mode 100644 index 000000000000..770c97852cc3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/cat_holding_food","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"mouse_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/play_with_mouse","target_key":"mouse_target"}]}],"key":"mouse_target","observer_abort":3}],"invert":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.compiled.json new file mode 100644 index 000000000000..0cf26f4dd1a9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_loc_is_type","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/leave_cat_home"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}],"loc_type":"/obj/structure/cat_house","observer_abort":2},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"cat_home","required_dist":1},{"type":"/datum/bt_node/ai_behavior/enter_cat_home","target_key":"cat_home"}]}],"key":"cat_home","observer_abort":3}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.compiled.json new file mode 100644 index 000000000000..d7496ff2ade9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"stove_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"stove_target","always_reset_target":true}]}],"key":"stove_target","observer_abort":3} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/cat/kitten.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/kitten.bt.compiled.json new file mode 100644 index 000000000000..3054191cd5aa --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/cat/kitten.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":2},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/beacon_for_food","target_key":"human_beg_target","meows_key":"hungry_meows"}],"key":"human_beg_target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_farther_than_from_key","children":[{"type":"/datum/bt_node/ai_behavior/beacon_for_food","target_key":"cat_food_target","meows_key":"hungry_meows"}],"anchor_key":"cat_food_target","distance_key":"max_distance_to_food"},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"cat_food_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"cat_food_target","always_reset_target":true}]}]}],"key":"cat_food_target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/cats"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"cat_food_target","target_source":"/datum/target_source/oview_typed/from_bb_key/huntable_prey","targeting_strategy":"/datum/targeting_strategy/cat_food","vision_range":9},{"type":"/datum/bt_node/ai_behavior/find_human_to_beg","target_key":"human_beg_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/dog/guarddog.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/dog/guarddog.bt.compiled.json new file mode 100644 index 000000000000..d94250485b84 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/dog/guarddog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_retaliate_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/fox.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/fox.bt.compiled.json new file mode 100644 index 000000000000..cc55e5de0823 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/fox.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}}],"key":"BB_basic_flee_target","observer_abort":2},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/fox"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/decorator/no_humans_watching","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"polling_rate":20},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/fox_docile.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/fox_docile.bt.compiled.json new file mode 100644 index 000000000000..d977d389b7cc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/fox_docile.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}}],"key":"BB_basic_flee_target","observer_abort":2},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/random_walk"},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/fox"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/gondolas/gondola.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/gondolas/gondola.bt.compiled.json new file mode 100644 index 000000000000..e1ea2e2d727e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/gondolas/gondola.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/random_walk","bindings":{"bf07i8ep":10}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/orbie/orbie.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/orbie/orbie.bt.compiled.json new file mode 100644 index 000000000000..69f59fc81132 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/orbie/orbie.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"nearby_playmate","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/interact_with_playmate","target_key":"nearby_playmate"}]}],"key":"nearby_playmate","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/relay_pda_message","target_key":"last_received_message"}],"observer_abort":2,"key":"last_received_message"}]},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/find_playmate"}],"cooldown_key":"next_playdate"},{"type":"/datum/bt_node/subtree/find_food"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.compiled.json new file mode 100644 index 000000000000..0b2b3a8ebefd --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_contained_in_obj","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/container_attackable","children":[{"type":"/datum/bt_node/ai_behavior/break_out_of_object/from_bb","target_key":"BB_basic_mob_escape_target"}]},{"type":"/datum/bt_node/ai_behavior/resist"}]}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/subtree/perching"}],"chance":0.05},{"type":"/datum/bt_node/subtree/parrot_hoard"},{"type":"/datum/bt_node/ai_behavior/idle_random_walk/parrot"}]}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/parrot_repeat_speech"}],"cooldown_key":"BB_parrot_speech_cooldown","cooldown_duration":75.0},{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"chance":0.05}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.compiled.json new file mode 100644 index 000000000000..1eb876e6838e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"perch_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/perch_on_target/haunt","target_key":"perch_target"}]},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"perch_target"}]}],"key":"perch_target"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"perch_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/capable_human"}],"chance":0.02},{"type":"/datum/bt_node/subtree/parrot_hoard"},{"type":"/datum/bt_node/ai_behavior/idle_random_walk/parrot"}]}],"success_policy":1,"failure_policy":0,"loop_delay":15.0},{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/parrot_repeat_speech"}],"cooldown_key":"BB_parrot_speech_cooldown","cooldown_duration":75.0},{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"chance":0.05}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.compiled.json new file mode 100644 index 000000000000..547cbde046f3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/succeed"}],"key":"hoard_location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"hoard_location","targeting_strategy":"/datum/targeting_strategy/parrot_hoard_location","target_source":"/datum/target_source/oview","vision_range":"hoard_location_range"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_is_holding_item","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"hoard_location","required_dist":1},{"type":"/datum/bt_node/ai_behavior/drop_all_held_items"}]}],"observer_abort":1,"key":"Literally me"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"hoard_item_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot","target_key":"hoard_item_target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"hoard_item_target"}]}],"observer_abort":1,"key":"hoard_item_target"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item","target_key":"hoard_item_target"}],"chance":0.05}]}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.compiled.json new file mode 100644 index 000000000000..5cfd1694daf2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"perch_target","target_source":"/datum/target_source/oview_single_type/human_mob","targeting_strategy":"/datum/targeting_strategy/ally_mob"}],"chance":0.5},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"perch_target","target_source":"/datum/target_source/oview_typed/from_bb_key/parrot_perch_types","targeting_strategy":"/datum/targeting_strategy/anything"}]}],"key":"perch_target","invert":true},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"perch_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/perch_on_target","target_key":"perch_target"}]}],"observer_abort":1,"invert":false,"key":"perch_target"}]},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"perch_target"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin.bt.compiled.json new file mode 100644 index 000000000000..8e5adef45d2e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/move_to_and_hunt","bindings":{"bvtz06kb":"BB_fishing_target","b3y599q4":"BB_fishing_target","brrasnah":"BB_fishing_target","b3cnse9r":1,"bd1towgc":0,"bqwjf4id":450,"bm3y5m55":"BB_fishing_timer"}},{"type":"/datum/bt_node/subtree/move_to_and_hunt","bindings":{"bvtz06kb":"BB_drillable_ice","b3y599q4":"BB_drillable_ice","brrasnah":"BB_drillable_ice","b3cnse9r":1,"bd1towgc":1,"bqwjf4id":150,"bm3y5m55":"BB_ice_drilling_timer"}},{"type":"/datum/bt_node/subtree/move_to_and_hunt"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/penguin"}},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_fishing_target","target_source":"/datum/target_source/range_turfs/typecache_visible/ice","targeting_strategy":"/datum/targeting_strategy/fishing"}],"cooldown_key":"BB_fishing_timer"}],"cooldown_key":"BB_next_food_eat"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_drillable_ice","target_source":"/datum/target_source/range_turfs/typecache_visible/ice","targeting_strategy":"/datum/targeting_strategy/drillable_ice"}],"cooldown_key":"BB_ice_drilling_timer"}],"invert":true,"key":"BB_fishing_target"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/penguin_egg","targeting_strategy":"/datum/targeting_strategy/uncarried_egg"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.compiled.json new file mode 100644 index 000000000000..74c13f8600fd --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_mom","required_dist":1},{"type":"/datum/bt_node/ai_behavior/look_to_parent","parent_key":"BB_found_mom"}]}],"cooldown_key":"BB_parent_emote_cooldown","cooldown_duration":60}],"observer_abort":2,"key":"BB_found_mom"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/ai_behavior/find_mom","mom_types_key":"BB_find_mom_types","ignore_types_key":"BB_ignore_mom_types","found_mom_key":"BB_found_mom"},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/penguin"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.compiled.json new file mode 100644 index 000000000000..3290bdc5f1fa --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"friendly_cultist","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/befriend_target","target_key":"friendly_cultist","befriend_message":"friendly_message"}]}],"key":"friendly_cultist","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"occupied_rune","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/activate_rune"}]}],"key":"occupied_rune","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"dead_cultist","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/drag_target","target_key":"dead_cultist"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"nearby_rune","required_dist":0,"finish_on_arrival":false},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"dead_cultist"}]}],"key":"dead_cultist","observer_abort":3},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"friendly_cultist","target_source":"/datum/target_source/oview_single_type/carbon_mob","targeting_strategy":"/datum/targeting_strategy/befriendable_cultist","vision_range":9,"time_between_perform":50},{"type":"/datum/bt_node/ai_behavior/find_occupied_rune"},{"type":"/datum/bt_node/ai_behavior/find_dead_cultist"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/pets/sloth.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/pets/sloth.bt.compiled.json new file mode 100644 index 000000000000..814ac30820ad --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/pets/sloth.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/subtree/climb_tree"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_climbed_tree","target_source":"/datum/target_source/oview_single_type/flora_tree","targeting_strategy":"/datum/targeting_strategy/anything"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/revolutionary.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/revolutionary.bt.compiled.json new file mode 100644 index 000000000000..e717c859fade --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/revolutionary.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_hostile_combat","bindings":{"bbrbyj7y":"/datum/bt_node/ai_behavior/random_speech_blackboard"}}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.compiled.json new file mode 100644 index 000000000000..311240eadb5d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/ai_behavior/wait"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_lightning_strike","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_cybersun_barrage","target_key":"Current Target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.compiled.json new file mode 100644 index 000000000000..a67dcbcb128d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","time_between_perform":6.0,"target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target","approach_movement_type":"/datum/ai_movement/basic_avoidance"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.compiled.json new file mode 100644 index 000000000000..8a767cda2251 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/living_floor.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/living_floor.bt.compiled.json new file mode 100644 index 000000000000..42ff9194db7f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/living_floor.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"target_key":"Current Target","maximum_distance":0}],"success_policy":1,"failure_policy":1}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","vision_range":2}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.compiled.json new file mode 100644 index 000000000000..0643dd9bec54 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat","bindings":{"bp3p5vvb":80}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.compiled.json new file mode 100644 index 000000000000..c3c0eb4d8492 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","key":"BB_current_hunting_target","targeting_strategy":"/datum/targeting_strategy/anything","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/move_to_target","key":"BB_current_hunting_target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_current_hunting_target"}]}],"key":"BB_GUNMIMIC_GUN_EMPTY","invert":true}],"observer_abort":3,"key":"BB_current_hunting_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest","key":"BB_current_hunting_target","targeting_strategy":"/datum/targeting_strategy/anything","target_source":"/datum/target_source/animatable_objects","vision_range":7,"revalidation_mode":3}],"key":"BB_GUNMIMIC_GUN_EMPTY","invert":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.compiled.json new file mode 100644 index 000000000000..68d1b491e28f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":0,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.compiled.json new file mode 100644 index 000000000000..b9fb9ac2f16e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/simple_hostile_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.compiled.json new file mode 100644 index 000000000000..da1e8d7c186c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/avoid_friendly_fire","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_GUNMIMIC_GUN_EMPTY","invert":true},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/skeleton.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/skeleton.bt.compiled.json new file mode 100644 index 000000000000..dc27d1b52653 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/skeleton.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"rattles"}],"chance":0.2}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman.bt.compiled.json new file mode 100644 index 000000000000..8a767cda2251 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.compiled.json new file mode 100644 index 000000000000..6d376bbd6511 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_ranged_combat","bindings":{"bpnckxev":50}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.compiled.json new file mode 100644 index 000000000000..3b31b3414591 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_targeted_spell","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_secondary_spell","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_wizard_blink_spell","target_key":"Current Target"}]}],"cooldown_key":"BB_wizard_spell_cooldown","cooldown_duration":10}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"invert":false,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie.bt.compiled.json new file mode 100644 index 000000000000..83e682656a60 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.compiled.json new file mode 100644 index 000000000000..9c3ea45a3502 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/simple_hostile_combat"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.compiled.json new file mode 100644 index 000000000000..28e715a7375e --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/feed_on_slime_target","target_key":"BB_current_pet_target"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_pet_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_current_pet_target","observer_abort":3}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/slime/ai/slime.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/slime/ai/slime.bt.compiled.json new file mode 100644 index 000000000000..67621bb800c5 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/slime/ai/slime.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_slime_evolve"}],"ability_key":"BB_slime_evolve"},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_slime_reproduce"}],"observer_abort":2,"ability_key":"BB_slime_reproduce"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/feed_on_slime_target","target_key":"BB_slime_eat_target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"BB_slime_eat_target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_slime_eat_target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"BB_slime_eat_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/feed_on_slime_target","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":false},{"type":"/datum/bt_node/decorator/slime_wants_to_eat","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_slime_eat_target","targeting_strategy":"/datum/targeting_strategy/slime_food","target_source":"/datum/target_source/oview_living_no_slimes","vision_range":7,"must_be_reachable":true}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/change_slime_face"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/snails/snail.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/snails/snail.bt.compiled.json new file mode 100644 index 000000000000..997827fa395f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/snails/snail.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"snail_retreat_ability"}],"success_policy":1,"failure_policy":1}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ant.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ant.bt.compiled.json new file mode 100644 index 000000000000..69269d3f36fd --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ant.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/bear/bear.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/bear/bear.bt.compiled.json new file mode 100644 index 000000000000..8c4e6dfba042 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/bear/bear.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/subtree/climb_tree"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_found_honey","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/find_hive","target_key":"BB_found_honey","cooldown_key":"BB_bear_hive_cooldown","hunt_cooldown":50}]}],"key":"BB_found_honey","observer_abort":3},{"type":"/datum/bt_node/decorator/is_dragging","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/pull_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_bear_honeycomb_cooldown","hunt_cooldown":50}]}],"key":"BB_low_priority_hunting_target","observer_abort":3}],"observer_abort":1,"invert":true},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_climbed_tree","target_source":"/datum/target_source/oview_single_type/flora_tree","targeting_strategy":"/datum/targeting_strategy/anything"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_found_honey","target_source":"/datum/target_source/oview_single_type/beehive","targeting_strategy":"/datum/targeting_strategy/stocked_beehive","vision_range":10}],"cooldown_key":"BB_bear_hive_cooldown"},{"type":"/datum/bt_node/decorator/is_dragging","children":[{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/honeycomb","targeting_strategy":"/datum/targeting_strategy/huntable","vision_range":10}],"cooldown_key":"BB_bear_honeycomb_cooldown"}],"invert":true},{"type":"/datum/bt_node/ai_behavior/random_speech/bear"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.compiled.json new file mode 100644 index 000000000000..50293ab32886 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"$be5c4p10"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/carp_flee","bindings":{"bf3k9q2p":"$b7cm2s3z"}},{"type":"$bc0mb4t9"}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"$bid1e6k3"},{"type":"/datum/bt_node/subtree/random_walk"}]}]},{"type":"$bf1nd7r2"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/find_magicarp_spell_target","ability_key":"BB_magicarp_spell","target_key":"BB_magicarp_spell_target","targeting_strategy_key":"targeting_strategy"}],"key":"BB_magicarp_spell"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"__bindings":{"be5c4p10":{"label":"escape_tree","default":"/datum/bt_node/subtree"},"bf1nd7r2":{"label":"find_targets_tree","default":"/datum/bt_node/subtree/carp_target_selection"},"bc0mb4t9":{"label":"combat_tree","default":"/datum/bt_node/subtree/carp_combat"},"bid1e6k3":{"label":"idle_tree","default":"/datum/bt_node/subtree/carp_migration"},"b7cm2s3z":{"label":"flee_from_key","default":"Current Target"}}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp.bt.compiled.json new file mode 100644 index 000000000000..f1ea869828c9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_carp_tree","bindings":{"be5c4p10":"/datum/bt_node/subtree/escape_captivity"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.compiled.json new file mode 100644 index 000000000000..b44fb494abd2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target","ability_key":"BB_magicarp_spell","target_key":"BB_magicarp_spell_target"}],"ability_key":"BB_magicarp_spell"}],"key":"BB_magicarp_spell_target"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target","time_between_perform":15.0},{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/make_carp_rift/towards/aggressive","ability_key":"BB_carp_rift","target_key":"Current Target"}],"ability_key":"BB_carp_rift"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.compiled.json new file mode 100644 index 000000000000..b50eacda3733 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/carp_should_flee","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/make_carp_rift/away","ability_key":"BB_carp_rift","target_key":"Current Target"}],"ability_key":"BB_carp_rift"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/find_flee_location","target_key":"$bf3k9q2p","hiding_location_key":"Current Target Hiding Location","destination_key":"BB_flee_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_flee_location","required_dist":0,"finish_on_arrival":true}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"target_key":"Current Target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.compiled.json new file mode 100644 index 000000000000..3d9462e4bbaa --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_carp_rift_shortcut/nearby","destination_key":"BB_carp_rift_destination"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_carp_rift_destination","required_dist":0,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_carp_rift_destination"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/carp_path_blocked","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/make_carp_rift/towards/unvalidated","ability_key":"BB_carp_rift","target_key":"BB_carp_migration_target"}],"ability_key":"BB_carp_rift"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"BB_carp_migration_target","time_between_perform":15.0}]}],"target_key":"BB_carp_migration_target"},{"type":"/datum/bt_node/ai_behavior/succeed"}]},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_carp_migration_target","required_dist":3,"finish_on_arrival":true},{"type":"/datum/bt_node/ai_behavior/clear_arrived_migration_target","target_key":"BB_carp_migration_target"}]}],"key":"BB_carp_migration_target"},{"type":"/datum/bt_node/ai_behavior/find_next_carp_migration_step","path_key":"BB_carp_migration_path","target_key":"BB_carp_migration_target"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.compiled.json new file mode 100644 index 000000000000..56f25cf96ecb --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_carp_tree","bindings":{"bf1nd7r2":"/datum/bt_node/subtree/carp_passive_selection","b7cm2s3z":"BB_basic_flee_target"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.compiled.json new file mode 100644 index 000000000000..71ffcc900036 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location","check_faction":false}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.compiled.json new file mode 100644 index 000000000000..736b43c21acb --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_carp_tree","bindings":{"bf1nd7r2":"/datum/bt_node/subtree/carp_retaliate_selection","bc0mb4t9":"/datum/bt_node/subtree/carp_retaliate_selection","bid1e6k3":"/datum/bt_node/subtree"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.compiled.json new file mode 100644 index 000000000000..f1ea869828c9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/basic_carp_tree","bindings":{"be5c4p10":"/datum/bt_node/subtree/escape_captivity"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.compiled.json new file mode 100644 index 000000000000..c9ac61faf9b8 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":false} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.compiled.json new file mode 100644 index 000000000000..4727d5ac7e33 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"key":"BB_basic_stop_fleeing","invert":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.compiled.json new file mode 100644 index 000000000000..3594068e0ce9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat_with_retaliate"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.compiled.json new file mode 100644 index 000000000000..9dbb6fb090f0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/random_walk"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.compiled.json new file mode 100644 index 000000000000..0889c9e9f359 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/find_target_facing_turf","target_key":"Current Target","set_key":"BB_glare_position"},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_glare_position","required_dist":0},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_glare_ability","target_key":"Current Target"}]}],"observer_abort":3,"ability_key":"BB_glare_ability"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_blind_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/heal_eye_damage","target_key":"BB_blind_target"}]}],"observer_abort":3,"key":"BB_blind_target"},{"type":"/datum/bt_node/subtree/move_to_and_hunt","bindings":{"bqwjf4id":20,"b3cnse9r":1}},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_blind_target","target_source":"/datum/target_source/oview_single_type/carbon_mob","targeting_strategy":"/datum/targeting_strategy/damaged_eyes","vision_range":9},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/carrot","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":6}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/faithless.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/faithless.bt.compiled.json new file mode 100644 index 000000000000..4bfe6e5b1d87 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/faithless.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"BB_low_priority_hunting_target","always_reset_target":true}]}],"observer_abort":3,"key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_typed/from_bb_key/hunt_target_list","targeting_strategy":"/datum/targeting_strategy/unbroken_light","vision_range":7},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/faithless"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.compiled.json new file mode 100644 index 000000000000..73f73c702cb7 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","check_faction":"$b2jvnm5d"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/random_speech/garden_gnome"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ghost.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ghost.bt.compiled.json new file mode 100644 index 000000000000..15c3371800ee --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/ghost.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/simple_retaliate_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.compiled.json new file mode 100644 index 000000000000..5e3ae60adf83 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/relay_to_hive_partner"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_hive_partner","target_source":"/datum/target_source/oview_single_type/hivebot","targeting_strategy":"/datum/targeting_strategy/living_not_dead","vision_range":10}],"chance":0.1}],"success_policy":0,"failure_policy":0,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.compiled.json new file mode 100644 index 000000000000..80226ce0624a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_machine_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/repair_machines","target_key":"BB_machine_target"}]}],"observer_abort":2,"key":"BB_machine_target"},{"type":"/datum/bt_node/subtree/relay_to_hive_partner"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_machine_target","target_source":"/datum/target_source/oview_single_type/machine","targeting_strategy":"/datum/targeting_strategy/damaged_machine","vision_range":10},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_hive_partner","target_source":"/datum/target_source/oview_single_type/hivebot","targeting_strategy":"/datum/targeting_strategy/living_not_dead","vision_range":10}],"key":"BB_hive_partner","invert":true}],"chance":0.1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.compiled.json new file mode 100644 index 000000000000..768d4f7c1f9c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","time_between_perform":30,"avoid_friendly_fire":true}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/relay_to_hive_partner"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_hive_partner","target_source":"/datum/target_source/oview_single_type/hivebot","targeting_strategy":"/datum/targeting_strategy/living_not_dead","vision_range":10}],"key":"BB_hive_partner","invert":true}],"chance":0.1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.compiled.json new file mode 100644 index 000000000000..54c129760abd --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","time_between_perform":15.0,"avoid_friendly_fire":true}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/relay_to_hive_partner"},{"type":"/datum/bt_node/subtree/random_walk"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_hive_partner","target_source":"/datum/target_source/oview_single_type/hivebot","targeting_strategy":"/datum/targeting_strategy/living_not_dead","vision_range":10}],"key":"BB_hive_partner","invert":true}],"chance":0.1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.compiled.json new file mode 100644 index 000000000000..7f515ce5ab4c --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_hive_partner","required_dist":1},{"type":"/datum/bt_node/ai_behavior/relay_message","target_key":"BB_hive_partner"},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_hive_partner"}]}],"observer_abort":2,"key":"BB_hive_partner"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.compiled.json new file mode 100644 index 000000000000..80638d2c1c57 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat","bindings":{"bqul7l8t":"/datum/bt_node/subtree/random_speech_loop"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/lightgeist.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/lightgeist.bt.compiled.json new file mode 100644 index 000000000000..8a767cda2251 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/lightgeist.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.compiled.json new file mode 100644 index 000000000000..d7f083f2c7d3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_meteor_ground_spikes","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_meteor_spine_traps"}]}],"success_policy":1,"failure_policy":1}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/ai_behavior/meteor_heart_deaggro"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/morph.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/morph.bt.compiled.json new file mode 100644 index 000000000000..3594068e0ce9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/morph.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_combat_with_retaliate"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/mushroom.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/mushroom.bt.compiled.json new file mode 100644 index 000000000000..a3ff530bcf92 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/mushroom.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_hunt","bindings":{"bvtz06kb":"BB_low_priority_hunting_target","b3y599q4":"BB_low_priority_hunting_target","brrasnah":"BB_low_priority_hunting_target","bd1towgc":1,"b3cnse9r":1}},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_single_type/mushroom_food","vision_range":6}],"key":"BB_low_priority_hunting_target","invert":true}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.compiled.json new file mode 100644 index 000000000000..6e28b2abdab3 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_summon_mimics","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_summon_minions"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/find_paper_and_write"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_found_paper","target_source":"/datum/target_source/oview_single_type/paper","targeting_strategy":"/datum/targeting_strategy/empty_paper","time_between_perform":100}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.compiled.json new file mode 100644 index 000000000000..2c2d16007fc6 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_raise_horde_ability","target_key":"BB_basic_flee_target"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"BB_basic_flee_target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"BB_basic_flee_target","targeting_strategy":"targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"BB_basic_flee_target","observer_abort":3},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"},{"type":"/datum/bt_node/ai_behavior/use_mob_ability/domain","ability_key":"BB_domain_ability"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/roro.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/roro.bt.compiled.json new file mode 100644 index 000000000000..d94250485b84 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/roro.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_retaliate_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/banded.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/banded.bt.compiled.json new file mode 100644 index 000000000000..81aa18815bbd --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/banded.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/snake.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/snake.bt.compiled.json new file mode 100644 index 000000000000..060d0fd30425 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/snake/snake.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spaceman.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spaceman.bt.compiled.json new file mode 100644 index 000000000000..fa7d52228081 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spaceman.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree/simple_retaliate_combat"}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.compiled.json new file mode 100644 index 000000000000..cfd354a3a885 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_spider_web_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/spin_web","action_key":"BB_spider_web_action","target_key":"BB_spider_web_target"}]}],"observer_abort":3,"key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_unwebbed_turf","target_key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.compiled.json new file mode 100644 index 000000000000..69b4956b49ca --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}}],"observer_abort":2,"key":"BB_basic_flee_target"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_spider_web_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/spin_web","action_key":"BB_spider_web_action","target_key":"BB_spider_web_target"}]}],"observer_abort":3,"key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"BB_basic_flee_target_hiding_location"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_unwebbed_turf","target_key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.compiled.json new file mode 100644 index 000000000000..7fbac82cdd81 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.compiled.json new file mode 100644 index 000000000000..16995a0eb626 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_spider_web_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/spin_web","action_key":"BB_spider_web_action","target_key":"BB_spider_web_target"}]}],"observer_abort":3,"key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_unwebbed_turf","target_key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.compiled.json new file mode 100644 index 000000000000..6989bddc89c9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/consider_venting"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"observer_abort":3,"invert":false,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_entry_vent_target","target_source":"/datum/target_source/oview_single_type/vent_pump","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.compiled.json new file mode 100644 index 000000000000..7ce03eefeee0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"Current Target"}},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_spider_web_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/spin_web","action_key":"BB_spider_web_action","target_key":"BB_spider_web_target"}]}],"observer_abort":3,"key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/find_unwebbed_turf","target_key":"BB_spider_web_target"}]},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.compiled.json new file mode 100644 index 000000000000..3cc7fd46b43b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/face_target_or_face_initial","target_key":"Current Target"}],"observer_abort":2,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/statue.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/statue.bt.compiled.json new file mode 100644 index 000000000000..daa9321f79c2 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/statue.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","movement_failed":false},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"BB_low_priority_hunting_target","hunt_cooldown":100,"always_reset_target":true}]}],"observer_abort":3,"key":"BB_low_priority_hunting_target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"}],"observer_abort":3,"invert":true,"key":"BB_low_priority_hunting_target"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_typed/from_bb_key/hunt_target_list","targeting_strategy":"/datum/targeting_strategy/unbroken_light","vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.compiled.json new file mode 100644 index 000000000000..39d4a08e4edc --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/ai_behavior/wait"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/wait"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","vision_range":14},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/perform_emote","emote":"scream"}],"chance":0.05}],"observer_abort":0,"key":"Current Target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.compiled.json new file mode 100644 index 000000000000..1b78b4ebaf44 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat","bindings":{"b95z0f0c":"/datum/bt_node/subtree/random_speech_loop"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.compiled.json new file mode 100644 index 000000000000..6c6daa9752af --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/targeted_mob_ability","ability_key":"BB_fugu_inflate","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/attack_obstructions/attack_turfs","target_key":"Current Target"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/subtree/run_away_from_target"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/stoats/stoat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/stoats/stoat.bt.compiled.json new file mode 100644 index 000000000000..8e3ac45c42c9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/stoats/stoat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":0,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/make_babies"},{"type":"/datum/bt_node/decorator/is_dragging","children":[{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_LAST_STOLEN_ITEM"}}],"observer_abort":1},{"type":"/datum/bt_node/subtree/steal_and_flee"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/random_speech_loop"},{"type":"/datum/bt_node/subtree/find_stealable_object"},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/subtree/find_partner"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trader/trader.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trader/trader.bt.compiled.json new file mode 100644 index 000000000000..e7b5d0ea2ad0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trader/trader.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity/pacifist"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","avoid_friendly_fire":true,"time_between_perform":30}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_first_customer","required_dist":1}],"key":"BB_rush_to_sell","value":true},{"type":"/datum/bt_node/ai_behavior/succeed"}]},{"type":"/datum/bt_node/ai_behavior/setup_shop"}]}],"observer_abort":2,"key":"BB_first_customer"}],"observer_abort":0,"invert":true,"key":"BB_shop_spot"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk/not_while_on_target","target_key":"BB_shop_spot"}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target","target_key":"BB_first_customer","target_source":"/datum/target_source/oview","targeting_strategy":"/datum/targeting_strategy/capable_human","revalidation_mode":1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/tree.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/tree.bt.compiled.json new file mode 100644 index 000000000000..1b78b4ebaf44 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/tree.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_hostile_obstacles_combat","bindings":{"b95z0f0c":"/datum/bt_node/subtree/random_speech_loop"}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/burst.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/burst.bt.compiled.json new file mode 100644 index 000000000000..a2f6923b6f4d --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/burst.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/trooper_ranged","bindings":{"b4kar3zl":30,"bnr1aazo":1}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful.bt.compiled.json new file mode 100644 index 000000000000..1f531427f00b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements","target_key":"Call reinforcements target"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true}],"observer_abort":2,"key":"Call reinforcements target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Call reinforcements target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful_burst.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful_burst.bt.compiled.json new file mode 100644 index 000000000000..52da7f12e760 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/peaceful_burst.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements","target_key":"Call reinforcements target"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true}],"observer_abort":2,"key":"Call reinforcements target"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","time_between_perform":30,"avoid_friendly_fire":true}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Call reinforcements target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/ranged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/ranged.bt.compiled.json new file mode 100644 index 000000000000..eb5c181ecd68 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/trooper_ranged","bindings":{"b4kar3zl":10,"beslksyc":5,"bnr1aazo":1}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/shotgunner.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/shotgunner.bt.compiled.json new file mode 100644 index 000000000000..dbcb92512680 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/shotgunner.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/trooper_ranged","bindings":{"b4kar3zl":30,"beslksyc":3,"bnr1aazo":1}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper.bt.compiled.json new file mode 100644 index 000000000000..644c7eefb550 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper_ranged.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper_ranged.bt.compiled.json new file mode 100644 index 000000000000..e60f01c3b793 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/trooper/trooper_ranged.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/cooldown","children":[{"type":"/datum/bt_node/ai_behavior/call_reinforcements"}],"observer_abort":2,"cooldown_key":"BB_basic_mob_reinforcements_cooldown","cooldown_duration":300,"lock_on_succeed":true}],"key":"BB_calls_reinforcements","value":true},{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location","time_between_perform":"$b4kar3zl","max_range":"$beslksyc","avoid_friendly_fire":"$bnr1aazo"},{"type":"/datum/bt_node/ai_behavior/succeed"}]}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/move_to_reinforce"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"__bindings":{"b4kar3zl":{"label":"time_between_perform","default":0},"beslksyc":{"label":"max_range","default":3},"bnr1aazo":{"label":"avoid_friendly_fire","default":0}}} diff --git a/build/behavior_trees/code/modules/mob/living/basic/turtle/turtle.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/turtle/turtle.bt.compiled.json new file mode 100644 index 000000000000..2a5a1630c166 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/turtle/turtle.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/ability_available","children":[{"type":"/datum/bt_node/ai_behavior/use_mob_ability","ability_key":"BB_generic_action"}],"observer_abort":2,"ability_key":"BB_generic_action"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"turtle_headbutt_victim","required_dist":1},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"turtle_headbutt_cooldown","cooldown_duration":600},{"type":"/datum/bt_node/ai_behavior/hunt_target/headbutt_leg","target_key":"turtle_headbutt_victim","always_reset_target":true}]}],"observer_abort":2,"key":"turtle_headbutt_victim"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"turtle_flora_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/set_bb_cooldown","cooldown_key":"turtle_flora_cooldown","cooldown_duration":600},{"type":"/datum/bt_node/ai_behavior/hunt_target/sniff_flora","target_key":"turtle_flora_target","always_reset_target":true}]}],"observer_abort":2,"key":"turtle_headbutt_victim"},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk","bindings":{"bf07i8ep":10}}]},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"turtle_headbutt_victim","target_source":"/datum/target_source/oview_typed/from_bb_key/turtle_headbutt_types","targeting_strategy":"/datum/targeting_strategy/legged_conscious_human"}],"cooldown_key":"turtle_headbutt_cooldown"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"turtle_flora_target","target_source":"/datum/target_source/oview_typed/from_bb_key/turtle_flora_types","targeting_strategy":"/datum/targeting_strategy/sniffable_hydro"}],"cooldown_key":"turtle_flora_cooldown"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/express_happiness"}],"success_policy":1,"failure_policy":1,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/axolotl.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/axolotl.bt.compiled.json new file mode 100644 index 000000000000..9dbb6fb090f0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/axolotl.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/random_walk"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/butterfly.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/butterfly.bt.compiled.json new file mode 100644 index 000000000000..9dbb6fb090f0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/butterfly.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/random_walk"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.compiled.json new file mode 100644 index 000000000000..49087f9c3453 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_roach_hunt_cooldown","hunt_cooldown":50,"always_reset_target":true}]}],"key":"BB_low_priority_hunting_target","observer_abort":3},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/ants","targeting_strategy":"/datum/targeting_strategy/huntable","vision_range":2}],"cooldown_key":"BB_roach_hunt_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.compiled.json new file mode 100644 index 000000000000..509de755c2e4 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_roach_hunt_cooldown","hunt_cooldown":50,"always_reset_target":true}]}],"key":"BB_low_priority_hunting_target","observer_abort":3},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/ants","targeting_strategy":"/datum/targeting_strategy/huntable","vision_range":2}],"cooldown_key":"BB_roach_hunt_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.compiled.json new file mode 100644 index 000000000000..458fb780ce9f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_roach_hunt_cooldown","hunt_cooldown":50,"always_reset_target":true}]}],"key":"BB_low_priority_hunting_target","observer_abort":3},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/ants","targeting_strategy":"/datum/targeting_strategy/huntable","vision_range":2}],"cooldown_key":"BB_roach_hunt_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.compiled.json new file mode 100644 index 000000000000..d3b96084f49a --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_ranged_attack/mobroach","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/maintain_distance","target_key":"Current Target"}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","observer_abort":3},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_roach_hunt_cooldown","hunt_cooldown":50,"always_reset_target":true}]}],"key":"BB_low_priority_hunting_target","observer_abort":3},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/ants","targeting_strategy":"/datum/targeting_strategy/huntable","vision_range":2}],"cooldown_key":"BB_roach_hunt_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/crab.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/crab.bt.compiled.json new file mode 100644 index 000000000000..025b68b9301f --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/crab.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree/run_away_from_target","bindings":{"byk9gqj4":"BB_basic_flee_target"}},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/go_for_swim"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"BB_basic_flee_target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"key":"BB_basic_stop_fleeing","invert":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"swim_alternate_turf","targeting_strategy":"/datum/targeting_strategy/walkable_turf","target_source":"/datum/target_source/oview_water_turfs"}],"cooldown_key":"key_swimmer_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/crab"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cable.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cable.bt.compiled.json new file mode 100644 index 000000000000..74aa222412f0 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cable.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":0},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"BB_mouse_wants_to_eat_cable"},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"BB_low_priority_hunting_target","cooldown_key":"BB_mouse_cable_hunt_cooldown","hunt_cooldown":200}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/cable","targeting_strategy":"/datum/targeting_strategy/accessible_cable","vision_range":0}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"BB_low_priority_hunting_target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cheese.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cheese.bt.compiled.json new file mode 100644 index 000000000000..235e587e3b97 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/eat_cheese.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_current_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/interact_with_target","target_key":"BB_current_hunting_target","cooldown_key":"BB_mouse_cheese_hunt_cooldown","hunt_cooldown":200}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","targeting_strategy":"/datum/targeting_strategy/pickup_item","target_source":"/datum/target_source/oview_single_type/cheese","vision_range":1}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"BB_current_hunting_target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/frog.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/frog.bt.compiled.json new file mode 100644 index 000000000000..4f860c1f6df4 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/frog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/frog_engage_target"},{"type":"/datum/bt_node/subtree/go_for_swim"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"swim_alternate_turf","targeting_strategy":"/datum/targeting_strategy/walkable_turf","target_source":"/datum/target_source/oview_water_turfs"}],"cooldown_key":"key_swimmer_cooldown"},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/frog"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/frog_engage_target.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/frog_engage_target.bt.compiled.json new file mode 100644 index 000000000000..3df4a0ae1111 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/frog_engage_target.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_has_trait","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/find_flee_location","target_key":"Current Target","hiding_location_key":"Current Target Hiding Location","destination_key":"BB_flee_location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_flee_location","required_dist":0,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"key":"Current Target","trait":"scary_fisherman"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":true}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}]}],"observer_abort":3,"key":"Current Target"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/lizard.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/lizard.bt.compiled.json new file mode 100644 index 000000000000..3231de256acf --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/lizard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/subtree/find_food"},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/lizard"}}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.compiled.json new file mode 100644 index 000000000000..426dcea21279 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/override_id_set","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"}],"observer_abort":2,"override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"}],"key":"Current Target","observer_abort":1},{"type":"/datum/bt_node/subtree/move_to_and_eat"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest","target_key":"Current Target","targeting_strategy":"flee_targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/subtree/find_food"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/random_speech/mothroach"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse.bt.compiled.json new file mode 100644 index 000000000000..ea00144350e9 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/override_id_set","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"}],"observer_abort":2,"override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/eat_cheese"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/run_away_from_target"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","target_source":"/datum/target_source/oview_single_type/cheese","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":1}],"cooldown_key":"BB_mouse_cheese_hunt_cooldown"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/play_instrument_on_floor"},{"type":"/datum/bt_node/subtree/eat_cable"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/random_walk"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","target_source":"/datum/target_source/oview_single_type/cheese","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":1}],"cooldown_key":"BB_mouse_cheese_hunt_cooldown"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_SONG_INSTRUMENT","target_source":"/datum/target_source/oview_single_type/piano_synth","targeting_strategy":"/datum/targeting_strategy/playable_synthesizer"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/cable","targeting_strategy":"/datum/targeting_strategy/accessible_cable","vision_range":0}],"key":"BB_mouse_wants_to_eat_cable"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]},{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/mouse"}},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_mouse_wants_to_eat_cable","value":true}],"chance":0.01}],"cooldown_key":"BB_mouse_cable_hunt_cooldown"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse_rat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse_rat.bt.compiled.json new file mode 100644 index 000000000000..b74136eee82b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/mouse_rat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/decorator/override_id_set","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"}],"observer_abort":2,"override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/ai_behavior/attack_obstructions","target_key":"Current Target"},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]}],"success_policy":0,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/eat_cheese"},{"type":"/datum/bt_node/subtree/eat_cable"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_current_hunting_target","target_source":"/datum/target_source/oview_single_type/cheese","targeting_strategy":"/datum/targeting_strategy/anything","vision_range":1}],"cooldown_key":"BB_mouse_cheese_hunt_cooldown"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","target_source":"/datum/target_source/oview_single_type/cable","targeting_strategy":"/datum/targeting_strategy/accessible_cable","vision_range":0}],"key":"BB_mouse_wants_to_eat_cable"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/random_speech_loop","bindings":{"bqdqne64":"/datum/bt_node/ai_behavior/random_speech/mouse"}},{"type":"/datum/bt_node/decorator/key_off_cooldown","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/set_bb_key","target_key":"BB_mouse_wants_to_eat_cable","value":true}],"chance":0.01}],"cooldown_key":"BB_mouse_cable_hunt_cooldown"}]}],"success_policy":0,"failure_policy":0,"loop_delay":10}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.compiled.json new file mode 100644 index 000000000000..04e34b375de1 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/is_at_distance","children":[{"type":"/datum/bt_node/ai_behavior/keep_playing_instrument","song_instrument_key":"BB_SONG_INSTRUMENT"}],"observer_abort":1,"maximum_distance":1,"require_reach":true},{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_SONG_INSTRUMENT","required_dist":1},{"type":"/datum/bt_node/ai_behavior/setup_instrument","song_instrument_key":"BB_SONG_INSTRUMENT","song_lines_key":"song_lines"},{"type":"/datum/bt_node/ai_behavior/play_instrument","song_instrument_key":"BB_SONG_INSTRUMENT","volume":50}]}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_SONG_INSTRUMENT","target_source":"/datum/target_source/oview_single_type/piano_synth","targeting_strategy":"/datum/targeting_strategy/playable_synthesizer"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}],"observer_abort":3,"key":"BB_SONG_INSTRUMENT"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/space_bat.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/space_bat.bt.compiled.json new file mode 100644 index 000000000000..d94250485b84 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/space_bat.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree/simple_retaliate_combat"} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/suicide_frog.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/suicide_frog.bt.compiled.json new file mode 100644 index 000000000000..232470affa5b --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/suicide_frog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/code/modules/mob/living/basic/vermin/trash.bt.compiled.json b/build/behavior_trees/code/modules/mob/living/basic/vermin/trash.bt.compiled.json new file mode 100644 index 000000000000..f663b3d4ce78 --- /dev/null +++ b/build/behavior_trees/code/modules/mob/living/basic/vermin/trash.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/escape_captivity"},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/frog_engage_target"},{"type":"/datum/bt_node/subtree/random_walk"}]},{"type":"/datum/bt_node/ai_behavior/random_speech/frog"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.compiled.json b/build/behavior_trees/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.compiled.json new file mode 100644 index 000000000000..9ea2577af390 --- /dev/null +++ b/build/behavior_trees/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/decorator/is_dragging","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"BB_low_priority_hunting_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/hunt_target/pull_target","target_key":"BB_low_priority_hunting_target","hunt_cooldown":50}]}],"key":"BB_low_priority_hunting_target","observer_abort":3}],"observer_abort":3,"invert":true},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/subtree/dog_harassment"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/idle_dog"}],"success_policy":1,"failure_policy":0},{"type":"/datum/bt_node/subtree/random_speech_loop"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_interaction_target","target_key":"BB_low_priority_hunting_target","targeting_strategy":"/datum/targeting_strategy/huntable","target_source":"/datum/target_source/oview_typed/from_bb_key/hunt_target_list","vision_range":7}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/modular_darkpack/modules/beastmaster/code/beastmaster.bt.compiled.json b/build/behavior_trees/modular_darkpack/modules/beastmaster/code/beastmaster.bt.compiled.json new file mode 100644 index 000000000000..adc4945ab6de --- /dev/null +++ b/build/behavior_trees/modular_darkpack/modules/beastmaster/code/beastmaster.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/subtree","override_id":"pet_command"} diff --git a/build/behavior_trees/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.compiled.json b/build/behavior_trees/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.compiled.json new file mode 100644 index 000000000000..e43f7f8b8f18 --- /dev/null +++ b/build/behavior_trees/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/ai_behavior/tentacle_release_target","time_between_perform":5.0}],"observer_abort":2,"key":"BB_abyss_tentacle_mode","value":"Passive"},{"type":"/datum/bt_node/decorator/bb_key_true","children":[{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/ai_behavior/tentacle_crush_victim"}],"observer_abort":0,"key":"BB_abyss_tentacle_mode","value":"Aggressive"}],"observer_abort":0,"key":"BB_abyss_tentacle_grabbed"},{"type":"/datum/bt_node/decorator/bb_key_equals","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/ai_behavior/tentacle_grab_target"}],"observer_abort":3,"key":"Current Target"}],"observer_abort":0,"invert":true,"key":"BB_abyss_tentacle_mode","value":"Passive"}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/update_combat_targets","target_key":"Current Target","targeting_strategy":"targeting_strategy","vision_range":2,"hiding_location_key":"Current Target Hiding Location"}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true} diff --git a/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.compiled.json b/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.compiled.json new file mode 100644 index 000000000000..a2940becfe97 --- /dev/null +++ b/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/pawn_contained_in_obj","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/container_attackable","children":[{"type":"/datum/bt_node/ai_behavior/break_out_of_object/from_bb","target_key":"BB_basic_mob_escape_target"}]},{"type":"/datum/bt_node/ai_behavior/resist"}]}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_grabbed_by_enemy","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2},{"type":"/datum/bt_node/decorator/pawn_is_restrained","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"observer_abort":2}]},{"type":"/datum/bt_node/subtree","override_id":"pet_command"},{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/parallel","children":[{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/ai_behavior/basic_melee_attack","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}],"success_policy":1,"failure_policy":1},{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"Current Target","required_dist":1,"finish_on_arrival":false}],"failure_policy":1,"success_policy":0,"repeat_secondary":true,"finish_on_primary":true}],"observer_abort":3,"key":"Current Target"},{"type":"/datum/bt_node/composite/subplan","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/subtree/corvid_hoard"},{"type":"/datum/bt_node/ai_behavior/idle_random_walk"}]}],"success_policy":1,"failure_policy":0,"loop_delay":15.0}]},{"type":"/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list","target_key":"Current Target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"},{"type":"/datum/bt_node/ai_behavior/random_speech/corvid"},{"type":"/datum/bt_node/decorator/pawn_buckled_to_obj","children":[{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/resist"}],"chance":0.05}]}],"failure_policy":0,"success_policy":0,"repeat_secondary":true,"repeat_secondary_delay":10,"finish_on_primary":true}]} diff --git a/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.compiled.json b/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.compiled.json new file mode 100644 index 000000000000..b61e77c5149b --- /dev/null +++ b/build/behavior_trees/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.compiled.json @@ -0,0 +1 @@ +{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/decorator/target_is_holding_item","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"hoard_location","required_dist":1},{"type":"/datum/bt_node/ai_behavior/drop_all_held_items"}]}],"observer_abort":1,"key":"Literally me"},{"type":"/datum/bt_node/decorator/bb_key_set","children":[{"type":"/datum/bt_node/composite/selector","children":[{"type":"/datum/bt_node/composite/sequence","children":[{"type":"/datum/bt_node/ai_behavior/move_to_target","target_key":"hoard_item_target","required_dist":1},{"type":"/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot","target_key":"hoard_item_target","targeting_strategy":"targeting_strategy","hiding_location_key":"Current Target Hiding Location"}]},{"type":"/datum/bt_node/ai_behavior/clear_key","key":"hoard_item_target"}]}],"observer_abort":1,"key":"hoard_item_target"},{"type":"/datum/bt_node/decorator/random_chance","children":[{"type":"/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item","target_key":"hoard_item_target","vision_range":12}],"chance":0.05}]} diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index ec1a3ebbb134..d7337ed66951 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -144,10 +144,3 @@ /datum/controller/subsystem/ai_controllers/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/ai_controllers/##X -#define UNPLANNED_CONTROLLER_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/unplanned_controllers/##X);\ -/datum/controller/subsystem/unplanned_controllers/##X/New(){\ - NEW_SS_GLOBAL(SS##X);\ - PreInit();\ -}\ -/datum/controller/subsystem/unplanned_controllers/##X/fire() {..() /*just so it shows up on the profiler*/} \ -/datum/controller/subsystem/unplanned_controllers/##X diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm index 0254bfebff00..c3b175bfdef8 100644 --- a/code/__DEFINES/_flags.dm +++ b/code/__DEFINES/_flags.dm @@ -345,6 +345,18 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 /// Emote only prints to runechat, not to the chat window #define EMOTE_RUNECHAT (1<<3) +// Emote usability flags +/// Emote can be used while unconscious +#define EMOTE_CANUSE_UNCONSCIOUS (1<<0) +/// Emote can be used while in soft crit +#define EMOTE_CANUSE_SOFTCRIT (1<<1) +/// Emote can be used while in hard crit +#define EMOTE_CANUSE_HARDCRIT (1<<2) +/// Emote can be used while dead +#define EMOTE_CANUSE_DEAD (1<<3) +/// Emote requires that the user has at least one usable hand +#define EMOTE_CANUSE_REQUIRE_HANDS (1<<4) + // Flags for the empath component /// Can the empath see if a living mob has combat mode on #define EMPATH_SEE_COMBAT (1<<0) diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index a6724aeb4aae..a8c3371f5d21 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -1,16 +1,38 @@ -#define GET_AI_BEHAVIOR(behavior_type) SSai_behaviors.ai_behaviors[behavior_type] -#define GET_TARGETING_STRATEGY(targeting_type) SSai_behaviors.targeting_strategies[targeting_type] -#define GET_TARGET_PRIORITY_STRATEGY(targeting_type) SSai_behaviors.target_priority_strategies[targeting_type] +#define GET_TARGETING_STRATEGY(targeting_type) SSai_controllers.targeting_strategies[targeting_type] +#define GET_TARGET_PRIORITY_STRATEGY(targeting_type) SSai_controllers.target_priority_strategies[targeting_type] +#define GET_TARGET_SOURCE(source_type) SSai_controllers.target_sources[source_type] +/// Clears the blackboard latch used by a [/datum/bt_node/decorator/do_once]. +#define UNLOCK_DO_ONCE(controller, lock_key) ##controller.clear_blackboard_key(lock_key) + +/** + * Returns TRUE if the target should be rejected based on factions. + * Inlined faction check for targeting strategies but with less proc overhead. + * Strategies with fully custom faction logic set custom_faction_check and override faction_check() instead. + */ +#define TARGETING_FACTION_CHECK(strategy, controller, living_mob, the_target) \ + (((strategy).ignore_faction || (controller).blackboard[BB_ALWAYS_IGNORE_FACTION] || (controller).blackboard[BB_TEMPORARILY_IGNORE_FACTION]) \ + ? (strategy).invert_faction_check \ + : ((living_mob).faction_check_atom((the_target), exact_match = (strategy).check_factions_exactly) \ + ? !(strategy).invert_faction_check \ + : (strategy).invert_faction_check)) + +// Revalidation modes for /datum/bt_node/ai_behavior/acquire_target +/// If a target is already set, validate it via is_valid_target before searching. Replace if invalid. +#define TARGET_REVALIDATE 1 +/// If a target is already set, return SUCCESS immediately without re-checking. +#define TARGET_KEEP_IF_SET 2 +/// Always run the full candidate search, ignoring any existing target. +#define TARGET_ALWAYS_SEARCH 3 #define HAS_AI_CONTROLLER_TYPE(thing, type) istype(thing?.ai_controller, type) //AI controller flags //If you add a new status, be sure to add it to the ai_controllers subsystem's ai_controllers_by_status list. -///The AI is currently active. +///The AI is currently active, and is planned by the high priority subsystem. #define AI_STATUS_ON "ai_on" -///The AI is currently offline for any reason. +///The AI is currently active, but is planned by the low priority (background) subsystem. +#define AI_STATUS_ON_LOW "ai_on_low" +///The AI is not running. Cancels any active plans if set. #define AI_STATUS_OFF "ai_off" -///The AI is currently in idle mode. -#define AI_STATUS_IDLE "ai_idle" //Flags returned by get_able_to_run() ///pauses AI processing @@ -43,16 +65,6 @@ #define AI_BEHAVIOR_INSTANT (NONE) -///Does this task require movement from the AI before it can be performed? -#define AI_BEHAVIOR_REQUIRE_MOVEMENT (1<<0) -///Does this require the current_movement_target to be adjacent and in reach? -#define AI_BEHAVIOR_REQUIRE_REACH (1<<1) -///Does this task let you perform the action while you move closer? (Things like moving and shooting) -#define AI_BEHAVIOR_MOVE_AND_PERFORM (1<<2) -///Does finishing this task not null the current movement target? -#define AI_BEHAVIOR_KEEP_MOVE_TARGET_ON_FINISH (1<<3) -///Does this behavior NOT block planning? -#define AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION (1<<4) ///AI flags /// Don't move if being pulled @@ -65,27 +77,22 @@ #define CAN_ACT_IN_STASIS (1<<3) /// Continue processing while aggressively grabbed #define CAN_ACT_WHILE_GRABBED (1<<4) +/// Keeps planning on low prio when unwatched +#define RUN_WHILE_UNWATCHED (1<<5) +/// Always plans at high priority. Only works with RUN_WHILE_UNWATCHED +#define ALWAYS_HIGH_PRIORITY (1<<6) /// Flags we expect for most AI controllers #define DEFAULT_AI_FLAGS (PAUSE_DURING_DO_AFTER | CAN_ACT_WHILE_GRABBED) /// Flags for passive mobs that are easy to push around #define PASSIVE_AI_FLAGS (PAUSE_DURING_DO_AFTER | STOP_MOVING_WHEN_PULLED) -//Base Subtree defines - -///This subtree should cancel any further planning, (Including from other subtrees) -#define SUBTREE_RETURN_FINISH_PLANNING 1 - -//Generic subtree defines - /// default search range (tiles, passed to oview) when using find_and_set #define SEARCH_TACTIC_DEFAULT_RANGE 7 /// probability that the pawn should try resisting out of restraints #define RESIST_SUBTREE_PROB 50 ///macro for whether it's appropriate to resist right now, used by resist subtree #define SHOULD_RESIST(source) (source.on_fire || source.buckled || HAS_TRAIT(source, TRAIT_RESTRAINED) || (source.pulledby && source.pulledby.grab_state > GRAB_PASSIVE)) -///macro for whether the pawn can act, used generally to prevent some horrifying ai disasters -#define IS_DEAD_OR_INCAP(source) (source.incapacitated || source.stat) GLOBAL_LIST_INIT(all_radial_directions, list( "NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), @@ -97,3 +104,7 @@ GLOBAL_LIST_INIT(all_radial_directions, list( "WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST), "NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTHWEST) )) + + +///Use this if you dont want a controller to show up in the sidebar (e.g. when its a class that just sets BB keys) +#define ABSTRACT_AI_CLASS "Abstract" diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 21ed0c47aa18..bff772494fa9 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -1,5 +1,22 @@ //Generic BB keys +///Use this if you need a generic variable for a target; Use this if you don't have multiple different things to target in your ai (PROTIP: YOU BASICALLY NEVER DO!) +#define BB_CURRENT_TARGET "Current Target" +///Use this if you need a generic variable for a hiding location; +#define BB_CURRENT_TARGET_HIDING_LOCATION "Current Target Hiding Location" +///For any battle screech cooldowns +#define BB_BATTLE_SCREECH_COOLDOWN "Battle Screech Cooldown" +///Target for snitching (calling reinforcements on) but dont want to atatck. +#define BB_CALL_REINFORCEMENTS_TARGET "Call reinforcements target" +///Generic target for hunting +#define BB_HUNT_TARGET_LIST "Hunt Target List" +///Target of current movement.alist +#define BB_CURRENT_MOVEMENT_TARGET "Current target movement" +///The pawn controlled by this controller +#define BB_MY_PAWN "Literally me" + +///Cooldown on venting (sus) +#define BB_VENTING_COOLDOWN "Venting Cooldown" #define BB_CURRENT_MIN_MOVE_DISTANCE "min_move_distance" ///time until we should next eat, set by the generic hunger subtree @@ -30,13 +47,17 @@ #define BB_GUILTY_CONSCIOUS_CHANCE "guilty_concious_rate" ///the item we will steal #define BB_ITEM_TO_STEAL "item_to_steal" +///be stoat do crime +#define BB_WANTS_TO_COMMIT_THEFT "BB_WANTS_TO_COMMIT_THEFT" +///last stolen item +#define BB_LAST_STOLEN_ITEM "BB_LAST_STOLEN_ITEM" ///the owner we will try to play with #define BB_OWNER_TARGET "BB_owner_target" ///the list of interactions we can have with the owner #define BB_INTERACTIONS_WITH_OWNER "BB_interactions_with_owner" -///The trait checked by ai_behavior/find_potential_targets/prioritize_trait to return a target with a trait over the rest. +///The trait checked by ai_behavior/update_targets/prioritize_trait to return a target with a trait over the rest. #define BB_TARGET_PRIORITY_TRAIT "target_priority_trait" /// Store a single or list of emotes at this key @@ -48,6 +69,8 @@ #define BB_REINFORCEMENTS_SAY "BB_reinforcements_say" /// Something the mob will remote when calling reinforcements #define BB_REINFORCEMENTS_EMOTE "BB_reinforcements_emote" +/// Does this mob call reinforcements? +#define BB_CALLS_REINFORCEMENTS "BB_calls_reinforcements" ///Turf we want a mob to move to #define BB_TRAVEL_DESTINATION "BB_travel_destination" @@ -56,6 +79,8 @@ #define BB_SONG_INSTRUMENT "BB_SONG_INSTRUMENT" ///song lines blackboard, set by default on controllers #define BB_SONG_LINES "song_lines" +///mob doesn't use hand code so we're assuming its instrument is up its ass +#define BB_INSTRUMENT_UP_ASS "Has its instrument up its ass" ///bane ai used by example script #define BB_BANE_BATMAN "BB_bane_batman" @@ -66,6 +91,12 @@ /// Are we a panicking goose? #define BB_GOOSE_VOMIT_CHANCE "BB_goose_vomit_chance" +/// Set TRUE to suppress a mob's idle wandering (e.g. while under external/deadchat control). +#define BB_DISABLE_IDLE "BB_disable_idle" + +/// Set TRUE on a mob's controller by /datum/component/tameable once it has been tamed. +#define BB_TAMED "BB_tamed" + //Hunting BB keys ///key that holds our current hunting target #define BB_CURRENT_HUNTING_TARGET "BB_current_hunting_target" @@ -73,6 +104,14 @@ #define BB_LOW_PRIORITY_HUNTING_TARGET "BB_low_priority_hunting_target" ///key that holds the cooldown for our hunting subtree #define BB_HUNTING_COOLDOWN(type) "BB_HUNTING_COOLDOWN_[type]" +///cooldown key for the cockroach's ant hunt +#define BB_ROACH_HUNT_COOLDOWN "BB_roach_hunt_cooldown" +///cooldown key for the mouse's cheese hunt +#define BB_MOUSE_CHEESE_HUNT_COOLDOWN "BB_mouse_cheese_hunt_cooldown" +///cooldown key for the mouse's cable hunt +#define BB_MOUSE_CABLE_HUNT_COOLDOWN "BB_mouse_cable_hunt_cooldown" +///Whether we're hungry for a cable +#define BB_MOUSE_WANTS_TO_EAT_CABLE "BB_mouse_wants_to_eat_cable" ///Basic Mob Keys @@ -81,18 +120,19 @@ /// Key used to store the time we can actually attack #define BB_BASIC_MOB_MELEE_COOLDOWN_TIMER "BB_basic_melee_cooldown_timer" -///Targeting subtrees -#define BB_BASIC_MOB_CURRENT_TARGET "BB_basic_current_target" -#define BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION "BB_basic_current_target_hiding_location" #define BB_TARGETING_STRATEGY "targeting_strategy" #define BB_TARGET_PRIORITY_STRATEGY "target_priority_strategy" #define BB_HUNT_TARGETING_STRATEGY "hunt_targeting_strategy" ///some behaviors that check current_target also set this on deep crit mobs #define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target" +/// Target atom written by escape-captivity BT decorators (pawn_buckled_to_obj, pawn_contained_in_obj) +#define BB_BASIC_MOB_ESCAPE_TARGET "BB_basic_mob_escape_target" ///Blackboard key for a whitelist typecache of "things we can target while trying to move" #define BB_OBSTACLE_TARGETING_WHITELIST "BB_targeting_whitelist" -/// Key for the minimum status at which we want to target mobs (does not need to be specified if CONSCIOUS) +/// Key for the minimum status at which we want to target mobs (does not need to be specified if STABLE) #define BB_TARGET_MINIMUM_STAT "BB_target_minimum_stat" +/// If set, unconscious mobs will be treated as hard crit for the purposes of targeting and attacking. (So you can have a mob fooled by someone sleeping) +#define BB_TREAT_UNCONSCIOUS_AS_HARDCRIT "BB_treat_unconscious_as_hardcrit" /// Flag for whether to target only wounded mobs #define BB_TARGET_WOUNDED_ONLY "BB_target_wounded_only" /// What typepath the holding object targeting strategy should look for @@ -101,6 +141,9 @@ #define BB_BASIC_MOB_IDLE_WALK_CHANCE "BB_basic_idle_walk_chance" #define BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN "BB_basic_mob_target_refresh_cooldown" +/// whether we can have fuck +#define BB_FUCKS "can we fuck?" + /// Minimum range to keep target within #define BB_RANGED_SKIRMISH_MIN_DISTANCE "BB_ranged_skirmish_min_distance" /// Maximum range to keep target within @@ -115,6 +158,8 @@ #define BB_FLEE_TARGETING_STRATEGY "flee_targeting_strategy" #define BB_BASIC_MOB_FLEE_DISTANCE "BB_basic_flee_distance" #define DEFAULT_BASIC_FLEE_DISTANCE 9 +/// Computed flee destination turf set by find_flee_location and consumed by move_to_target +#define BB_FLEE_LOCATION "BB_flee_location" /// Generic key for a non-specific targeted action #define BB_TARGETED_ACTION "BB_TARGETED_action" @@ -189,7 +234,7 @@ ///are we in some kind of temporary state of ignoring factions when targeting? can result in volatile results if multiple behaviours touch this #define BB_TEMPORARILY_IGNORE_FACTION "BB_temporarily_ignore_factions" -///currently only used by clowns, a list of what can the mob speak randomly +///A list of what can the mob speak randomly #define BB_BASIC_MOB_SPEAK_LINES "BB_speech_lines" #define BB_EMOTE_SAY "emote_say" #define BB_EMOTE_HEAR "emote_hear" @@ -224,6 +269,9 @@ ///key that holds the next time we will start fishing #define BB_FISHING_TIMER "BB_fishing_timer" +///key that holds the next time we will start drilling ice +#define BB_ICE_DRILLING_TIMER "BB_ice_drilling_timer" + ///are we ONLY allowed to fish when we're hungry? #define BB_ONLY_FISH_WHILE_HUNGRY "BB_only_fish_while_hungry" @@ -241,7 +289,7 @@ // Keys used by one and only one behavior // Used to hold state without making bigass lists -/// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently +/// For /datum/ai_behavior/update_targets, what if any field are we using currently #define BB_FIND_TARGETS_FIELD(type) "bb_find_targets_field_[type]" ///Currently enraged diff --git a/code/__DEFINES/ai/behavior_trees.dm b/code/__DEFINES/ai/behavior_trees.dm new file mode 100644 index 000000000000..b576c2b644a5 --- /dev/null +++ b/code/__DEFINES/ai/behavior_trees.dm @@ -0,0 +1,74 @@ +/// Maximum number of execution indices the bt_viewer draining log can hold between polls. +#define BT_EXECUTION_LOG_MAX 250 + +// BT node return values +/// Node completed its goal and succeeded +#define BT_SUCCESS 1 +/// Node failed for one reason or the other +#define BT_FAILURE 2 +/// Node has an action running; +#define BT_RUNNING 3 + +// Parallel node completion policies (mutually exclusive per axis) +/// Parallel succeeds when child 1 succeeds (default) +#define BT_PARALLEL_SUCCESS_CHILD_ONE 0 +/// Parallel succeeds only when all children succeed +#define BT_PARALLEL_SUCCESS_ALL 1 +/// Parallel fails when child 1 fails (default) +#define BT_PARALLEL_FAILURE_CHILD_ONE 0 +/// Parallel fails when any child fails +#define BT_PARALLEL_FAILURE_ANY 1 + +/// Subplan propagates BT_SUCCESS when all children succeed (default) +#define BT_SUBPLAN_SUCCEED_ON_SUCCESS 0 +/// Subplan resets and loops (returns BT_RUNNING) when all children succeed +#define BT_SUBPLAN_LOOP_ON_SUCCESS 1 +/// Subplan propagates BT_FAILURE when a child fails (default) +#define BT_SUBPLAN_FAIL_ON_FAILURE 0 +/// Subplan resets and loops (returns BT_RUNNING) when a child fails +#define BT_SUBPLAN_LOOP_ON_FAILURE 1 + +/// No observer abort registered +#define BT_ABORT_NONE 0 +/// Abort this branch when the watched condition becomes FALSE +#define BT_ABORT_SELF (1<<0) +/// Abort lower-priority running behaviors when the watched condition becomes TRUE +#define BT_ABORT_LOWER_PRIORITY (1<<1) +/// Both BT_ABORT_SELF and BT_ABORT_LOWER_PRIORITY +#define BT_ABORT_BOTH (BT_ABORT_SELF | BT_ABORT_LOWER_PRIORITY) + +// BT viewer node type identifiers (stored on bt_node.node_type) +/// Selector composite node +#define BT_NODE_SELECTOR 0 +/// Sequence composite node +#define BT_NODE_SEQUENCE 1 +/// Parallel composite node +#define BT_NODE_PARALLEL 2 +/// Decorator (gate/condition) node +#define BT_NODE_DECORATOR 3 +/// Leaf behavior node +#define BT_NODE_LEAF 4 +/// Subtree container node +#define BT_NODE_SUBTREE 5 +/// Subplan composite node +#define BT_NODE_SUBPLAN 6 + +// Strings used to actually fill the JSONs for behavior trees +/// Key storing the node typepath in a descriptor list +#define BT_DESC_TYPE "type" +/// Key storing the children list in a descriptor list +#define BT_DESC_CHILDREN "children" +/// Key storing the override slot ID in a subtree descriptor +#define BT_DESC_OVERRIDE_ID "override_id" +/// Key storing bindable parameter declarations in a compiled subtree descriptor +#define BT_DESC_BINDINGS "__bindings" + +/// Resolves the compiled JSON path from a behavior tree's source JSON path (e.g. "code/datums/ai/dog/dog.bt.json" -> "build/behavior_trees/code/datums/ai/dog/dog.bt.compiled.json"). +#define BT_COMPILED_PATH(json_path) (replacetext("build/behavior_trees/" + json_path, ".json", ".compiled.json")) + +// Runtime subtree IDs. Can be used to override trees at runtime + +/// pet_command ID to override based on given pet command +#define SUBPLAN_ID_PET_COMMAND "pet_command" +/// Lets us override monkey combat behavior +#define SUBPLAN_ID_MONKEY_COMBAT "monkey_combat" diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm index 653bace09d8a..f134f088d29f 100644 --- a/code/__DEFINES/ai/bot_keys.dm +++ b/code/__DEFINES/ai/bot_keys.dm @@ -9,6 +9,8 @@ #define BB_SALUTE_MESSAGES "salute_messages" ///the beepsky we will salute #define BB_SALUTE_TARGET "salute_target" +///cooldown key for authority saluting +#define BB_SALUTE_COOLDOWN "salute_cooldown" ///our announcement ability #define BB_ANNOUNCE_ABILITY "announce_ability" ///list of our radio channels @@ -19,8 +21,6 @@ #define BB_BOT_BEACON_COOLDOWN "bot_beacon_cooldown" // medbot keys -///the patient we must heal -#define BB_PATIENT_TARGET "patient_target" ///list holding our wait dialogue #define BB_WAIT_SPEECH "wait_speech" ///what we will say to our patient after we heal them @@ -53,12 +53,13 @@ #define BB_CLEANBOT_EMAGGED_PHRASES "emagged_phrases" ///key that holds drawings we hunt #define BB_CLEANABLE_DRAWINGS "cleanable_drawings" -///Key that holds our clean target -#define BB_CLEAN_TARGET "clean_target" + ///key that holds the janitor we will befriend #define BB_FRIENDLY_JANITOR "friendly_janitor" ///key that holds the victim we will spray #define BB_ACID_SPRAY_TARGET "acid_spray_target" +///key that holds our acid spray attack cooldown +#define BB_ACID_SPRAY_COOLDOWN "acid_spray_cooldown" ///key that holds trash we will burn #define BB_HUNTABLE_TRASH "huntable_trash" @@ -75,6 +76,8 @@ #define BB_WASH_FRUSTRATION "wash_frustration" ///key that holds cooldown after we finish cleaning something, so we dont immediately run off to patrol #define BB_POST_CLEAN_COOLDOWN "post_clean_cooldown" +///key that holds cooldown after trash talk +#define BB_TRASH_TALK_COOLDOWN "trash_talk_cooldown" //secbots ///threat of our current target @@ -99,8 +102,6 @@ //firebot keys ///things we can extinguish #define BB_FIREBOT_CAN_EXTINGUISH "can_extinguish" -///the target we will extinguish -#define BB_FIREBOT_EXTINGUISH_TARGET "extinguish_target" ///lines we say when we detect a fire #define BB_FIREBOT_FIRE_DETECTED_LINES "fire_detected_lines" ///lines we say when we are idle @@ -123,33 +124,23 @@ #define BB_VIBEBOT_INSTRUMENT "instrument" //repairbots -///key that holds the floor we should tile over -#define BB_TILELESS_FLOOR "tileless_floor" -///key that holds the turf we should place a girder over -#define BB_GIRDER_TARGET "girder_target" -///key that holds the girder we should place a wall over -#define BB_GIRDER_TO_WALL_TARGET "girder_to_wall" -///key that holds the grille we must fix -#define BB_WINDOW_FRAMETARGET "grille_target" -///key that holds the machinery we repair with a welder -#define BB_WELDER_TARGET "welder_target" ///our wall girder ability #define BB_GIRDER_BUILD_ABILITY "girder_build_ability" -///key that holds breached floors we should repair -#define BB_BREACHED_FLOOR "breached_floor" ///key that holds our emagged speech #define BB_REPAIRBOT_EMAGGED_SPEECH "emagged_speech" ///key that holds our normal speech #define BB_REPAIRBOT_NORMAL_SPEECH "normal_speech" -///key that holds the thing we should deconstruct -#define BB_DECONSTRUCT_TARGET "deconstruct_target" ///key that holds our speech timer #define BB_REPAIRBOT_SPEECH_COOLDOWN "speech_cooldown" -///key that holds our target borg -#define BB_ROBOT_TARGET "robot_target" -///key that holds materials we can refill -#define BB_REFILLABLE_TARGET "refillable_target" +///Key that holds our current interaction type, to determine how we will interact with this object +#define BB_REPAIRBOT_INTERACTION_TYPE "interaction_type" +///Key that hold robot target +#define BB_ROBOT_TARGET "robot target" +///Key thast holds thingt to deconstruct +#define BB_DECONSTRUCT_TARGET "deconstruct_target" +#define REPAIRBOT_INTERACTION_INTERACT 1 +#define REPAIRBOT_INTERACTION_BUILD_GIRDERS 2 //mulebots ///key that holds our delivery destination's name diff --git a/code/__DEFINES/ai/carp.dm b/code/__DEFINES/ai/carp.dm index 459b98ffb020..1ff4acb3fe2e 100644 --- a/code/__DEFINES/ai/carp.dm +++ b/code/__DEFINES/ai/carp.dm @@ -6,6 +6,8 @@ #define BB_CARP_MIGRATION_PATH "BB_carp_migration_path" /// Current target turf in your migration #define BB_CARP_MIGRATION_TARGET "BB_carp_migration_target" +/// Turf of a carp rift we've decided to ride towards +#define BB_CARP_RIFT_DESTINATION "BB_carp_rift_destination" /// Targeting keys for magicarp spells #define BB_MAGICARP_SPELL_TARGET "BB_magicarp_spell_target" diff --git a/code/__DEFINES/ai/haunted.dm b/code/__DEFINES/ai/haunted.dm index d836d6be60da..1956d60ac3d0 100644 --- a/code/__DEFINES/ai/haunted.dm +++ b/code/__DEFINES/ai/haunted.dm @@ -8,6 +8,8 @@ #define HAUNTED_ITEM_AGGRO_ADDITION 2 ///how far a cursed item will still try to chase a target #define CURSED_VIEW_RANGE 7 +///Max number of throws per attack sequence before giving up on the current target +#define HAUNTED_MAX_THROW_ATTEMPTS 4 #define BB_TO_HAUNT_LIST "BB_to_haunt_list" ///Actual mob the item is haunting at the moment diff --git a/code/__DEFINES/ai/monkey.dm b/code/__DEFINES/ai/monkey.dm index f11ac96fefe2..6329e546cad6 100644 --- a/code/__DEFINES/ai/monkey.dm +++ b/code/__DEFINES/ai/monkey.dm @@ -7,14 +7,12 @@ #define BB_MONKEY_BLACKLISTITEMS "BB_monkey_blacklistitems" #define BB_MONKEY_PICKUPTARGET "BB_monkey_pickuptarget" #define BB_MONKEY_PICKPOCKETING "BB_monkey_pickpocketing" -#define BB_MONKEY_CURRENT_ATTACK_TARGET "BB_monkey_current_attack_target" #define BB_MONKEY_CURRENT_PRESS_TARGET "BB_monkey_current_press_target" #define BB_MONKEY_CURRENT_GIVE_TARGET "BB_monkey_current_give_target" #define BB_MONKEY_TARGET_DISPOSAL "BB_monkey_target_disposal" #define BB_MONKEY_TARGET_MONKEYS "BB_monkey_target_monkeys" #define BB_MONKEY_DISPOSING "BB_monkey_disposing" #define BB_MONKEY_RECRUIT_COOLDOWN "BB_monkey_recruit_cooldown" -#define BB_RESISTING "BB_resisting" /// Monkey is not necessarily a wild animal so it won't resort to fighting over food and such #define BB_MONKEY_TAMED "BB_monkey_tamed" /// Chance to give our held item to a nearby mob @@ -23,3 +21,17 @@ #define BB_MONKEY_PRESS_TYPEPATH "BB_monkey_press_typepath" /// The item the monkey is currently serving to someone #define BB_MONKEY_CURRENT_SERVED_ITEM "BB_monkey_current_served_item" +/// Set to TRUE when the monkey's current pickup target is on a mob (pickpocket), null when it's on the floor +#define BB_MONKEY_PICKUP_IS_PICKPOCKET "BB_monkey_pickup_is_pickpocket" +/// List of nearby patrons the monkey can serve food to +#define BB_MONKEY_PATRONS_NEARBY "BB_monkey_patrons_nearby" +/// Common emotes played during idle behavior +#define BB_MONKEY_IDLE_COMMON_EMOTES "BB_monkey_idle_common_emotes" +/// Rare emotes played during idle behavior +#define BB_MONKEY_IDLE_RARE_EMOTES "BB_monkey_idle_rare_emotes" +/// Whether the monkey believes its gun still works (20% chance it doesn't notice a dry-fire) +#define BB_MONKEY_GUN_WORKED "BB_monkey_gun_worked" +///Whether the monkey wants to press some shit +#define BB_MONKEY_WANNA_PRESS_SOME_SHIT "Monkeys wants to press something" +///Whether the monkey can look for patrons again +#define BB_MONKEY_PATRON_FIND_COOLDOWN "Monkeys can look for patron" diff --git a/code/__DEFINES/ai/monsters.dm b/code/__DEFINES/ai/monsters.dm index 8a4e9d56aa53..b13ef26e1b00 100644 --- a/code/__DEFINES/ai/monsters.dm +++ b/code/__DEFINES/ai/monsters.dm @@ -60,16 +60,28 @@ // bee keys ///the bee hive we live inside #define BB_CURRENT_HOME "BB_current_home" +///the bee hive we want to move into +#define BB_TARGET_HOME "BB_target_home" ///the hydro we will pollinate #define BB_TARGET_HYDRO "BB_target_hydro" ///key to swarm around #define BB_SWARM_TARGET "BB_swarm_target" +///turf picked by swirl_around_target for move_to_target to path toward +#define BB_SWIRL_TURF "BB_swirl_turf" +///key to indicate if the bee wants to go in or out of its hive. +#define BB_WANTS_TO_TRANSITION_HIVE "BB_wants_to_transition_hive" // bear keys ///the hive with honey that we will steal from #define BB_FOUND_HONEY "BB_found_honey" +///cooldown between hive raids +#define BB_BEAR_HIVE_COOLDOWN "BB_bear_hive_cooldown" +///cooldown between honeycomb hunts +#define BB_BEAR_HONEYCOMB_COOLDOWN "BB_bear_honeycomb_cooldown" ///the tree that we will climb #define BB_CLIMBED_TREE "BB_climbed_tree" +///tree climbing cooldown +#define BB_TREE_CLIMBING_COOLDOWN "Tree Climbing Cooldown" /// Lobstrosities will only attack people with one of these traits #define BB_LOBSTROSITY_EXPLOIT_TRAITS "BB_lobstrosity_exploit_traits" @@ -90,6 +102,8 @@ #define BB_BLIND_TARGET "BB_blind_target" ///value to store the minimum eye damage to prevent us from attacking a human #define BB_EYE_DAMAGE_THRESHOLD "BB_eye_damage_threshold" +///the turf in front of our target we move to so our glare lines up +#define BB_GLARE_POSITION "BB_glare_position" // hivebot keys ///the machine we must go to repair @@ -108,6 +122,10 @@ #define BB_TARGET_CANNIBAL "BB_target_cannibal" ///the tree we will burn down #define BB_TARGET_TREE "BB_target_tree" +///cooldown key for sculpting statues +#define BB_WHELP_SCULPT_COOLDOWN "BB_whelp_sculpt_cooldown" +///cooldown key for burning trees +#define BB_WHELP_BURN_COOLDOWN "BB_whelp_burn_cooldown" // Regal Rats /// The rat's ability to corrupt an area. @@ -140,6 +158,8 @@ #define BB_BOULDER_TARGET "BB_boulder_target" /// key that holds the ore_vent we will harvest boulders from #define BB_VENT_TARGET "BB_vent_target" +/// Prevent us from strip-mining Lavaland lol +#define BB_MINING_COOLDOWN "Mining Cooldown" // minebot keys /// key that stores our toggle light ability @@ -174,6 +194,8 @@ #define BB_MINEBOT_REPAIR_DRONE "minebot_repair_drone" ///should we plant mines? #define BB_MINEBOT_PLANT_MINES "minebot_plant_mines" +///should we plant mines? +#define BB_MINEBOT_CRIT_ALERT_COOLDOWN "minebot callcrit cooldown" //seedling keys /// the water can we will pick up @@ -231,6 +253,21 @@ #define BB_MOOK_MUSIC_AUDIENCE "music_audience" /// the bonfire we will light up #define BB_MOOK_BONFIRE_TARGET "bonfire_target" +/// turf we are wandering toward (away from village) +#define BB_WANDER_DESTINATION "BB_wander_destination" +/// turf we will stand on to deposit ores at the material stand +#define BB_DEPOSIT_POSITION "BB_deposit_position" +/// list of /datum/pet_command instances the chief can issue +#define BB_MOOK_COMMANDS "BB_mook_commands" +/// list of things we can heal +#define BB_MOOK_HEAL_TARGETS "heal targets" +/// Cooldown on giving commands +#define BB_COMMAND_COOLDOWN "command cooldown" +///Things to light on fire +#define BB_BONFIRE_TARGETS "bonfire targets" +///Mook mining cooldown +#define BB_MOOK_MINING_COOLDOWN "mining cooldown" + //gutlunch keys ///the trough we will eat from @@ -250,7 +287,7 @@ #define BB_SWIM_ALTERNATE_TURF "swim_alternate_turf" ///key holds our state of swimming #define BB_CURRENTLY_SWIMMING "currently_swimming" -///key holds how long we will be swimming for +///Time between swims #define BB_KEY_SWIMMER_COOLDOWN "key_swimmer_cooldown" //Wizard AI keys /// Key where we store our main targeted spell @@ -301,6 +338,8 @@ #define BB_DEER_GRASS_TARGET "deer_grass_target" ///our tree target #define BB_DEER_TREE_TARGET "deer_tree_target" +///set when we roll the urge to go find another deer to play with +#define BB_DEER_WANTS_TO_PLAY "deer_wants_to_play" ///our temporary playmate #define BB_DEER_PLAYFRIEND "deer_playfriend" ///our home target @@ -309,6 +348,11 @@ #define BB_DEER_RESTING "deer_resting" ///time till our next rest duration #define BB_DEER_NEXT_REST_TIMER "deer_next_rest_timer" +///cooldowns gating how often we graze, drink and mark territory +#define BB_DEER_GRAZE_COOLDOWN "deer_graze_cooldown" +#define BB_DEER_DRINK_COOLDOWN "deer_drink_cooldown" +#define BB_DEER_MARK_COOLDOWN "deer_mark_cooldown" +#define BB_DEER_PLAY_COOLDOWN "deer_play_cooldown" //the thing boss #define BB_THETHING_CHARGE "BB_THETHING_CHARGE" @@ -318,19 +362,25 @@ #define BB_THETHING_CARDTENDRILS "BB_THETHING_CARDTENDRILS" #define BB_THETHING_ACIDSPIT "BB_THETHING_ACIDSPIT" /// Blackboard key for The Thing boss that determines attack mode. TRUE means it will focus on closing the distance and murdering the person in question. Otherwise AOE. -#define BB_THETHING_ATTACKMODE "BB_THETHING_ATTACKMODE" +#define BB_THETHING_MELEEMODE "BB_THETHING_MELEEMODE" /// The Thing will be in attack mode forever if true #define BB_THETHING_NOAOE "BB_THETHING_NOAOE" /// What (first in combo) attack was last executed #define BB_THETHING_LASTAOE "BB_THETHING_LASTAOE" +/// The action object selected by pick_random_ability for the current AOE slot +#define BB_THETHING_SELECTED_AOE "BB_THETHING_SELECTED_AOE" -//turtle -///our tree's ability -#define BB_TURTLE_TREE_ABILITY "turtle_tree_ability" ///people we headbutt! #define BB_TURTLE_HEADBUTT_VICTIM "turtle_headbutt_victim" ///flore we must smell #define BB_TURTLE_FLORA_TARGET "turtle_flora_target" +///Cooldown between headbutts +#define BB_TURTLE_HEADBUTT_COOLDOWN "turtle_headbutt_cooldown" +///Cooldown between smelling flora +#define BB_TURTLE_FLORA_COOLDOWN "turtle_flora_cooldown" +#define BB_TURTLE_HEADBUTT_TYPES "turtle_headbutt_types" +#define BB_TURTLE_FLORA_TYPES "turtle_flora_types" + #define BB_GUNMIMIC_GUN_EMPTY "BB_GUNMIMIC_GUN_EMPTY" diff --git a/code/__DEFINES/ai/pet_commands.dm b/code/__DEFINES/ai/pet_commands.dm index 5f03bf1a5b0d..33b574fdfaf1 100644 --- a/code/__DEFINES/ai/pet_commands.dm +++ b/code/__DEFINES/ai/pet_commands.dm @@ -5,6 +5,10 @@ #define BB_CURRENT_PET_TARGET "BB_current_pet_target" /// Blackboard field for how we target things, as usually we want to be more permissive than normal #define BB_PET_TARGETING_STRATEGY "BB_pet_targeting" +/// Hiding location scratch key used by basic_melee_attack in pet command attack subtrees +#define BB_PET_ATTACK_HIDING_LOCATION "BB_pet_attack_hiding_location" +/// Scratch key holding the active untargeted ability datum for /datum/pet_command/untargeted_ability +#define BB_PET_ACTIVE_ABILITY "BB_pet_active_ability" /// Typecache of weakrefs to mobs this mob is friends with, will follow their instructions and won't attack them #define BB_FRIENDS_LIST "BB_friends_list" /// List of strings we might say to encourage someone to make better choices. diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 067569a8a9ac..ef61ebcc4ac4 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -29,6 +29,8 @@ #define BB_FIND_MOM_TYPES "BB_find_mom_types" ///list of types of mobs we must ignore #define BB_IGNORE_MOM_TYPES "BB_ignore_mom_types" +///cooldown between emoting at our parent +#define BB_PARENT_EMOTE_COOLDOWN "BB_parent_emote_cooldown" /// The current string that this parrot will repeat back to someone #define BB_PARROT_REPEAT_STRING "BB_parrot_repeat_string" @@ -52,6 +54,8 @@ #define BB_HOARD_LOCATION_RANGE "hoard_location_range" /// key that holds items we arent interested in hoarding #define BB_IGNORE_ITEMS "ignore_items" +/// cooldown key throttling how often a parrot repeats a phrase +#define BB_PARROT_SPEECH_COOLDOWN "BB_parrot_speech_cooldown" // Cultist pet keys ///our ability to summon runes diff --git a/code/__DEFINES/ai/slime.dm b/code/__DEFINES/ai/slime.dm index ba08dbba556b..9c8c2be10a88 100644 --- a/code/__DEFINES/ai/slime.dm +++ b/code/__DEFINES/ai/slime.dm @@ -12,3 +12,5 @@ #define BB_SLIME_EVOLVE "BB_slime_evolve" ///Our reproduce action #define BB_SLIME_REPRODUCE "BB_slime_reproduce" +///Target to try and eat, separate from retaliation because they use different strats +#define BB_SLIME_EAT_TARGET "BB_slime_eat_target" diff --git a/code/__DEFINES/ai/tourist.dm b/code/__DEFINES/ai/tourist.dm index 5ef27550d6f7..e46db87fde60 100644 --- a/code/__DEFINES/ai/tourist.dm +++ b/code/__DEFINES/ai/tourist.dm @@ -15,3 +15,7 @@ #define BB_CUSTOMER_CURRENT_TARGET "BB_customer_current_target" /// Robot customer has said their can't find seat line at least once. Used to rate limit how often they'll complain after the first time. #define BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE "BB_customer_said_cant_find_seat_line" +/// The exit portal atom the customer walks to when leaving the venue. +#define BB_CUSTOMER_EXIT_PORTAL "BB_customer_exit_portal" +/// Cooldown key used to throttle seat-search retries. +#define BB_CUSTOMER_FIND_SEAT_COOLDOWN "BB_customer_find_seat_cooldown" diff --git a/code/__DEFINES/ai/trader.dm b/code/__DEFINES/ai/trader.dm index 853dd8736b64..5d8c0405296e 100644 --- a/code/__DEFINES/ai/trader.dm +++ b/code/__DEFINES/ai/trader.dm @@ -4,3 +4,5 @@ #define BB_SHOP_SPOT "BB_shop_spot" ///Reference to our first customer to harass with deals #define BB_FIRST_CUSTOMER "BB_first_customer" +///Whether we spook customers with our deals +#define BB_TRADER_RUSH_TO_SELL "BB_rush_to_sell" diff --git a/code/__DEFINES/ai/ventcrawling.dm b/code/__DEFINES/ai/ventcrawling.dm index a60b7fd59401..b672113738de 100644 --- a/code/__DEFINES/ai/ventcrawling.dm +++ b/code/__DEFINES/ai/ventcrawling.dm @@ -14,3 +14,5 @@ #define BB_TIME_TO_GIVE_UP_ON_VENT_PATHING "BB_seconds_until_we_give_up_on_vent_pathing" /// The timer ID of the timer that makes us give up on vent pathing. #define BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID "BB_give_up_on_vent_pathing_timer_id" +/// The world.time when we entered a vent. Null when not in one. +#define BB_VENT_ENTRY_TIME "BB_vent_entry_time" diff --git a/code/__DEFINES/basic_mobs.dm b/code/__DEFINES/basic_mobs.dm index 1a97163a8715..d97418718a16 100644 --- a/code/__DEFINES/basic_mobs.dm +++ b/code/__DEFINES/basic_mobs.dm @@ -127,3 +127,9 @@ GLOBAL_LIST_EMPTY(customized_pets) #define BASIC_MOB_END_ATTACK_CHAIN 1 ///Return value for [/mob/living/basic/proc/early_melee_attack]. Using this value will make the attack end, and sets a cooldown. Useful if you add behavior to early_melee_attack #define BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN 2 + +///Delay between trying to update target selection +#define BASIC_MOB_FIND_TARGET_RATE 1 SECONDS + +///Time between idle behavior execution +#define IDLE_BEHAVIOR_RATE 1.5 SECONDS diff --git a/code/__DEFINES/bitrunning.dm b/code/__DEFINES/bitrunning.dm index 900182eb43d7..379a302fac22 100644 --- a/code/__DEFINES/bitrunning.dm +++ b/code/__DEFINES/bitrunning.dm @@ -4,6 +4,13 @@ #define BITRUNNER_COST_HIGH 3 #define BITRUNNER_COST_EXTREME 20 +#define BITRUNNING_GRADE_NONE "None" +#define BITRUNNING_GRADE_D "D" +#define BITRUNNING_GRADE_C "C" +#define BITRUNNING_GRADE_B "B" +#define BITRUNNING_GRADE_A "A" +#define BITRUNNING_GRADE_S "S" + /// Yay you did it #define BITRUNNER_REWARD_MIN 1 /// You faced some decent odds diff --git a/code/__DEFINES/bodyparts.dm b/code/__DEFINES/bodyparts.dm index a9b152578115..68277dad8464 100644 --- a/code/__DEFINES/bodyparts.dm +++ b/code/__DEFINES/bodyparts.dm @@ -37,13 +37,13 @@ #define AUGGED_LIMB_EMP_PARALYZE_TIME 3 SECONDS /// When hit by an EMP, the time an augged leg will be knocked down for. -#define AUGGED_LEG_EMP_KNOCKDOWN_TIME 3 SECONDS +#define AUGGED_LEG_EMP_KNOCKDOWN_TIME 5 SECONDS /// When hit by an EMP, the time a augged chest will cause a hardstun for if its above the damage threshold. #define AUGGED_CHEST_EMP_STUN_TIME 3 SECONDS -/// When hit by an EMP, the time an augged chest will cause the mob to shake() for. -#define AUGGED_CHEST_EMP_SHAKE_TIME 5 SECONDS +/// When hit by an EMP, the time an augged chest will cause the mob to jitter for. +#define AUGGED_CHEST_EMP_SHAKE_TIME 6 SECONDS /// When hit by an EMP, the time an augged head will make vision fucky for. -#define AUGGED_HEAD_EMP_GLITCH_DURATION 6 SECONDS +#define AUGGED_HEAD_EMP_GLITCH_DURATION 3.5 SECONDS // Color priorities for bodyparts /// Abductor team recoloring priority @@ -163,6 +163,41 @@ DEFINE_BITFIELD(surgery_state, list( /// Surgery states flipped on automatically if the bodypart lacks vessels #define VESSELLESS_SURGERY_STATES (SURGERY_VESSELS_CLAMPED|SURGERY_ORGANS_CUT) +// ~biology defines +// What kind of biology a limb has, and what wounds it can suffer +/// Has absolutely fucking nothing, no wounds +#define BIO_INORGANIC NONE +/// Has bone - allows the victim to suffer T2-T3 bone blunt wounds +#define BIO_BONE (1<<0) +/// Has flesh - allows the victim to suffer fleshy slash pierce and burn wounds +#define BIO_FLESH (1<<1) +/// Has metal - allows the victim to suffer robotic blunt and burn wounds +#define BIO_METAL (1<<2) +/// Has wood - should probably be able to catch on fire, or something +#define BIO_WOOD (1<<3) +/// Is wired internally - allows the victim to suffer electrical wounds (robotic T1-T3 slash/pierce) +#define BIO_WIRED (1<<4) +/// Has bloodflow - can suffer bleeding wounds and can bleed +#define BIO_BLOODED (1<<5) +/// Is connected by a joint - can suffer T1 bone blunt wounds (dislocation) +#define BIO_JOINTED (1<<6) +/// Skin is covered in thick chitin and is resistant to cutting +#define BIO_CHITIN (1<<7) +/// the bodypart is made of rocks. +#define BIO_STONE (1<<8) +/// Limb is comprised entirely of slime jelly - can be cannibalized by any jelly person with the ability to eat their own limbs to replenish their own slime (blood) +#define BIO_JELLY (1<<9) +/// Robotic - can suffer all metal/wired wounds, such as: UNIMPLEMENTED PLEASE UPDATE ONCE SYNTH WOUNDS 9/5/2023 ~Niko +#define BIO_ROBOTIC (BIO_METAL|BIO_WIRED) +/// Has flesh and bone - See BIO_BONE and BIO_FLESH +#define BIO_FLESH_BONE (BIO_BONE|BIO_FLESH) +/// Standard humanoid - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt, except dislocations. Think human heads/chests +#define BIO_STANDARD_UNJOINTED (BIO_FLESH_BONE|BIO_BLOODED) +/// Standard humanoid limbs - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt. Can also bleed, and be dislocated. Think human arms and legs +#define BIO_STANDARD_JOINTED (BIO_STANDARD_UNJOINTED|BIO_JOINTED) +/// Xenomorph limbs (xenos are immune to wounds anyhow) +#define BIO_STANDARD_ALIEN (BIO_CHITIN|BIO_BONE|BIO_BLOODED|BIO_JOINTED) + /// Biological state that has some kind of skin that can be cut. #define BIOSTATE_HAS_SKIN (BIO_FLESH|BIO_METAL|BIO_CHITIN) /// Checks if a bodypart lacks both flesh and metal, meaning it has no skin to cut. diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index deb4be811df4..9f57ff4ed909 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -72,7 +72,7 @@ #define CANKNOCKDOWN (1<<1) /// If set, this mob can be knocked unconscious via status effect. /// NOTE, does not mean immune to sleep. Unconscious and sleep are two different things. -/// NOTE, does not relate to the unconscious stat either. Only the status effect. +/// NOTE, does not relate to the unconscious trait either. Only the status effect. #define CANUNCONSCIOUS (1<<2) /// If set, this mob can be grabbed or pushed when bumped into #define CANPUSH (1<<3) diff --git a/code/__DEFINES/construction/material.dm b/code/__DEFINES/construction/material.dm index 413de505765d..61d37f0bc7a9 100644 --- a/code/__DEFINES/construction/material.dm +++ b/code/__DEFINES/construction/material.dm @@ -75,7 +75,7 @@ GLOBAL_LIST_INIT(material_flags_to_string, alist( #define MATERIAL_FLAMMABILITY "flammability" #define MATERIAL_RADIOACTIVITY "radioactivity" #define MATERIAL_FIRESTACKER "firestacker" -#define MATERIAL_VAMPIRES_BANE "vampires_bane" +#define MATERIAL_UNHOLY_BANE "unholy_bane" #define MATERIAL_TELEPORTING "teleporting" #define MATERIAL_PENETRATING "penetrating" diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm index e2d1616c6b9e..f6432791ba63 100644 --- a/code/__DEFINES/crafting.dm +++ b/code/__DEFINES/crafting.dm @@ -37,6 +37,9 @@ #define CRAFT_IGNORE_DO_AFTER (1<<9) /// This craft won't change the materials of the resulting item to match that of the combined components #define CRAFT_NO_MATERIALS (1<<10) +/// This craft will collect items used in the crafting recipe for check_requirements() +/// Ugly microop but only a few recipes care about this, and we do a lot of work for it +#define CRAFT_COLLECT_REQUIREMENTS (1<<11) //Crafting blacklist behaviors /// By default, blacklist the result if it's not in reqs @@ -108,4 +111,5 @@ #define CAT_ENTERTAINMENT "Entertainment" #define CAT_TOOLS "Tools" #define CAT_CULT "Blood Cult" +#define CAT_CONSTRUCTION "Construction" #define CAT_TZIMISCE "Tzimisce" // DARKPACK EDIT ADD diff --git a/code/__DEFINES/dcs/signals/signals_action.dm b/code/__DEFINES/dcs/signals/signals_action.dm index 66a4ffc894b6..649662d2add1 100644 --- a/code/__DEFINES/dcs/signals/signals_action.dm +++ b/code/__DEFINES/dcs/signals/signals_action.dm @@ -58,6 +58,9 @@ /// From /datum/action/cooldown/spell/touch/lay_on_hands/proc/determine_if_this_hurts_instead(), sent to the /mob/living/carbon/hurtguy: (/mob/living/carbon/mendicant) #define COMSIG_ON_LAY_ON_HANDS "mob_ability_lay_on_hands" +/// From /datum/action/update_button_status (atom/movable/screen/movable/action_button/current_button, force) +#define COMSIG_ACTION_STATUS_UPDATE "action_status_update" + /// From /datum/action/innate/dash/proc/teleport when teleport is successful #define COMSIG_DASH_ACTION_DASHED "dash_action_dashed" /// From /datum/action/innate/dash/proc/charge when charges are replenished diff --git a/code/__DEFINES/dcs/signals/signals_ai_controller.dm b/code/__DEFINES/dcs/signals/signals_ai_controller.dm index 1888612d2c4a..0b0b1a35f8d3 100644 --- a/code/__DEFINES/dcs/signals/signals_ai_controller.dm +++ b/code/__DEFINES/dcs/signals/signals_ai_controller.dm @@ -3,7 +3,7 @@ #define COMSIG_AI_CONTROLLER_POSSESSED_PAWN "ai_controller_possessed_pawn" ///sent from ai controllers when they stop possessing a pawn: (datum/ai_controller/source_controller) #define COMSIG_AI_CONTROLLER_UNPOSSESSED_PAWN "ai_controller_unpossessed_pawn" -///sent from ai controllers when they pick behaviors: (list/datum/ai_behavior/old_behaviors, list/datum/ai_behavior/new_behaviors) -#define COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS "ai_controller_picked_behaviors" -///sent from ai controllers when a behavior is inserted into the queue: (list/new_arguments) -#define AI_CONTROLLER_BEHAVIOR_QUEUED(type) "ai_controller_behavior_queued_[type]" +///sent from the pawn when an ai controller possesses it: (datum/ai_controller/source_controller) +#define COMSIG_PAWN_POSSESSED_BY_AI_CONTROLLER "pawn_possessed_by_ai_controller" +///sent from the pawn of an ai controller when a runtime subtree override slot changes: (new_type) new_type is null when cleared +#define COMSIG_AI_OVERRIDE_SLOT_CHANGED(id) "ai_override_slot_changed_[id]" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index 6e70a9b36b9c..06df0cbfe5af 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -84,6 +84,7 @@ #define COMSIG_ATOM_PROCESSED "atom_processed" ///called when teleporting into a possibly protected turf: (channel, turf/origin, turf/destination) #define COMSIG_ATOM_INTERCEPT_TELEPORTING "intercept_teleporting" + #define COMPONENT_INTERCEPT_TELEPORT (1<<0) ///called when an atom is added to the hearers on get_hearers_in_view(): (list/processing_list, list/hearers) #define COMSIG_ATOM_HEARER_IN_VIEW "atom_hearer_in_view" ///called when an atom starts orbiting another atom: (atom) diff --git a/code/__DEFINES/dcs/signals/signals_heretic.dm b/code/__DEFINES/dcs/signals/signals_heretic.dm index 14fe7a51e535..dc19b2f0b057 100644 --- a/code/__DEFINES/dcs/signals/signals_heretic.dm +++ b/code/__DEFINES/dcs/signals/signals_heretic.dm @@ -9,6 +9,8 @@ /// Default behavior is to continue attack chain and do nothing else, so return this to use up the hand after use. #define COMPONENT_USE_HAND (1<<0) +/// From /obj/item/melee/sickly_blade/preattack : (mob/living/source, mob/living/target) +#define COMSIG_HERETIC_BLADE_PREATTACK "blade_preattack" /// From /obj/item/melee/sickly_blade/afterattack : (mob/living/source, mob/living/target) #define COMSIG_HERETIC_BLADE_ATTACK "blade_attack" /// From /obj/item/melee/sickly_blade/ranged_interact_with_atom (without proximity) : (mob/living/source, mob/living/target) @@ -16,6 +18,8 @@ /// For [/datum/status_effect/protective_blades] to signal when it is triggered #define COMSIG_BLADE_BARRIER_TRIGGERED "blade_barrier_triggered" +/// For [/datum/status_effect/protective_blades] to the mob after triggering a blade +#define COMSIG_MOB_BLADE_BARRIER_TRIGGERED "mob_blade_barrier_triggered" /// at the end of determine_drafted_knowledge #define COMSIG_HERETIC_SHOP_SETUP "heretic_shop_finished" @@ -24,3 +28,21 @@ #define COMSIG_HERETIC_PASSIVE_UPGRADE_FIRST "heretic_passive_upgrade_first" /// called on the antagonist datum, upgrades the passive to level 3 #define COMSIG_HERETIC_PASSIVE_UPGRADE_FINAL "heretic_passive_upgrade_final" + +/// Sent to a heretic datum when an influence is drained +#define COMSIG_HERETIC_INFLUENCE_DRAINED "heretic_influence_drained" + +/// Send to a mob after they apply the moonlight amulet to another mob +#define COMSIG_MOB_APPLIED_MOONLIGHT_AMULET "mob_applied_moonlight_amulet" + +/// Sent from a mob as they invoke rust_heretic_act +#define COMSIG_MOB_RUST_HERETIC_ACT "living_rust_heretic_act" + +/// Sent to a heretic datum on a successful sacrifice: (mob/living/sacrificed, high_value) +#define COMSIG_HERETIC_SACRIFICE "heretic_sacrifice" + +/// Sent to a mob when they summon a heretic monster +#define COMSIG_HERETIC_SUMMONED_MOB "heretic_summoned_mob" + +/// Sent to a heretic when they successfully research a piece of heretic knowledge +#define COMSIG_HERETIC_RESEARCHED_KNOWLEDGE "heretic_researched_knowledge" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm index 1c6fcbffbda3..de7093057dd4 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm @@ -14,3 +14,6 @@ ///Signal sent off of ai/movement/proc/start_moving_towards #define COMSIG_MOB_AI_MOVEMENT_STARTED "mob_ai_movement_started" + +///Signal sent off of ai/movement/proc/increment_pathing_failures +#define COMSIG_MOB_AI_MOVEMENT_FAILED "mob_ai_movement_failed" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm index b459c2530b13..95c138ad5796 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm @@ -10,7 +10,7 @@ ///from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers) #define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged" -/// Sent from /datum/ai_planning_subtree/parrot_as_in_repeat() : () +/// Sent from /datum/bt_node/ai_behavior/parrot_repeat_speech/perform(): () #define COMSIG_NEEDS_NEW_PHRASE "parrot_needs_new_phrase" #define NO_NEW_PHRASE_AVAILABLE (1<<0) //! Cancel to try again later for when we actually get a new phrase diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index 13deb5794787..fe99bfa253b1 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -62,6 +62,8 @@ /// From /obj/item/bodypart/proc/update_limb(): (dropping_limb, is_creating) #define COMSIG_BODYPART_UPDATED "bodypart_updated" +/// Also from /obj/item/bodypart/proc/update_limb(), sent to the mob: (dropping_limb, is_creating) +#define COMSIG_CARBON_BODYPART_UPDATED "carbon_bodypart_updated" /// From /datum/component/butchering/create_replacement_limb(): (replacement limb) #define COMSIG_BODYPART_BUTCHERED "bodypart_butchered" @@ -221,3 +223,5 @@ #define COMSIG_END_DREAMING "end_dreaming" /// From /mob/living/carbon/item_coverage_changed: (added_slots, removed_slots) #define COMSIG_CARBON_ITEM_COVERAGE_CHANGED "carbon_item_coverage_changed" +/// From /mob/living/carbon/proc/get_sight_and_cutoffs(): (list/new_sight_flags) +#define COMSIG_CARBON_UPDATE_SIGHT_CUTOFFS "carbon_update_sight_cutoffs" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index 13e65bc7b5d9..0b7d381d18e3 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -393,5 +393,11 @@ /// From /mob/living/on_looking_z_level_change() : (turf/old_turf, turf/new_turf) #define COMSIG_LIVING_LOOK_Z_CHANGE "living_look_z_change" +/// From /obj/item/stack/ore/bluespace_crystal/attack_self() : (obj/item/crushed_crystal) +#define COMSIG_MOB_CRUSHED_BLUESPACE_CRYSTAL "living_crushed_bluespace_crystal" + /// From /mob/living/proc/refresh_gravity() : (new_gravity, old_gravity) #define COMSIG_LIVING_GRAVITY_CHANGED "living_gravity_changed" + +/// From /mob/living/changeNext_move() : (next_move, delay) +#define COMSIG_LIVING_CHANGENEXT_MOVE "living_changenext_move" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index 81697e545ee3..dc1f639baf75 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -295,5 +295,8 @@ ///sent to radio TTS sounds when the volume preference is changed and applied #define COMSIG_MOB_TTS_RADIO_VOLUME_PREFERENCE_APPLIED "tts_radio_volume_preference_applied" +/// Sent when a mob's name is fullyrenamed (oldname(can be null), newname) +#define COMSIG_MOB_FULLY_RENAMED "mob_fully_renamed" + ///from base of [/datum/component/multiple_lives/proc/respawn]: (mob/respawned_mob, gibbed, lives_left) #define COMSIG_ON_MULTIPLE_LIVES_RESPAWN "on_multiple_lives_respawn" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm index ca40ca7aef0f..1ab11a18c618 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm @@ -12,3 +12,5 @@ #define COMSIG_SILICON_AI_VACATE_APC "AI_vacate_apc" ///called when an AI's control is toggled #define COMSIG_SILICON_AI_SET_CONTROL_DISABLED "AI_set_control_disabled" +/// Sent when a silicon's laws change from a module rack +#define COMSIG_SILICON_MODULE_RACK_LAWSET_UPDATE "silicon_module_rack_lawset_update" diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 7092efd0e2d5..91948c77bb7c 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -153,6 +153,8 @@ #define COMSIG_MOB_DROPPED_ITEM "mob_dropped_item" ///from base of obj/item/pickup(): (/mob/taker) #define COMSIG_ITEM_PICKUP "item_pickup" +///from base of mob/put_in_hand(), after forceMove item is now in the mob's hand: (mob/holder, hand_index) +#define COMSIG_ITEM_ENTERED_HANDS "item_entered_hands" ///from base of obj/item/on_outfit_equip(): (mob/equipper, visuals_only, slot) #define COMSIG_ITEM_EQUIPPED_AS_OUTFIT "item_equip_as_outfit" ///from base of datum/storage/handle_enter(): (datum/storage/storage) @@ -256,6 +258,13 @@ ///used to interrupt insertion #define COMPONENT_CLOSET_INSERT_INTERRUPT (1<<0) +/// From closet_teleporter_controller when something is teleporting through a closet +#define COMSIG_CLOSET_TELEPORTER_PRE_SENDING "closet_teleporter_pre_sending" + /// Blocks the item from teleporting + #define CLOSET_TELEPORT_BLOCKED (1<<0) + /// Ignores the can_teleport check + #define CLOSET_TELEPORT_FORCED (1<<1) + ///From open: (forced) #define COMSIG_CLOSET_PRE_OPEN "closet_pre_open" #define BLOCK_OPEN (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_spell.dm b/code/__DEFINES/dcs/signals/signals_spell.dm index 1154eba338f1..50f1ee20fae3 100644 --- a/code/__DEFINES/dcs/signals/signals_spell.dm +++ b/code/__DEFINES/dcs/signals/signals_spell.dm @@ -74,6 +74,9 @@ #define COMSIG_TOUCH_HANDLESS_CAST "spell_touch_handless_cast" /// Return this to prevent the hand spawning/unspawning #define COMPONENT_CAST_HANDLESS (1<<0) +/// "Casting" a touch spell brings it into your hand, so this is for actually hitting someone with it +/// Sent from /datum/action/cooldown/spell/touch/do_hand_hit: (atom/hit, mob/living/carbon/caster, obj/item/melee/touch_attack/hand) +#define COMSIG_SPELL_TOUCH_SPELL_ACTUALLY_CAST "spell_touch_spell_actually_cast" // Jaunt Spells /// Sent from datum/action/cooldown/spell/jaunt/before_cast, before the mob enters jaunting as a pre-check: (datum/action/cooldown/spell/spell) diff --git a/code/__DEFINES/diseases.dm b/code/__DEFINES/diseases.dm index 2cee0c1a0314..4a0cdd676c40 100644 --- a/code/__DEFINES/diseases.dm +++ b/code/__DEFINES/diseases.dm @@ -110,7 +110,9 @@ DEFINE_BITFIELD(spread_flags, list( /// Symptom Offset Duration - number of cycles over which sleeping/having spaceacillin or a slowdown effect can prevent symptoms appearing //// Set to maximum # of cycles you want to be able to offset symptoms. Scales down linearly over time. #define DISEASE_SYMPTOM_OFFSET_DURATION 200 - /// Symptom Frequency Modifier //// Raise to make symptoms fire less frequently, lower to make them fire more frequently. Keep at 0 or above. #define DISEASE_SYMPTOM_FREQUENCY_MODIFIER 1 +/// Symptom Immunity Recovery Bonus +/// Raise to make recovering from similar diseases more harshly affect future similar ones (like smallpox and cowpox) +#define DISEASE_SYMPTOM_IMMUNITY_RECOVERY_BONUS 1.5 diff --git a/code/__DEFINES/footsteps.dm b/code/__DEFINES/footsteps.dm index 2d55c09ff08d..c648e8636b65 100644 --- a/code/__DEFINES/footsteps.dm +++ b/code/__DEFINES/footsteps.dm @@ -8,6 +8,7 @@ #define FOOTSTEP_LAVA "lava" #define FOOTSTEP_MEAT "meat" #define FOOTSTEP_CATWALK "catwalk" +#define FOOTSTEP_CONCRETE "concrete" //barefoot sounds #define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot" #define FOOTSTEP_WOOD_CLAW "woodclaw" @@ -107,6 +108,12 @@ GLOBAL_LIST_INIT(footstep, list( 'sound/effects/footstep/catwalk3.ogg', 'sound/effects/footstep/catwalk4.ogg', 'sound/effects/footstep/catwalk5.ogg'), 100, 1), + FOOTSTEP_CONCRETE = list(list( + 'sound/effects/footstep/concrete1.ogg', + 'sound/effects/footstep/concrete2.ogg', + 'sound/effects/footstep/concrete3.ogg', + 'sound/effects/footstep/concrete4.ogg', + 'sound/effects/footstep/concrete5.ogg'), 100, 1), FOOTSTEP_PARKET = list(list( // DARKPACK EDIT ADD START - WALLS 'modular_darkpack/modules/walls/sounds/parket1.ogg', 'modular_darkpack/modules/walls/sounds/parket2.ogg', diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index 9eb09cf8f2e2..e4ad9cd53e72 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -132,24 +132,25 @@ DEFINE_BITFIELD(smoothing_junction, list( #define SMOOTH_GROUP_FLOOR_SIDERITE S_TURF(54) //![/turf/open/misc/asteroid/basalt/smooth/siderite] #define SMOOTH_GROUP_FLOOR_SHALE S_TURF(55) //![/turf/open/misc/asteroid/basalt/smooth/shale] #define SMOOTH_GROUP_FLOOR_BASALT S_TURF(56) //![/turf/open/misc/asteroid/basalt] - -#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(57) ///turf/closed -#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(58) ///turf/closed/wall/material -#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(59) ///turf/closed/wall/r_wall/plastitanium/syndicate, /turf/closed/indestructible/syndicate -#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(60) ///turf/closed/indestructible/hotelwall -#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(61) ///turf/closed/mineral, /turf/closed/indestructible -#define SMOOTH_GROUP_RED_ROCK_WALLS S_TURF(62) ///turf/closed/mineral/asteroid, /turf/closed/mineral/random/stationside/asteroid -#define SMOOTH_GROUP_SHALE_WALLS S_TURF(63) ///turf/closed/mineral/random/volcanic/shale -#define SMOOTH_GROUP_BOSS_WALLS S_TURF(64) ///turf/closed/indestructible/riveted/boss -#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(65) ///turf/closed/wall/mineral/titanium/survival -#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(66) ///turf/open/cliff -#define SMOOTH_GROUP_HIEROPHANT S_TURF(67) ///turf/closed/indestructible/riveted/hierophant -#define SMOOTH_GROUP_PLASTINUM_WALLS S_TURF(68) ///turf/closed/indestructible/riveted/plastinum +#define SMOOTH_GROUP_FLOOR_HEXACRETE S_TURF(57) ///turf/open/floor/concrete + +#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(58) ///turf/closed +#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(59) ///turf/closed/wall/material +#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(60) ///turf/closed/wall/r_wall/plastitanium/syndicate, /turf/closed/indestructible/syndicate +#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(61) ///turf/closed/indestructible/hotelwall +#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(62) ///turf/closed/mineral, /turf/closed/indestructible +#define SMOOTH_GROUP_RED_ROCK_WALLS S_TURF(63) ///turf/closed/mineral/asteroid, /turf/closed/mineral/random/stationside/asteroid +#define SMOOTH_GROUP_SHALE_WALLS S_TURF(64) ///turf/closed/mineral/random/volcanic/shale +#define SMOOTH_GROUP_BOSS_WALLS S_TURF(65) ///turf/closed/indestructible/riveted/boss +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(66) ///turf/closed/wall/mineral/titanium/survival +#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(67) ///turf/open/cliff +#define SMOOTH_GROUP_HIEROPHANT S_TURF(68) ///turf/closed/indestructible/riveted/hierophant +#define SMOOTH_GROUP_PLASTINUM_WALLS S_TURF(69) ///turf/closed/indestructible/riveted/plastinum // DARKPACK EDIT CHANGE START - WALLS -#define SMOOTH_GROUP_CITY_WALL S_TURF(69) ///turf/closed/wall/vampwall +#define SMOOTH_GROUP_CITY_WALL S_TURF(70) ///turf/closed/wall/vampwall -#define MAX_S_TURF 69 //Always match this value with the one above it. +#define MAX_S_TURF 70 //Always match this value with the one above it. // DARKPACK EDIT CHANGE END #define S_OBJ(num) ("-" + #num + ",") @@ -168,9 +169,10 @@ DEFINE_BITFIELD(smoothing_junction, list( #define SMOOTH_GROUP_ABDUCTOR_WALLS S_OBJ(11) ///turf/closed/wall/mineral/abductor, /obj/structure/falsewall/abductor #define SMOOTH_GROUP_TITANIUM_WALLS S_OBJ(12) ///turf/closed/wall/mineral/titanium, /obj/structure/falsewall/titanium #define SMOOTH_GROUP_PLASTITANIUM_WALLS S_OBJ(14) ///turf/closed/wall/mineral/plastitanium, /obj/structure/falsewall/plastitanium -#define SMOOTH_GROUP_SURVIVAL_TITANIUM_POD S_OBJ(15) ///turf/closed/wall/mineral/titanium/survival/pod, /obj/machinery/door/airlock/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_POD S_OBJ(15) ///turf/closed/wall/mineral/plastitanium/survival/pod, /obj/machinery/door/airlock/survival_pod, /obj/structure/window/reinforced/shuttle/survival_pod #define SMOOTH_GROUP_HIERO_WALL S_OBJ(16) ///obj/effect/temp_visual/elite_tumor_wall, /obj/effect/temp_visual/hierophant/wall #define SMOOTH_GROUP_BAMBOO_WALLS S_OBJ(17) //![/turf/closed/wall/mineral/bamboo, /obj/structure/falsewall/bamboo] +#define SMOOTH_GROUP_CONCRETE_WALLS S_OBJ(18) ///turf/closed/wall/concrete #define SMOOTH_GROUP_PAPERFRAME S_OBJ(21) ///obj/structure/window/paperframe, /obj/structure/mineral_door/paperframe diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 3c2b96257c12..9efef4af9112 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -58,10 +58,21 @@ GLOBAL_LIST_INIT(turfs_openspace, typecacheof(list( #define isopenspaceturf(A) (is_type_in_typecache(A, GLOB.turfs_openspace)) +GLOBAL_LIST_INIT(turfs_snow, typecacheof(list( + /turf/open/floor/fake_snow, + /turf/open/misc/asteroid/snow, + /turf/open/misc/snow +))) + +#define issnowturf(A) (is_type_in_typecache(A, GLOB.turfs_snow)) + #define isopenturf(A) (istype(A, /turf/open)) #define isindestructiblefloor(A) (istype(A, /turf/open/indestructible)) +/// Can this turf have machines anchored to it +#define is_anchorable_floor(A) (isfloorturf(A) || isindestructiblefloor(A)) + #define isspaceturf(A) (istype(A, /turf/open/space)) #define is_space_or_openspace(A) (isopenspaceturf(A) || isspaceturf(A)) @@ -115,7 +126,12 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define isdummy(A) (istype(A, /mob/living/carbon/human/dummy)) -//Human sub-species +/** + * Human sub-species checks. + * NOTE: our long term objective is to move away from hardcoded species checks in favor of traits, components, organs and bodyparts! + * It's more reusable and modulable code overall. If you think you're better off using traits etc. in place of an is_species check, + * then do just that. + */ #define isabductor(A) (is_species(A, /datum/species/abductor)) #define isspirit(A) (is_species(A, /datum/species/spirit)) #define isgolem(A) (is_species(A, /datum/species/golem)) @@ -137,7 +153,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define isandroid(A) (is_species(A, /datum/species/android)) #define isnightmare(A) (is_species(A, /datum/species/shadow/nightmare)) - //More carbon mobs #define isalien(A) (istype(A, /mob/living/carbon/alien)) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 48428c4cb6d8..48d2b0df0815 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -546,6 +546,14 @@ #define EAR_PROTECTION_VACUUM 3 #define EAR_PROTECTION_FULL INFINITY +/** + * EMP protection + * These values are additive to determine your overall emp protection + */ +#define EMP_PROTECTION_NONE 0 +#define EMP_PROTECTION_MODERATE 1 +#define EMP_PROTECTION_HIGH 2 + /** * Soundbang defines * These values are used as argument to determine the strength of the soundbang_act call @@ -662,7 +670,7 @@ #define SILENCE_RANGED_MESSAGE (1<<0) /// Returns whether or not the given mob can succumb -#define CAN_SUCCUMB(target) ((HAS_TRAIT(target, TRAIT_CRITICAL_CONDITION) || HAS_TRAIT(target, TRAIT_DEATHCOMA)) && !HAS_TRAIT(target, TRAIT_NODEATH)) // DARKPACK EDIT CHANGE - Torpor +#define CAN_SUCCUMB(target) (((target.stat == SOFT_CRIT || target.stat == HARD_CRIT) || HAS_TRAIT(target, TRAIT_DEATHCOMA)) && !HAS_TRAIT(target, TRAIT_NODEATH)) // DARKPACK EDIT CHANGE - Torpor // Body position defines. /// Mob is standing up, usually associated with lying_angle value of 0. @@ -1120,3 +1128,12 @@ GLOBAL_LIST_INIT(regal_rat_minion_commands, list( /datum/pet_command/follow, /datum/pet_command/attack/mouse )) + +/// Checks if the mob is unconscious (or in other words, has [TRAIT_KNOCKEDOUT]) - note dead mobs are considered unconscious +#define IS_UNCONSCIOUS(mob) (HAS_TRAIT(mob, TRAIT_KNOCKEDOUT)) +/// Checks if the mob is in soft crit, hard crit, dead, or is otherwise unconscious (which hard crit and death apply anyways) +#define IS_UNCONSCIOUS_OR_CRIT(mob) (IS_UNCONSCIOUS(mob) || mob.stat >= SOFT_CRIT) +/// Checks if the mob is unconscious but not dead +#define IS_UNCONSCIOUS_AND_ALIVE(mob) (IS_UNCONSCIOUS(mob) && mob.stat != DEAD) +/// Checks if the mob is dead or faking death (via [TRAIT_FAKEDEATH]) +#define IS_DEAD_OR_FAKING(mob) (mob.stat == DEAD || HAS_TRAIT(mob, TRAIT_FAKEDEATH)) diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm index 8f6aa7a268fb..b05d554f5e35 100644 --- a/code/__DEFINES/mod.dm +++ b/code/__DEFINES/mod.dm @@ -39,6 +39,7 @@ #define CAN_OVERSLOT "can_overslot" #define UNSEALED_MESSAGE "unsealed_message" #define SEALED_MESSAGE "sealed_message" +#define MOD_PART_TRAITS "mod_part_traits" //Defines used to override MOD clothing's icon and worn icon files in the skin. #define MOD_ICON_OVERRIDE "mod_icon_override" diff --git a/code/__DEFINES/monkeys.dm b/code/__DEFINES/monkeys.dm index 8dfccc937f76..a2280ca95a41 100644 --- a/code/__DEFINES/monkeys.dm +++ b/code/__DEFINES/monkeys.dm @@ -33,12 +33,9 @@ /// amount of aggro to add if someone stole the food we wanted #define MONKEY_FOOD_HATRED_AMOUNT 2 /// probability of reducing aggro by one when the monkey attacks -#define MONKEY_HATRED_REDUCTION_PROB 20 +#define MONKEY_HATRED_REDUCTION_PROB 40 /// Monkey was calmed, such as from weed #define MONKEY_CALMED_HATRED_AMOUNT -2 /// Monkey was angered, such as from alcohol #define MONKEY_ANGERED_HATRED_AMOUNT 2 - -///Monkey recruit cooldown -#define MONKEY_RECRUIT_COOLDOWN (1 MINUTES) diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index aa79245b35d0..5fda2e9c15b2 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -104,18 +104,16 @@ #define STACKABLE_HELMET_EXEMPT (1<<12) /// Prevents plasmamen from igniting when wearing this #define PLASMAMAN_PREVENT_IGNITION (1<<13) -/// Usable as casting clothes by wizards (matters for suits, glasses and headwear) -#define CASTING_CLOTHES (1<<14) ///Moths can't eat the clothing that has this flag. -#define INEDIBLE_CLOTHING (1<<15) +#define INEDIBLE_CLOTHING (1<<14) /// Headgear/helmet allows internals -#define HEADINTERNALS (1<<16) +#define HEADINTERNALS (1<<15) /// Prevents masks from getting adjusted from enabling internals -#define INTERNALS_ADJUST_EXEMPT (1<<17) +#define INTERNALS_ADJUST_EXEMPT (1<<16) /// Indicates that the piece of clothing contributes towards Sleeping Carp's style factor, which determines evasion probabilities. See /datums/martial/sleeping_carp/carp_style_check(). -#define CARP_STYLE_FACTOR (1<<18) +#define CARP_STYLE_FACTOR (1<<17) /// Prevents clothing from losing bodyparts coverage when shredded -#define NO_ZONE_DISABLING (1<<19) +#define NO_ZONE_DISABLING (1<<18) /// Integrity defines for clothing (not flags but close enough) #define CLOTHING_PRISTINE 0 // We have no damage on the clothing diff --git a/code/__DEFINES/research.dm b/code/__DEFINES/research.dm index f2be6fef6dac..b6708c7568c3 100644 --- a/code/__DEFINES/research.dm +++ b/code/__DEFINES/research.dm @@ -48,24 +48,31 @@ #define CELL_LINE_TABLE_GRAPE "cell_line_grape_table" #define CELL_LINE_TABLE_HAUBEROACH "cell_line_hauberoach_table" #define CELL_LINE_TABLE_MEGACARP "cell_line_megacarp_table" +#define CELL_LINE_TABLE_PENGUIN "cell_line_penguin_table" #define CELL_LINE_TABLE_MOUSE "cell_line_mouse_table" #define CELL_LINE_TABLE_PINE "cell_line_pine_table" #define CELL_LINE_TABLE_PUG "cell_line_pug_table" +#define CELL_LINE_TABLE_FOX "cell_line_fox_table" +#define CELL_LINE_TABLE_STOAT "cell_line_stoat_table" +#define CELL_LINE_TABLE_RABBIT "cell_line_rabbit_table" +#define CELL_LINE_TABLE_MOTHROACH "cell_line_mothroach_table" #define CELL_LINE_TABLE_SLIME "cell_line_slime_table" #define CELL_LINE_TABLE_SNAKE "cell_line_snake_table" +#define CELL_LINE_TABLE_LIZARD "cell_line_lizard_table" #define CELL_LINE_TABLE_SNAIL "cell_line_snail_table" #define CELL_LINE_TABLE_VATBEAST "cell_line_vatbeast_table" #define CELL_LINE_TABLE_NETHER "cell_line_nether_table" #define CELL_LINE_TABLE_GLUTTON "cell_line_glutton_table" #define CELL_LINE_TABLE_CLOWNANA "cell_line_clownana_table" #define CELL_LINE_TABLE_LONGFACE "cell_line_longface_table" -#define CELL_LINE_TABLE_FROG "cell_line_frog_table" +#define CELL_LINE_TABLE_FROG "cell_line_frog_table" #define CELL_LINE_TABLE_AXOLOTL "cell_line_axolotl_table" #define CELL_LINE_TABLE_WALKING_MUSHROOM "cell_line_walking_mushroom_table" #define CELL_LINE_TABLE_QUEEN_BEE "cell_line_bee_queen_table" #define CELL_LINE_TABLE_BUTTERFLY "cell_line_butterfly_table" #define CELL_LINE_TABLE_MEGA_ARACHNID "cell_line_table_mega_arachnid" #define CELL_LINE_TABLE_GOAT "cell_line_table_goat" +#define CELL_LINE_TABLE_SHEEP "cell_line_table_SHEEP" #define CELL_LINE_TABLE_PIG "cell_line_table_pig" //! Biopsy cell line organ types diff --git a/code/__DEFINES/research/techweb_nodes.dm b/code/__DEFINES/research/techweb_nodes.dm index baf220fda688..207603d78d39 100644 --- a/code/__DEFINES/research/techweb_nodes.dm +++ b/code/__DEFINES/research/techweb_nodes.dm @@ -44,6 +44,7 @@ #define TECHWEB_NODE_EXP_TOOLS "exp_tools" #define TECHWEB_NODE_EXPLOSIVES "explosives" #define TECHWEB_NODE_EXTREME_OFFICE "extreme_office" +#define TECHWEB_NODE_FISHING_ANOMALOUS "fishing_anomalous" #define TECHWEB_NODE_FISHING_EQUIP "fishing_equip" #define TECHWEB_NODE_FISHING_EQUIP_ADV "fishing_equip_adv" #define TECHWEB_NODE_FOOD_PROC "food_proc" diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 60f6cbbd7625..aaf1fbf50e26 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -4,7 +4,6 @@ #define LAW_ZEROTH "zeroth" #define LAW_INHERENT "inherent" #define LAW_SUPPLIED "supplied" -#define LAW_ION "ion" #define LAW_HACKED "hacked" //AI notification defines @@ -81,7 +80,6 @@ /// Default view range for finding targets. #define DEFAULT_SCAN_RANGE 7 //Amount of time that must pass after a Commissioned bot gets saluted to get another. -#define BOT_COMMISSIONED_SALUTE_DELAY (60 SECONDS) //Bot mode defines displaying how Bots act ///The Bot is currently active, and will do whatever it is programmed to do. diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index d352a64dcc8e..7aaf3e433741 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -109,7 +109,7 @@ /// Range to hear whispers normally #define WHISPER_RANGE 1 /// Additional range to partially hear whispers -#define EAVESDROP_EXTRA_RANGE 1 //how much past the specified message_range does the message get starred, whispering only +#define EAVESDROP_RANGE 1 //how much past the specified message_range does the message get starred, whispering only /// How close intercoms can be for radio code use #define MODE_RANGE_INTERCOM 1 diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm index 34b0bf4242a5..0aae3db833d2 100644 --- a/code/__DEFINES/shuttles.dm +++ b/code/__DEFINES/shuttles.dm @@ -112,6 +112,8 @@ #define SHUTTLE_UNLOCK_WABBAJACK "wabbajack" // Needs cargo budget to be almost empty to be purchasable. #define SHUTTLE_UNLOCK_SCRAPHEAP "scrapheap" +// Needs biohazard to be triggered - Blob, Zombies, or a Severe Disease +#define SHUTTLE_UNLOCK_TOMBSTONE "tombstone" //Shuttle Events diff --git a/code/__DEFINES/skin.dm b/code/__DEFINES/skin.dm index 45702762a97f..20c2390bc6cd 100644 --- a/code/__DEFINES/skin.dm +++ b/code/__DEFINES/skin.dm @@ -16,6 +16,9 @@ #define SKIN_TGUISAY "tgui_say" #define SKIN_TGUISAY_BROWSER "[SKIN_TGUISAY].browser" +#define SKIN_ESCAPE_MENU "[SKIN_MAPWINDOW].escape_menu" +#define ESCAPE_MENU_TOGGLE_MESSAGE "%7B%22type%22%3A%22toggle%22%2C%22payload%22%3Anull%7D" + #define SKIN_DEFAULT "default" #define SKIN_DEFAULT_SHIFT "[SKIN_DEFAULT].Shift" #define SKIN_DEFAULT_SHIFTUP "[SKIN_DEFAULT].ShiftUp" diff --git a/code/__DEFINES/song.dm b/code/__DEFINES/song.dm index c565dde7e71f..9df8ebf2ba9b 100644 --- a/code/__DEFINES/song.dm +++ b/code/__DEFINES/song.dm @@ -13,7 +13,8 @@ ///it's what monkeys play! #define MONKEY_SONG "BPM: 200\nC4/0,14,C,A4-F2,F3,A3,F-F2,A-F,F4,G4,F,D4-Bb2-G2\nD3,G3,D-G2,G3-G2,D,D4-G3,D,B4-B2,G,B3,G-B2,B3-B2\nG4,A4,G,E4-C3,E3,G3,E-C,G-C,E,E4-G,E,C5-E-A3,C4\nA-E3,C,E4-C3,A4-C4,B4-A3-A2,C5-C4,D5-F-B3,D4,B-F3\nD,F4-D3,D4,F-B-B2,G4-D,A4-C-F3,F,C/2,B3/2,A3-C3/2\nB/2,C4,E-C3,F4,G-C,F-F3,F-C,C4/2,B/2,A-A2/2,G3/2\nF/I" -///song played by the mook bard +///PUN PUN..PUNKEY KUN... +#define GORILLA_SONG "BPM: 80\nD6-A5-F#-D5-A4-D3-F#-D5-A/1.5,A6-D3/1.5,D\nD6-D3/1.5,A-D/1.5,D6-D3/3,A-D/1.5,D6-D3/1.5\nC#6-A5-E-C#5-A4-A2-E-C#-A4/1.5,A6-A2/1.5,A\nA/1.5,C#6-A/1.5,A6-A2/3,A-A5-E-C#5/3,C#6/3\nA6-A2/1.5,D6-B5-F#-D5-B4-B2-F#-D-B4/1.5\nA6-B2/1.5,B,D6-B/1.5,A-B/1.5,D-B/3,A-B/1.5\nD-B/1.5,D-G5-D5-G4-D4-G2-A4-D/1.5,A6-G/1.5,G/3\nD6/1.5,C#-A2/3,A5-E-C#5-A4-A-E4/3,A6-A2/1.5\nC#6-A/3,A6-A2/1.5,C#-A/1.5\nD-G5-D5-G4-D4-G2-A4-D/1.5,A6-G/1.5,G/3,D6/1.5\nC#-A2/3,A5-E5-C#5-A4-A-E4/3,A6-A2/1.5,C#6-A/3\nA6-A2/1.5,C#-A/1.5,F#4-D-A5-F#5-D5-A4-D3/1.5\nA-A6-D/1.5,G4-D/3,F#4/3,E/3,D4-D3/3,D6/3\nA3-D3/3,A-A6/3,A3-D/3,B3-D/1.5,D4-D3/1.5\nE-A6-A5-E5-C#5-A4-A2/1.5,C#6-A/1.5,A,A/3,A6/3\nA2/3,A3-C#/3,A-A2/3,B-A/1.5,D4-A/1.5\nE4-B5-B-F#5-D5-B4-B2/1.5,D6-B/1.5,B,B/3,B5/3\nB2/3,D/3,B/3,B/1.5,B/3,F#4/3\nF#-B5-G5-D5-G4-D4-G2/1.5,A4-D6-G/1.5,G4-G2/3\nF#/3,E/3,D4-A2/3,A5-A-E5-C#5-A4/3,A2/3,A3-D6/3\nA-A2/3,B3-A/1.5,D4-A/1.5\nE4-B5-G5-D5-G4-D4-G2/1.5,D6-G/1.5,G,A/3\nA5-A-E5-C#-A4/3,A2/3,D/3,A/3,A/1.5,A/3,F#/3\nF#-D-A5-F#5-D5-A4-D3-F#-D5-A/1.5,A-A6-D3/1.5\nG4-D/3,F#4/3,E4/3,D4-D3/3,D6/3,D3/3,A3-A6/3\nA3-D/3,B3-D/1.5,D4-D3/1.5\nE-A6-A5-E5-C#-A4-A2-E-C#-A4/1.5,C#6-A2/1.5,A\nA/3,A6/3,A2/3,A3-C#/3,A-A2/3,B-A-A5-E-C#5/1.88\nD4/7.5,A2/3,B/3,B5-B-F#5-D5-B4-B2-F#-D-B4/3\nE4/3,D6-B2/1.5,B,B3-B2/3,B5/3,B2/3,D/3,B/3\nB/1.5,B/3,F#4/3,F#-B5-G5-D5-G4-D4-G2-A4-D/1.5\nA-D6-G/1.5,G4-G2/3,F#/3,E/3,D4-A2/3\nA5-A-E5-C#-A4-A-E4/3,A2/3,A3-D6/3,A-A2/3\nB3-A/1.5,D4-A/1.5,E-B5-G5-D5-G4-D4-G2-A4-D/1.5\nD6-G/1.5,G,A2/3,A5-A-E5-C#-A4-A-E4/3,A2/3\nA3-D/3,A-A2/3,B3-A/1.5,D4-F#5-A/1.5\nF#4-D6-A5-F#5-D5-A4-D3-A5/3,A/3,A6-D-A5/3,A/7.5\nB5/7.5,D/1.5,A6/3,D6-A5-D3/1.5,A6-A5-D/3,A/3\nD6-A-D3/7.5,B/5,A6-D/1.5,A-D6-D3/3,A5/3\nC#6-A-E5-C#5-A4-A2/3,A5/3,A-A6-A2/3,A5/7.5,B/5\nA2/1.5,A6/3,A5-A2/1.5,A5-C#6-A2/3,A5/3\nA-A6-A2/7.5,B/5,A/3,C#/3,A6-A-A2/3,B/3\nA5-D6-B-F#-D5-B4-B2/3,A/3,A-A6-B/3,A5/7.5,B5/5\nB2,D6-B/3,A/3,A-A6-B/3,A5/3,A-D-B/7.5,B5/5\nA6-B2/1.5,D-B/1.5,D-G5-D5-G4-D4-G2/1.5,A-G/1.5\nG/3,D6/1.5,C#-A2/3,A5-E-C#5-A4/3,A6-A2/1.5\nC#6-A/3,A6-A2/1.5,C#-A/1.5,D-G5-D5-G4-D4-G2/1.5\nA6-G/1.5,G/3,D6/1.5,C#-A2/3,A5-E-C#5-A4/3\nA6-A2/1.5,C#6-A/3,A6-A2/1.5,C#-A/3,F#4/3\nF#-D-A5-F#5-D5-A4-D3-F#-D5-A/1.5,A-A6-D3/1.5\nG4-D/3,F#4/3,E4/3,D4-D6-D3/1.5,D6-D3/3,A3-A6/3\nA3-D/3,B3-D/1.5,D4-D3/1.5\nE-A6-A5-E5-C#5-A4-A2-E-C#-A4/3,F#5/3,A2/3,A5/3\nA2/3,C#6/1.5,D6-A/3,A6/3,C#-A2/1.5,B-A5-A2/3\nB-A-A5-E-C#5/3,D4-F#/3,D-A2/3,B-B5/3\nE4-B-B-F#-D5-B4-B2-F#-D-B4/3,B5/3,D6-B2/3\nB3-B5/3,B2/3,B5/1.5,B-B2/3,B5-B/3,B-B2/3,D/3\nB5-B2/3,B/3,B5/3,F#4-B2/3,F#-B5/3\nF#-B-G5-D5-G4-D4-G2-A4-D/3,B/3,A-D6-G/3,B/3\nG4-G2/3,F#-B/3,E/3,D4-C#6-A2/3\nA5-A-E5-C#5-A4-A-E4/3,C#6-A2/3,A3-D6/3\nA-C#-A2/3,B3-A/3,C#/3,D4-A/3,D6/3\nE-B5-G5-D5-G4-D4-G2-A4-D/3,D6/3,D-G/3,D/3,G/3\nD/1.5,E6-A2/3,A5-A-E5-C#5-A4-A-E4/3,E6-A2/3,D/3\nE-A/3,A/3,E/3,A/3,D/3\nD-D-A5-F#5-D5-A4-D3-F#-D5-A/3,D6/3,A6-A-D3/3\nD6/3,D3/3,D6/1.5,D-D-D-D3/1.5,A-D6-D-D3/3,A/3\nD6-D-D3/3,A-D/3,D6/3,D-D3/3,D6/3\nC#6-A-A5-E5-C#5-A4-A2-E-C#-A4/3,F#/3,A6-A2/3\nA5/3,A2/3,C#6/1.5,D-A/3,A6/3,C#-C#-A2/1.5\nA6-A5-A2/3,A-A5-E-C#5/3,C#6-F#/3,A6-A2/3,B/3\nD-B-B-F#-D5-B4-B2-F#-D-B4/3,B5/3,A6-D6-B2/3\nB5/3,B2/3,B5/1.5,D-B-B2/3,B5-B/3,A-B-B2/3,D/3\nD-B5-B2/3,A-B/3,B5/3,D-B2/3,F#4-B5/3\nF#-D-B-G5-D5-G4-D4-G2-A4-D/3,B/3,A-A6-D6-G/3\nB/3,G4-G2/3,F#-D-B/3,E4/3,D4-C#-C#-A2/3\nA5-A-E5-C#5-A4-A-E4/3,A6-C#6-A2/3,A3-D6/3\nA-C#-C#-A2/3,B3-A6-A2/3,C#/3,D4-C#-A/3,D6/3\nE-D-B5-G5-D5-G4-D4-G2-A4-D/3,D6/3,A6-D-G/3,D/3\nG/3,D-D/1.5,C#-E6-A2/3,A5-A-E5-C#5-A4-A-E4/3\nA6-E6-A2/3,D/3,C#6-E-A/3,A6-A2/3,E/3,C#-A/3,D/3\nA5-F#-D-A-F#5-D5-A4-D3-F#-D5-A/3,A5-D6/3\nA-A4-A6-D3/3,A5-D6/7.5,B5/5,G4-D3/3,F#4-D6/3\nA6-E4/3,A5-F#-D-D-D3/3,D4/3,A-D6-D-D3/3,A-A6/3\nA5-D6-D3/7.5,B/5,D/3,D6/3,A6-D3/3,A5-D6/3\nA6-A5-E5-C#5-A4-A2-E-C#-A4/3,A5-F#5/3,A-A2/3\nA5-A/7.5,B/5,A2/3,C#6/3,A6/3,A5-D-A2/3,A6/3\nA5-C#-A2/3,A5/3,A-A3-A5-A2/7.5,B/5\nB3-A-A5-E-C#5/3,F#/7.5,D4/7.5,A6-A2/3,B5-B/7.5\nE4/7.5,A5-B-B-F#-D5-B4-B2-F#-D-B4/3,A-B5/3\nA-D6-B2/3,A-B5/7.5,B/5,B2/3,B5/1.5,B3-B5-B2/3\nA-B5-B/3,A-B-B2/3,A-D/3,A-B5-B2/7.5,B5/5,B2/3\nB5/3,B2/3,F#4-B5/3,F#-B-G5-D5-G4-D4-G2-A4-D/3\nB/3,A-D6-G/3,B/3,G4-G2/3,F#-B/3,E/3,D4-C#6-A2/3\nA5-A-E5-C#5-A4-A-E4/3,C#6-A2/3,A3-D6/3\nA-C#-A2/3,B3-A/3,C#/3,D4-A/3,D6/3\nE-B5-G5-D5-G4-D4-G2-A4-D/3,D6/3,D-G/3,D/3,G/3\nD/1.5,E6-A2/3,A5-A-E5-C#5-A4-A-E4/3,E6-A2/3\nA3-D/3,A-E-A2/3,B3-A/3,E/3,D4-A/3,D6/3\nA5-F#-D-D-A-F#5-D5-A4-D3-F#-D5-A/3,A5-D6/3\nA-A6-A-D3/3,A5-D6/7.5,B5/5,D3/3,D6/3,A6/3\nA5-D-D-D-D3/1.5,A-A6-D6-D-D3/3,A5-A6/3\nA5-D6-D-D3/7.5,B/5,A6-D/3,D6/3,A-D-D3/3,A5-D6/3\nC#6-A6-A5-E5-C#5-A4-A2-E-C#-A4/3,A5-F#/3\nA-A6-A2/3,A5-A/7.5,B/5,A2/3,C#6/3,A6/3\nA5-D-A2/3,A6/3,A5-C#-C#-A2/3,A5/3\nA-A6-A5-A2/7.5,B/5,A-A5-E-C#5/3,C#6-F#/3\nA6-A-A2/3,B-B/3,A5-D-B-B-F#-D5-B4-B2-F#-D-B4/3\nA-B5/3,A-A6-D6-B2/3,A5-B5/7.5,B/5,B2/3,B5/1.5\nD-B-B2/3,A-B5-B/3,A-A6-B-B2/3,A5-D/3\nA-D-B5-B2/7.5,B5/5,A6-B2/3,B5/3,D-B2/3,B5/3\nD-B-G5-D5-G4-D4-G2-A4-D/3,B/3,A6-D6-G/3,B/3,G/3\nD-B/1.5,C#-C#-A2/3,A5-A-E-C#5-A4-A-E4/3\nA6-C#6-A2/3,D/3,C#-C#-A/3,A6-A2/3,C#/3,C#-A/3\nD/3,D-B-G5-D5-G4-D4-G2-A4-D/3,D6/3,A6-D-G/3,D/3\nG/3,D-D/1.5,C#-E6-A2/3,A5-A-E5-C#5-A4-A-E4/3\nA6-E6-A2/3,A3-D/3,A-C#6-E-A2/3,B3-A6-A2/3,E/3\nD4-C#-A/3,D6/3\nA5-F#4-D-D-A-F#5-D5-A4-D3-F#-D5-A/3,A5-D6/3\nA-A6-A-D3/3,A5-D6/7.5,B5/5,D3/3,D6/3,A6/3\nA5-D-D-D-D3/1.5,A-A6-D6-D-D3/3,A5-A6/3\nA5-D6-D-D3/7.5,B/5,A6-D/3,D6/3,A-D-D3/3,A5-D6/3\nC#-A6-A5-E5-C#5-A4-A2-E-C#-A4/3,A5-F#/3\nA-A6-A2/3,A5-A/7.5,B/5,A2/3,C#6/3,A6/3\nA5-D-A2/3,A6/3,A5-C#-C#-A2/3,A5/3\nA-A6-A5-A2/7.5,B/5,A-A5-E-C#5/3,C#6-F#/3\nA6-A-A2/3,B-B/3,A5-D-B-B-F#-D5-B4-B2-F#-D-B4/3\nA-B5/3,A-A6-D6-B2/3,A5-B5/7.5,B/5,B2/3,B5/1.5\nD-B-B2/3,A-B5-B/3,A-A6-B-B2/3,A5-D/3\nA-D-B5-B2/7.5,B5/5,A6-B2/3,B5/3,D-B2/3,B5/3\nD-B-G5-D5-G4-D4-G2-A4-D/3,B/3,A6-D6-G/3,B/3,G/3\nD-B/1.5,C#-C#-A2/3,A5-A-E-C#5-A4-A-E4/3\nA6-C#6-A2/3,D/3,C#-C#-A/3,A6-A2/3,C#/3,C#-A/3\nD/3,D-B-G5-D5-G4-D4-G2-A4-D/3,D6/3,A6-D-G/3,D/3\nG/3,D-D/1.5,C#-E6-A2/3,A5-A-E5-C#5-A4-A-E4/3\nA6-E6-A2/3,D/3,C#6-E-A/3,A6-A2/3,E/3,C#-A/3,D/3\nA5-D-D-A-F#-D5-A4-D3-F#-D5-A/3,A5-D6/3\nA-A6-A-D3/3,A5-D6/7.5,B/5,D3/3,D6/3,A6/3\nA5-D-D-D-D3/1.5,A-A6-D6-D-D3/3,A5-A6/3\nA5-D6-D-D3/7.5,B/5,A6-D/3,D6/3.75,A-D-D3/3.75\nA5-D6/3,C#-A6-A5-E5-C#5-A4-A2-E-C#-A4/3.75\nA5-F#/3.75,A-A6-A2/3.75,A5-A/7.5,B/5,A2/3\nC#6/3.75,A6/3,A5/15,D-A2/3,A6/3.75,A5/15\nC#-C#-A2/3.75,A5/3,A/15,A6-A5-A2/7.5,B/5\nA-A5-E-C#5/3,C#6-F#/3.75,A6/15,A-A2/3.75,B/15\nB/3.75,A5/15,D-B-B-F#-D5-B4-B2-F#-D-B4/3.75\nA/15,B5/3.75,A/15,A6-D6-B2/3.75,A5/15,B5/7.5\nB/5,B2/3,B5/1.5,D-B-B2/3.75,A/15,B5-B/3.75,A/15\nA6-B-B2/3.75,A5/15,D/3.75,A/15,D-B5-B2/7.5,B5/5\nA6-B2/3,B5/3,D-B2/3,B5/3,D-G5-D5-G4-D4-G2/3,B/3\nA-G/3,B/3,G/3,D6-B/1.5,C#-C#-A2/3,A5-E-C#5-A4/3\nA6-C#6-A2/1.5,C#-C#-A/3,A6-A2/3,C#/3,C#-A/3,D/3\nD-G5-D5-G4-D4/1.5,A6,D6/1.5,C#/3,A5-E-C#5-A4/3\nA6/1.5,C#6/3,A/1.5,C#/1.5,A5-D5/0.75" #define MOOK_SONG "BPM: 240\nA5,B5,C#6,D6,E6/0.17,A/0.5,A/0.25,A3/0.25\nA4/0.25,C#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.12\nC#6/0.25,C#/0.25,E6/0.25,A3/0.25,A4/0.25\nC#5/0.25,E5/0.25,A/0.25,C#/0.25,E/0.25,D/0.25\nG6/0.25,D/0.17,F6/0.17,C#6/0.5,E6/0.5,D4/0.25\nA/0.25,D5/0.25,F5/0.25,A/0.25,D/0.25,F/0.25\nD6/0.08,F6/0.08,D4/0.25,A/0.25,D5/0.25,F5/0.25\nCn4/0.2,B/0.17,D6/0.17,G5/0.5,G/0.25,B3/0.25\nD4/0.25,G4/0.25,B4/0.25,D/0.25,G/0.25,B/0.12\nB5/0.25,B/0.25,D6/0.25,G3/0.25,G4/0.25,B4/0.25\nF/0.25,G/0.25,B/0.25,F/0.25,D/0.25,F6/0.25\nC6/0.17,E/0.17,B5/0.5,D#/0.5,C4/0.25,G/0.25\nC5/0.25,E5/0.25,G/0.25,C/0.25,E/0.25,C6/0.08\nE6/0.08,C4/0.25,Dn4/0.25,E4/0.25,A5/0.17,B/0.5\nC6/0.25,F5/0.08,F4/0.08,C5/0.08,E5/0.12,G5/0.12\nC6/0.25,E6/0.25,E4/0.08,C5/0.08,B/0.17,F6/0.17\nE6/0.5,B/0.25,E4/0.08,G#4/0.08,C6/0.17,D6/0.5\nE6/0.25,A3/0.25,E4/0.25,C5/0.25,Gn3/0.25\nF5/0.12,A5/0.12,A6/0.25,F3/0.25,F4/0.12,A4/0.12\nC/0.12,F6/0.17,A6/0.17,G#6/0.5,A/0.25,F3/0.25\nF4/0.12,A4/0.12,D#5/0.12,B/0.17,G#/0.17,B6/0.5\nB5/0.25,G#/0.25,E3/0.25,E4/0.12,G#4/0.12\nDn/0.12,E6/0.08,E3/0.25,F#3/0.25,G#3/0.25\nE5/0.17,A5/0.17,E/0.5,E/0.25,A3/0.25,C#4/0.25\nE4/0.25,C#/0.25,E/0.12,A5/0.5,B/0.5,C#6/0.5\nD6/0.5,A3/0.25,C#4/0.25,E/0.25,C#/0.25,E/0.25\nE6/0.08,Gn/0.25,E4/0.25,A4/0.25,C#5/0.25,E/0.25\nA/0.25,C#/0.25,E6/0.17,E/0.5,Fn6/0.5,G6/0.5\nG3/0.25,E4/0.25,A/0.25,C#/0.25,E/0.25,A/0.25\nC#/0.25,F/0.08,A6/0.08,F3/0.25,F4/0.25,A4/0.25\nCn/0.25,F/0.25,A/0.25,C/0.25,G6/0.12,A6/0.12\nG A G F6 G3/0.25 D4/0.25 G4/0.25 B4/0.25 D/0.25\nG/0.25 B/0.25 E6/0.12 G6/0.12 F/0.71 G/0.71 F/0.71\nE3/0.25 E4/0.25 G4/0.25 B/0.25 E/0.25 G/0.25 B/0.25\nA5/0.08 E6/0.08 A3/0.25 E4/0.25 A4/0.25 C#/0.25 E/0.25 A/0.25 C#/0.25 D6/0.17 E6/0.5 F/0.25 B3/0.25 D4/0.12 F4/0.12 B4/0.12 F6/0.25 E/0.25 D6/0.25 G#3/0.25 E4/0.12 G#4/0.12 B/0.12 Cn6/0.12 D/0.25 A3/0.25 A4/0.25 C5/0.25 E5/0.25 G#3/0.25 Gn/0.25 C4/0.25 E4/0.25 A/" ///song played by the vibebot when cheering people up #define VIBEBOT_CHEER_SONG "BPM: 360\nE4/0.5,B4-G4/0.5,G-B-E/0.5,D4,Gb,A4-D-G/0.25\nGn-B3-E/0.5,G-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E,G\nB4/0.5,E-B-G/0.5,D,Gb,A4-D-G/0.25,B3-Gn-E/0.5\nG-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E-G-C5/0.5,G,E,G\nC,B4-Eb-Gb/0.5,B3-E-G/0.5,B-E-G3/0.5,C-Gn4-En/0.5\nG,E,G,C,Eb-Gb-B4/0.17,C-Gn-En/0.5,G,E,G,C\nB-C-Eb-Gb/0.5,B3-E-G/0.5,E-B-G3/0.5,C-En-Gn4,E,G,C\nE,G,A4-Gb-D5/0.17,E/0.5,B4-Gn/0.5,G-B-E/0.5,D4,Gb\nA-D-G/0.25,Gn-B3-E/0.5,G-B-E/0.5,A3-D-Gb/0.5\nE-Gn-B/0.17,E,G,B4/0.5,E-B-G/0.5,D,Gb,A4-D-G/0.25\nB3-Gn-E/0.5,G-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17\nE-G-C/0.5,G,E,G,C,B4-Eb-Gb/0.5,B3-E-G/0.5\nB-E-G3/0.5,C-Gn4-En/0.5,G,E,G,C,Eb-Gb-B4/0.17\nC-Gn-En/0.5,G,E,G,C,B-C-Eb-Gb/0.5,B3-E-G/0.5\nE-B-G3/0.5,C-En-Gn4,E,G,C,E,G,A4-Gb-D5/0.17\nGn-E-B/0.5,B-G3-E,E3-G-B,G4-E4-B/0.5,A3-D4-Gb/0.5\nA-D-G3/0.5,D3-G-A/0.5,Gn4-E-B/0.5,B-G3-E,E3-G-B\nG4-E4-B/0.5,A-D4-Gb/0.17,A4-F4-C4/0.5,C-A3-F\nF3-A-C,C-F4-A4/0.5,B-Gn-E/0.5,B-G3-E/0.5\nG-E3-B/0.5,A3-D-Gb4/0.5,D-A-G3/0.5,D3-A-G/0.5\nA-D4-G4/0.17,Gn-E4-B/0.5,B-G3-E,E3-G-B,G4-E4-B/0.5\nA-D-Gb/0.5,A-D-G3/0.5,D3-G-A/0.5,Gn4-E-B/0.5\nB-G3-E,E3-G-B,G4-E4-B/0.5,A-D4-Gb/0.17,A4-F-C/0.5\nC-A3-F,F3-A-C,C-F4-A4/0.5,B-Gn-E/0.5,B-G3-E/0.5\nG-E3-B/0.5,A3-D-Gb4/0.5,D-A-G3/0.5,D3-A-G/0.5\nE4-A-Db4/0.17,A2,E3,Gn-B-E4,A,E3,B-G-E4\nA3-Dn-F-D3/0.5,F3-A-D4/0.5,D3/0.5,A2,E3,B-E4-G,A\nE3,B-G-E4,F-D4-A3/0.17,A2,E3,B-E4-G,A,E3,B-G-E4\nB-A3-F4-D-D3/0.5,F3-D4-A/0.5,F-A-D3/0.5,A2,E3\nB-E4-G,A,E3,B-E4-G,F-A3-D4-D3/0.17,A2,E3,G-B-E4,A\nE3,B-G-E4,A3-D4-F4-D3/0.5,F3-A-D4/0.5,D3/0.5,A2,E3\nB-E4-G,A,E3,B-G-E4,F-D4-A3/0.17,A2,E3,B-E4-G,A,E3\nB-G-E4,B-A3-F4-D-D3/0.5,F3-D4-A/0.5,F-A-D3/0.5,A2\nE3,B-E4-G,A,E3,B-E4-G,F-A3-D4-D3/0.17,E/0.5\nB4-G4/0.5,G-B-E/0.5,D4,Gb,A4-D-G/0.25,Gn-B3-E/0.5\nG-B-E/0.5,A3-D-Gb/0.5,E-Gn-B/0.17,E,G,B4/0.5\nE-B-G/0.5,D,Gb,A4-D-G/0.25,B3-Gn-E/0.5,G-B-E/0.5\nA3-D-Gb/0.5,E-Gn-B/0.17,E-G-C5/0.5,G,E,G,C\nB4-Eb-Gb/0.5,B3-E-G/0.5,B-E-G3/0.5,C-Gn4-En/0.5,G\nE,G,C,Eb-Gb-B4/0.17,C-Gn-En/0.5,G,E,G,C\nB-Eb-Gb/0.5,B3-E-G/0.5,E-B-G3/0.53,En-Gn4/8,C,G,E\nB4,G,E,B3-E-G/0.07" diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm index c622c5831009..29f0bb1246c2 100644 --- a/code/__DEFINES/span.dm +++ b/code/__DEFINES/span.dm @@ -87,17 +87,17 @@ #define span_rlooc(str) ("" + str + "") // DARKPACK EDIT ADD #define span_major_announcement_text(str) ("" + str + "") #define span_major_announcement_title(str) ("" + str + "") +#define span_mansus(str) ("" + str + "") #define span_medal(str) ("" + str + "") #define span_medradio(str) ("" + str + "") #define span_memo(str) ("" + str + "") #define span_memoedit(str) ("" + str + "") -#define span_policy(str) ("" + str + "") #define span_mentor(str) ("" + str + "") // DARKPACK EDIT ADD - MENTOR #define span_mentornotice(str) ("" + str + "") // DARKPACK EDIT ADD - MENTOR #define span_message(str) ("" + str + "") #define span_mind_control(str) ("" + str + "") -#define span_minorannounce(str) ("" + str + "") #define span_minoralert(str) ("" + str + "") +#define span_minorannounce(str) ("" + str + "") #define span_monkey(str) ("" + str + "") #define span_name(str) ("" + str + "") #define span_narsie(str) ("" + str + "") @@ -110,6 +110,7 @@ #define span_ooc_announcement_text(str) ("" + str + "") #define span_papyrus(str) ("" + str + "") #define span_phobia(str) ("" + str + "") +#define span_policy(str) ("" + str + "") #define span_prefix(str) ("" + str + "") #define span_priorityalert(str) ("" + str + "") #define span_priorityannounce(str) ("" + str + "") diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm index 955e046edefc..0e285fde3abf 100644 --- a/code/__DEFINES/stat.dm +++ b/code/__DEFINES/stat.dm @@ -1,13 +1,8 @@ -/* - Used with the various stat variables (mob, machines) -*/ - -//mob/var/stat things -#define CONSCIOUS 0 +// Mob health stats +#define STABLE 0 #define SOFT_CRIT 1 -#define UNCONSCIOUS 2 -#define HARD_CRIT 3 -#define DEAD 4 +#define HARD_CRIT 2 +#define DEAD 3 //Maximum healthiness an individual can have #define MAX_SATIETY 600 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 739dfd190677..fe0d904e80c2 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -127,7 +127,6 @@ // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) -#define FIRE_PRIORITY_UNPLANNED_NPC 3 #define FIRE_PRIORITY_IDLE_NPC 5 #define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_SERVER_MAINT 10 @@ -137,13 +136,13 @@ #define FIRE_PRIORITY_BLOOD_DRYING 10 #define FIRE_PRIORITY_GARBAGE 15 #define FIRE_PRIORITY_DATABASE 16 +#define FIRE_PRIORITY_NPC_LOW 19 #define FIRE_PRIORITY_WET_FLOORS 20 #define FIRE_PRIORITY_AIR 20 -#define FIRE_PRIORITY_NPC 20 #define FIRE_PRIORITY_ASSETS 20 #define FIRE_PRIORITY_HYPERSPACE_DRIFT 20 -#define FIRE_PRIORITY_NPC_MOVEMENT 21 -#define FIRE_PRIORITY_NPC_ACTIONS 22 +#define FIRE_PRIORITY_NPC 21 +#define FIRE_PRIORITY_NPC_MOVEMENT 22 #define FIRE_PRIORITY_PATHFINDING 23 #define FIRE_PRIORITY_CLIFF_FALLING 24 #define FIRE_PRIORITY_PROCESS 25 @@ -164,6 +163,7 @@ #define FIRE_PRIORITY_TGUI 110 #define FIRE_PRIORITY_TICKER 200 #define FIRE_PRIORITY_SINGULO 350 +#define FIRE_PRIORITY_TIMED_ACTIONS 360 #define FIRE_PRIORITY_STATPANEL 390 #define FIRE_PRIORITY_CHAT 400 #define FIRE_PRIORITY_RUNECHAT 410 diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm index ca90b2940d6a..f5786c75a5dd 100644 --- a/code/__DEFINES/tgui.dm +++ b/code/__DEFINES/tgui.dm @@ -11,6 +11,8 @@ #define TGUI_WINDOW_SOFT_LIMIT 5 /// Maximum number of open windows #define TGUI_WINDOW_HARD_LIMIT 9 +/// Maximum number of open windows when user has prefs set to unlimited, basically infinite. You dont need 200 windows; you'll run out of memory by then +#define TGUI_WINDOW_UNLIMITED_LIMIT 200 /// Maximum ping timeout allowed to detect zombie windows #define TGUI_PING_TIMEOUT (4 SECONDS) diff --git a/code/__DEFINES/traits/_traits.dm b/code/__DEFINES/traits/_traits.dm index c873152d2d52..0a8eaee4220b 100644 --- a/code/__DEFINES/traits/_traits.dm +++ b/code/__DEFINES/traits/_traits.dm @@ -113,13 +113,13 @@ }\ } while (0) -#define HAS_TRAIT(target, trait) (target._status_traits?[trait] ? TRUE : FALSE) +#define HAS_TRAIT(target, trait) (target?._status_traits?[trait] ? TRUE : FALSE) #define HAS_TRAIT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait])) #define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]) && (length(target._status_traits[trait]) == 1)) #define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target._status_traits[trait] - source) > 0)) /// Returns a list of trait sources for this trait. Only useful for wacko cases and internal futzing /// You should not be using this -#define GET_TRAIT_SOURCES(target, trait) (target._status_traits?[trait] || list()) +#define GET_TRAIT_SOURCES(target, trait) (target?._status_traits?[trait] || list()) /// Returns the amount of sources for a trait. useful if you don't want to have a "thing counter" stuck around all the time #define COUNT_TRAIT_SOURCES(target, trait) length(GET_TRAIT_SOURCES(target, trait)) /// A simple helper for checking traits in a mob's mind diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 488b9535b83b..be043a838eeb 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -38,6 +38,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define STATION_TRAIT_MEDBOT_MANIA "station_trait_medbot_mania" #define STATION_TRAIT_PDA_GLITCHED "station_trait_pda_glitched" #define STATION_TRAIT_PREMIUM_INTERNALS "station_trait_premium_internals" +#define STATION_TRAIT_PUN_PUN_GYM_DAY "station_trait_pun_pun_gym_day" #define STATION_TRAIT_RADIOACTIVE_NEBULA "station_trait_radioactive_nebula" #define STATION_TRAIT_RANDOM_ARRIVALS "station_trait_random_arrivals" #define STATION_TRAIT_REVOLUTIONARY_TRASHING "station_trait_revolutionary_trashing" @@ -83,8 +84,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Recolored by item/greentext #define TRAIT_GREENTEXT_CURSED "greentext_curse" #define TRAIT_INCAPACITATED "incapacitated" -/// In some kind of critical condition. Is able to succumb. -#define TRAIT_CRITICAL_CONDITION "critical-condition" /// Whitelist for mobs that can read or write #define TRAIT_LITERATE "literate" /// Blacklist for mobs that can't read or write @@ -92,7 +91,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Mute. Can't talk. #define TRAIT_MUTE "mute" /// Softspoken. Always whisper. -#define TRAIT_SOFTSPOKEN "softspoken" +#define TRAIT_FORCE_WHISPER "softspoken" /// Gibs on death and slips like ice. #define TRAIT_CURSED "cursed" /// Emotemute. Can't... emote. @@ -405,15 +404,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_MESON_VISION "meson_vision" /// Gives us Night vision #define TRAIT_TRUE_NIGHT_VISION "true_night_vision" -/// Gives us minor night vision -#define TRAIT_MINOR_NIGHT_VISION "minor_night_vision" /// Negates our gravity, letting us move normally on floors in 0-g #define TRAIT_NEGATES_GRAVITY "negates_gravity" /// We are ignoring gravity #define TRAIT_IGNORING_GRAVITY "ignores_gravity" /// We have some form of forced gravity acting on us #define TRAIT_FORCED_GRAVITY "forced_gravity" -/// Makes whispers clearly heard from seven tiles away, the full hearing range +/// Allows the mob to hear whispers as if it were normal speech #define TRAIT_GOOD_HEARING "good_hearing" /// Allows you to hear speech through walls #define TRAIT_XRAY_HEARING "xray_hearing" @@ -457,6 +454,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ABDUCTOR_HUD "abductor_hud" /// Stop the user from seeing the sechud. Only works for trait handled sechuds. #define TRAIT_BLOCK_SECHUD "block_sechud" +/// Stop the user from seeing the medhud. Only works for trait handled medhuds. +#define TRAIT_BLOCK_MEDHUD "block_medhud" /// for something granting you a diagnostic hud #define TRAIT_DIAGNOSTIC_HUD "diag_hud" #define TRAIT_BOT_PATH_HUD "bot_path_hud" @@ -839,6 +838,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_IMMERSE_STOPPED "immerse_stopped" /// The effects of hyperspace drift are blocked when the tile has this trait #define TRAIT_HYPERSPACE_STOPPED "hyperspace_stopped" +/// This turf is blessed and blocks jaunts/revenants/cult TPs +#define TRAIT_TURF_BLESSED "turf_blessed" ///Turf slowdown will be ignored when this trait is added to a turf. #define TRAIT_TURF_IGNORE_SLOWDOWN "turf_ignore_slowdown" @@ -1002,8 +1003,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_HERETIC_AURA_HIDDEN "heretic_aura_hidden" /// This rod was infused by a heretic, making it awesome and improving influence gain #define TRAIT_ROD_MANSUS_INFUSED "rod_infused" -/// Allows heretics to cast their spells. -#define TRAIT_ALLOW_HERETIC_CASTING "allow_heretic_casting" /// Designates a heart as a living heart for a heretic. #define TRAIT_LIVING_HEART "living_heart" /// Trait given to all participants in a heretic arena @@ -1051,8 +1050,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_VEGETARIAN "trait_vegetarian" #define TRAIT_KEEN_NOSE "keen_nose" -/// This mob always lands on their feet when they fall, for better or for worse. -#define TRAIT_CATLIKE_GRACE "catlike_grace" +/// The mob has a variety of catlike behaviors, like landing on their feet when falling or chasing pinpointers. +#define TRAIT_CATLIKE_INSTINCT "catlike_grace" ///Won't show up on cameras when they snap a photo. #define TRAIT_INVISIBLE_TO_CAMERA "invisible_to_camera" @@ -1682,8 +1681,40 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Makes the owner immune from the pacification from synthpax #define TRAIT_SYNTHPAX_IMMUNE "synthpax_immune" +/// Allow the owner to eat cloth +#define TRAIT_CLOTH_EATER "cloth_eater" +/// Has the metabolization quirks of a lizardperson +#define TRAIT_LIZARD_METABOLISM "lizard_metabolism" +// Has the metabolization quirks of an ethereal +#define TRAIT_ETHEREAL_METABOLISM "ethereal_metabolism" /// Mobs with this trait will always have be a priority target for mining mobs #define TRAIT_MINING_AGGRO "mining_aggro" +/// Don't show the thinking indicator and therefore the typing indicator when we are about to type something... +#define TRAIT_HIDE_THINKING_INDICATOR "hide_thinking_indicator" + +// species-specific traits, used in place of deprecable is_species() checks. Feel free to use them for other things as well +#define TRAIT_NIGHTMARISH "nightmarish" +/// Allow the mob to utilize most but not all abductor equipment. Complemented by TRAIT_ABDUCTOR_KNOWLEDGE for the antagonists. +#define TRAIT_ABDUCTOR_KNOWLEDGE "abductor_knowledge" +/// Allow the mob to be instantly scanned at a distance with the abductor scanner gizmo. FOr abductors and cows. +#define TRAIT_ABDUCTOR_QUICK_SCAN "abductor_quick_scan" +/// The creature is weak to silver (or any material with the unholy bane property). Namely for vampires. +#define TRAIT_UNHOLY_BANEABLE "unholy_baneable" +/// Allows the mob to use the spooky instruments that turn people into spooky scary skeletons, while being immune to them +#define TRAIT_SPOOKY_INSTRUMENT_PLAYER "spooky_instrument_player" +/// Allows flypeople to re-ingest what has come out of their mouth. +#define TRAIT_VOMIT_SLURPER "vomit_slurper" +/// It looks, sounds and smells like a ghost. +#define TRAIT_GHOSTLY_MOB "ghostly_mob" +/// It requires an advanced health analyzer to be health-scanned. +#define TRAIT_REQUIRED_ADV_HEALTH_SCANNER "require_adv_health_scanner" + +/// Allows the wearer to cast spells that require robes and hats +#define TRAIT_CASTING_CLOTHING "casting_clothing" + +/// Turfs with this trait allow whispers to be projected over it when the whisperer is facing it +#define TRAIT_TURF_PROJECTS_WHISPERS "turf_projects_whispers" + // END TRAIT DEFINES diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 63d1d59b7312..2bc29bdf2da2 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -3,7 +3,6 @@ // common trait sources #define TRAIT_GENERIC "generic" -#define UNCONSCIOUS_TRAIT "unconscious" #define EYE_DAMAGE "eye_damage" #define EAR_DAMAGE "ear_damage" #define GENETIC_MUTATION "genetic" @@ -65,7 +64,6 @@ /// inherited from riding vehicles #define VEHICLE_TRAIT "vehicle" #define INNATE_TRAIT "innate" -#define CRIT_HEALTH_TRAIT "crit_health" #define OXYLOSS_TRAIT "oxyloss" /// Trait sorce for "was recently shocked by something" #define WAS_SHOCKED "was_shocked" @@ -174,7 +172,6 @@ #define STICKY_NODROP "sticky-nodrop" #define SKILLCHIP_TRAIT "skillchip" #define SKILL_TRAIT "skill" -#define PULLED_WHILE_SOFTCRIT_TRAIT "pulled-while-softcrit" #define LOCKED_BORG_TRAIT "locked-borg" /// trait associated to not having locomotion appendages nor the ability to fly or float #define LACKING_LOCOMOTION_APPENDAGES_TRAIT "lacking-locomotion-appengades" @@ -348,3 +345,6 @@ /// Trait granted from the metabolization of a reagent #define METABOLIZATION_TRAIT(type) "metabolize:[type]" + +/// Trait from a MODsuit theme +#define MOD_THEME_TRAIT "mod_theme_trait" diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index b345fda19950..c598c36582e7 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -70,37 +70,6 @@ GLOBAL_LIST_INIT(wound_severities_chronological, list( /// Wounds using this competition mode will remove any wounds of a lower severity than itself in a random wound roll. Used for ensuring the worse case scenario of a given injury_roll. #define WOUND_COMPETITION_OVERPOWER_LESSERS "wound_dominate" -// ~biology defines -// What kind of biology a limb has, and what wounds it can suffer -/// Has absolutely fucking nothing, no wounds -#define BIO_INORGANIC NONE -/// Has bone - allows the victim to suffer T2-T3 bone blunt wounds -#define BIO_BONE (1<<0) -/// Has flesh - allows the victim to suffer fleshy slash pierce and burn wounds -#define BIO_FLESH (1<<1) -/// Has metal - allows the victim to suffer robotic blunt and burn wounds -#define BIO_METAL (1<<2) -/// Has wood - should probably be able to catch on fire, or something -#define BIO_WOOD (1<<3) -/// Is wired internally - allows the victim to suffer electrical wounds (robotic T1-T3 slash/pierce) -#define BIO_WIRED (1<<4) -/// Has bloodflow - can suffer bleeding wounds and can bleed -#define BIO_BLOODED (1<<5) -/// Is connected by a joint - can suffer T1 bone blunt wounds (dislocation) -#define BIO_JOINTED (1<<6) -/// Skin is covered in thick chitin and is resistant to cutting -#define BIO_CHITIN (1<<7) -/// Robotic - can suffer all metal/wired wounds, such as: UNIMPLEMENTED PLEASE UPDATE ONCE SYNTH WOUNDS 9/5/2023 ~Niko -#define BIO_ROBOTIC (BIO_METAL|BIO_WIRED) -/// Has flesh and bone - See BIO_BONE and BIO_FLESH -#define BIO_FLESH_BONE (BIO_BONE|BIO_FLESH) -/// Standard humanoid - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt, except dislocations. Think human heads/chests -#define BIO_STANDARD_UNJOINTED (BIO_FLESH_BONE|BIO_BLOODED) -/// Standard humanoid limbs - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt. Can also bleed, and be dislocated. Think human arms and legs -#define BIO_STANDARD_JOINTED (BIO_STANDARD_UNJOINTED|BIO_JOINTED) -/// Xenomorph limbs (xenos are immune to wounds anyhow) -#define BIO_STANDARD_ALIEN (BIO_CHITIN|BIO_BONE|BIO_BLOODED|BIO_JOINTED) - // "Where" a specific biostate is within a given limb // Interior is hard shit, the last line, shit like bones // Exterior is soft shit, targeted by slashes and pierces (usually), protects exterior @@ -323,7 +292,6 @@ GLOBAL_LIST_INIT(biotypes_to_scar_file, list( // 1. Exterior is mangled: A critical slash or pierce wound on that limb // 2. Interior is mangled: At least a severe bone wound on that limb // Lack of exterior or interior count as mangled exterior/interior respectively -// see [/obj/item/bodypart/proc/get_mangled_state] for more information, as well as GLOB.bio_state_anatomy #define BODYPART_MANGLED_NONE NONE #define BODYPART_MANGLED_INTERIOR (1<<0) #define BODYPART_MANGLED_EXTERIOR (1<<1) diff --git a/code/__DEFINES/~darkpack/ai/ai_blackboard.dm b/code/__DEFINES/~darkpack/ai/ai_blackboard.dm new file mode 100644 index 000000000000..6189c6bc5c39 --- /dev/null +++ b/code/__DEFINES/~darkpack/ai/ai_blackboard.dm @@ -0,0 +1,2 @@ +#define BB_ABYSS_TENTACLE_MODE "BB_abyss_tentacle_mode" +#define BB_ABYSS_TENTACLE_GRABBED "BB_abyss_tentacle_grabbed" diff --git a/code/__DEFINES/~darkpack/book_sources.dm b/code/__DEFINES/~darkpack/book_sources.dm new file mode 100644 index 000000000000..0f6b4c9eb087 --- /dev/null +++ b/code/__DEFINES/~darkpack/book_sources.dm @@ -0,0 +1,9 @@ +// BOOK_SOURCES +/// Content is based on offical white wolf media +#define SOURCE_OFFICAL "Offical" +/// Contents based on media published through white wolf's storytellers vault which makes it akin to secondary canon +#define SOURCE_STORYTELLER_VAULT "Storyteller Vault" +/// Content is based on entirely homebrew (wether made up for ss13 or otherwise) +#define SOURCE_HOMEBREW "Homebrew" + +#define WE_MADE_IT_UP "Darkpack 13" diff --git a/code/__DEFINES/~darkpack/discipline.dm b/code/__DEFINES/~darkpack/discipline.dm index 3b3a6a3fd6f1..77fa833703d6 100644 --- a/code/__DEFINES/~darkpack/discipline.dm +++ b/code/__DEFINES/~darkpack/discipline.dm @@ -58,3 +58,7 @@ #define CATEGORY_DISCIPLINES "DISCIPLINES" /// Category for the keybinds where you activate individual powers #define CATEGORY_DISCIPLINE_POWERS "DISCIPLINE POWERS" + +#define ABYSS_TENTACLE_MODE_AGGRESSIVE "Aggressive" +#define ABYSS_TENTACLE_MODE_CONTROL "Control" +#define ABYSS_TENTACLE_MODE_PASSIVE "Passive" diff --git a/code/__DEFINES/~darkpack/jobs.dm b/code/__DEFINES/~darkpack/jobs.dm index dc0a0e29b8cb..3bf0605aee87 100644 --- a/code/__DEFINES/~darkpack/jobs.dm +++ b/code/__DEFINES/~darkpack/jobs.dm @@ -71,6 +71,7 @@ //Giovanni #define JOB_CAPO "Capo" +#define JOB_I_NONNI "I Nonni" #define JOB_LA_SQUADRA "La Squadra" #define JOB_LA_FAMIGLIA "La Famiglia" diff --git a/code/__DEFINES/~darkpack/phones.dm b/code/__DEFINES/~darkpack/phones.dm index e0ca254dd09a..839b2c2a8987 100644 --- a/code/__DEFINES/~darkpack/phones.dm +++ b/code/__DEFINES/~darkpack/phones.dm @@ -8,14 +8,6 @@ #define PHONE_CALLING 2 #define PHONE_IN_CALL 3 -#define PHONE_NO_SIM (1<<1) -#define PHONE_OPEN (1<<2) - -DEFINE_BITFIELD(phone_flags, list( - "PHONE_NO_SIM" = PHONE_NO_SIM, - "PHONE_OPEN" = PHONE_OPEN, -)) - // Icons used for call history logging #define PHONE_CALL_ACCEPTED "fa-phone-volume" #define PHONE_CALL_DECLINED "fa-phone-slash" diff --git a/code/__DEFINES/~darkpack/storyteller_stats.dm b/code/__DEFINES/~darkpack/storyteller_stats.dm index 7e384fd4aaf8..9b066f82cc47 100644 --- a/code/__DEFINES/~darkpack/storyteller_stats.dm +++ b/code/__DEFINES/~darkpack/storyteller_stats.dm @@ -1,7 +1,5 @@ // Stat Flags -#define AFFECTS_HEALTH (1<<0) -#define AFFECTS_SPEED (1<<1) -#define AFFECTS_STATS (1<<2) // If the stat affects other stats, like courage or permanent willpower. +#define AFFECTS_STATS (1<<0) // If the stat affects other stats, like courage or permanent willpower. #define STAT_FREEBIE_POINTS /datum/st_stat/freebie diff --git a/code/__DEFINES/~darkpack/traits/declarations.dm b/code/__DEFINES/~darkpack/traits/declarations.dm index d91f50270980..7ceb749fd630 100644 --- a/code/__DEFINES/~darkpack/traits/declarations.dm +++ b/code/__DEFINES/~darkpack/traits/declarations.dm @@ -198,5 +198,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_GRAVE_SMELL "grave_smell" #define TRAIT_GLOWING_EYES "glowing_eyes" #define TRAIT_PROMETHEAN_CLAY "promethean_clay" +#define TRAIT_UNCONTROLLABLE "uncontrollable" + +// Below are traits given by items/clothing being equiped or worn +#define TRAIT_BRASSKNUCKLES "brassknuckles" // END TRAIT DEFINES diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 63f3ed1c066b..a5f4abbffe46 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -14,13 +14,24 @@ /proc/init_crafting_recipes() for(var/datum/crafting_recipe_path as anything in valid_subtypesof(/datum/crafting_recipe)) var/datum/crafting_recipe/recipe = new crafting_recipe_path() + if(recipe.name == "" || !recipe.result) + qdel(recipe) + continue + var/is_cooking = (recipe.category in GLOB.crafting_category_food) recipe.reqs = sort_list(recipe.reqs, GLOBAL_PROC_REF(cmp_crafting_req_priority)) - if(recipe.name != "" && recipe.result) - if(is_cooking) - GLOB.cooking_recipes += recipe - else - GLOB.crafting_recipes += recipe + + if(is_cooking) + GLOB.cooking_recipes += recipe + GLOB.cooking_recipes_by_typepath[crafting_recipe_path] = recipe + if(!(recipe.crafting_flags & CRAFT_MUST_BE_LEARNED)) + GLOB.cooking_recipes_default += recipe + + else + GLOB.crafting_recipes += recipe + GLOB.crafting_recipes_by_typepath[crafting_recipe_path] = recipe + if(!(recipe.crafting_flags & CRAFT_MUST_BE_LEARNED)) + GLOB.crafting_recipes_default += recipe var/list/global_stack_recipes = list( /obj/item/stack/sheet/glass = GLOB.glass_recipes, diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 3380da135819..9898db8bacf4 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -553,8 +553,8 @@ world ) flatX1 = addX1 - flatX2 = addY1 - flatY1 = addX2 + flatX2 = addX2 + flatY1 = addY1 flatY2 = addY2 // Blend the overlay into the flattened icon diff --git a/code/__HELPERS/memory_helpers.dm b/code/__HELPERS/memory_helpers.dm index 807610af8645..2c5a77e8d2e3 100644 --- a/code/__HELPERS/memory_helpers.dm +++ b/code/__HELPERS/memory_helpers.dm @@ -64,7 +64,7 @@ if(current) var/new_memory_flags = initial(memory_type.memory_flags) - if(!(new_memory_flags & MEMORY_SKIP_UNCONSCIOUS) && current.stat >= UNCONSCIOUS) + if(!(new_memory_flags & MEMORY_SKIP_UNCONSCIOUS) && IS_UNCONSCIOUS(current)) return if(new_memory_flags & MEMORY_CHECK_BLINDNESS && current.is_blind()) return diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 1b62251f6516..6000a322fb66 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -178,135 +178,6 @@ GLOBAL_LIST_INIT(skin_tone_names, list( else return "unknown" -//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action -/mob/proc/break_do_after_checks(list/checked_health, check_clicks) - if(check_clicks && next_move > world.time) - return FALSE - return TRUE - -//pass a list in the format list("health" = mob's health var) to check health during this -/mob/living/break_do_after_checks(list/checked_health, check_clicks) - if(islist(checked_health)) - if(health < checked_health["health"]) - return FALSE - checked_health["health"] = health - return ..() - - -/** - * Timed action involving one mob user. Target is optional. - * - * Checks that `user` does not move, change hands, get stunned, etc. for the - * given `delay`. Returns `TRUE` on success or `FALSE` on failure. - * - * @param {mob} user - The mob performing the action. - * - * @param {number} delay - The time in deciseconds. Use the SECONDS define for readability. `1 SECONDS` is 10 deciseconds. - * - * @param {atom} target - The target of the action. This is where the progressbar will display. - * - * @param {flag} timed_action_flags - Flags to control the behavior of the timed action. - * - * @param {boolean} progress - Whether to display a progress bar / cogbar. - * - * @param {datum/callback} extra_checks - Additional checks to perform before the action is executed. - * - * @param {string} interaction_key - The assoc key under which the do_after is capped, with max_interact_count being the cap. Interaction key will default to target if not set. - * - * @param {number} max_interact_count - The maximum amount of interactions allowed. - * - * @param {boolean} hidden - By default, any action 1 second or longer shows a cog over the user while it is in progress. If hidden is set to TRUE, the cog will not be shown. - * - * @param {icon} icon - The icon file of the cog. Default: 'icons/effects/progressbar.dmi' - * - * @param {iconstate} iconstate - The icon state of the cog. Default: "Cog" - * - * @param {mob} bar_override - Mob which should see the bar instead of the user - */ -/proc/do_after(mob/user, delay, atom/target, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, hidden = FALSE, icon = 'icons/effects/progressbar.dmi', iconstate = "cog", mob/bar_override = null) - if(!user) - return FALSE - if(!isnum(delay)) - CRASH("do_after was passed a non-number delay: [delay || "null"].") - - if(!interaction_key && target) - interaction_key = target //Use the direct ref to the target - if(interaction_key) //Do we have a interaction_key now? - var/current_interaction_count = LAZYACCESS(user.do_afters, interaction_key) || 0 - if(current_interaction_count >= max_interact_count) //We are at our peak - return - LAZYSET(user.do_afters, interaction_key, current_interaction_count + 1) - - var/atom/user_loc = user.loc - var/atom/target_loc = target?.loc - - var/drifting = FALSE - if(!isnull(user.drift_handler)) - drifting = TRUE - - var/holding = user.get_active_held_item() - -#ifdef UNIT_TESTS - timed_action_flags &= ~IGNORE_SLOWDOWNS //it shouldn't stop unit test dummies from being fast as hell -#endif - - if(!(timed_action_flags & IGNORE_SLOWDOWNS)) - delay *= user.cached_multiplicative_actions_slowdown - - var/datum/progressbar/progbar - var/datum/cogbar/cog - - if(progress) - if(user.client || bar_override?.client) - progbar = new(bar_override || user, delay, target || user) - - if(!hidden && delay >= 1 SECONDS) - cog = new(bar_override || user, icon, iconstate) - - SEND_SIGNAL(user, COMSIG_DO_AFTER_BEGAN) - - var/endtime = world.time + delay - var/starttime = world.time - . = TRUE - while (world.time < endtime) - stoplag(1) - - if(!QDELETED(progbar)) - progbar.update(world.time - starttime) - - if(drifting && isnull(user.drift_handler)) - drifting = FALSE - user_loc = user.loc - - if(QDELETED(user) \ - || (!(timed_action_flags & IGNORE_USER_LOC_CHANGE) && !drifting && user.loc != user_loc) \ - || (!(timed_action_flags & IGNORE_HELD_ITEM) && user.get_active_held_item() != holding) \ - || (!(timed_action_flags & IGNORE_INCAPACITATED) && HAS_TRAIT(user, TRAIT_INCAPACITATED)) \ - || ((timed_action_flags & DO_AFTER_CHECK_NEXT_MOVE) && world.time < user.next_move) \ - || (extra_checks && !extra_checks.Invoke())) - . = FALSE - break - - if(target && (user != target) && \ - (QDELETED(target) \ - || (!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE) && target.loc != target_loc))) - . = FALSE - break - - if(!QDELETED(progbar)) - progbar.end_progress() - - cog?.remove() - - if(interaction_key) - var/reduced_interaction_count = (LAZYACCESS(user.do_afters, interaction_key) || 0) - 1 - if(reduced_interaction_count > 0) // Not done yet! - LAZYSET(user.do_afters, interaction_key, reduced_interaction_count) - return - // all out, let's clear er out fully - LAZYREMOVE(user.do_afters, interaction_key) - SEND_SIGNAL(user, COMSIG_DO_AFTER_ENDED) - /// Returns the total amount of do_afters this mob is taking part in /mob/proc/do_after_count() var/count = 0 @@ -859,7 +730,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) */ /proc/add_personality_mood_to_viewers(atom/movable/source, mood_key, list/personality_to_mood, range, ...) for(var/mob/living/nearby in viewers(range, source)) - if(nearby.stat >= UNCONSCIOUS || nearby.is_blind()) + if(IS_UNCONSCIOUS(nearby) || nearby.is_blind()) continue for(var/personality in personality_to_mood) if(HAS_PERSONALITY(nearby, personality)) diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm index 0d45d6da0615..c1ab33d1d1ed 100644 --- a/code/__HELPERS/reagents.dm +++ b/code/__HELPERS/reagents.dm @@ -162,15 +162,21 @@ input_reagent = get_chem_id(input_reagent) return input_reagent -///Returns a random reagent object, with the option to blacklist reagents. -/proc/get_random_reagent_id(randomization_flags = REAGENT_SPAWN_RANDOM_PRODUCERS, list/blacklist, list/whitelist) +/// +/** + * Returns a random reagent object, with the option to blacklist reagents. + * + * * randomization_flags - Flags to filter the reagents by. Default is REAGENT_SPAWN_RANDOM_PRODUCERS. + * * blacklist - Optional, a list of reagent typepaths to exclude from the random selection. + * Subtypes of the listed reagents are not explicitly included. + * If not provided, defaults to an empty list. + * * whitelist - Optional, a list of reagent typepaths to include in the random selection. + * Subtypes of the listed reagents are not explicitly included. + * If not provided, defaults to all reagent subtypes. + */ +/proc/get_random_reagent_id(randomization_flags = REAGENT_SPAWN_RANDOM_PRODUCERS, list/blacklist = list(), list/whitelist = subtypesof(/datum/reagent)) var/list/reagent_list_to_process = list() - - whitelist ||= subtypesof(/datum/reagent) - - for(var/datum/reagent/reagent_path as anything in whitelist) - if(is_path_in_list(reagent_path, blacklist)) - continue + for(var/datum/reagent/reagent_path as anything in whitelist - blacklist) if(reagent_path::randomized_spawns & randomization_flags) reagent_list_to_process += reagent_path return pick(reagent_list_to_process) diff --git a/code/__HELPERS/stack_trace.dm b/code/__HELPERS/stack_trace.dm index 7a99ff6c2f79..6cb79064e0e8 100644 --- a/code/__HELPERS/stack_trace.dm +++ b/code/__HELPERS/stack_trace.dm @@ -3,16 +3,10 @@ /proc/_stack_trace(message, file, line) CRASH("[message][WORKAROUND_IDENTIFIER][json_encode(list(file, line))][WORKAROUND_IDENTIFIER]") - -#if (DM_BUILD > 1667) -#warn if this is ci please remove my Note: comment below, thanks, love you! -#endif #define STACK_DEPTH_SEARCH_LIMIT 2000 -/// Returns an ordered list of all our parent procs, highest to deepest -/// Note: This will frequently cause erorrs and have seemingly infinitely repeating procs on the current good byond version -/// It'll be fixed when we can update to latest for dev but you likely can't do that right now (since debugging hasn't been fixed yet) -/// BIGGER, MORE IMPORTANT NOTE: Should not be used on master maybe ever, introspection like this is mostly useful for debugging -/// if you have another use I suspect you are just creating god's strongest footgun and should rethink things +/// Returns an ordered list of all our parent procs, highest to deepest. +/// +/// introspection like this is mostly useful for debugging, if you have another use I suspect you are just creating god's strongest footgun and should rethink things /proc/dump_stack(max_depth = STACK_DEPTH_SEARCH_LIMIT) var/list/proc_paths = list() var/crashed = FALSE diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm index 6e23be76ed39..6e9cbe4a97bf 100644 --- a/code/__HELPERS/type_processing.dm +++ b/code/__HELPERS/type_processing.dm @@ -38,7 +38,8 @@ GLOBAL_LIST_INIT(fancy_type_replacements, list( /obj/item/stack/sheet = "SHEET", /obj/item/stack/sheet/mineral = "MINERAL_SHEET", /obj/item/stack/ore = "ORE", - /obj/item/ai_module = "AI_LAW_MODULE", + /obj/item/ai_module = "AI_MODULE", + /obj/item/ai_module/law = "AI_LAW_MODULE", /obj/item/circuitboard = "CIRCUITBOARD", /obj/item/circuitboard/machine = "MACHINE_BOARD", /obj/item/circuitboard/computer = "COMPUTER_BOARD", diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 186a8844bcf1..4e0e2b0d7526 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -2,11 +2,11 @@ //Update this whenever you need to take advantage of more recent byond features #define MIN_COMPILER_VERSION 516 -#define MIN_COMPILER_BUILD 1648 +#define MIN_COMPILER_BUILD 1659 #if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) && !defined(OPENDREAM) //Don't forget to update this part #error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. -#error You need version 516.1648 or higher +#error You need version 516.1659 or higher #endif // 516.1660 broke (x in vars), which breaks a lot of things. diff --git a/code/_compile_options.dm b/code/_compile_options.dm index cc6e50191d6f..6fb97ee6cf40 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -149,14 +149,7 @@ #endif // 1 to use the default behaviour; // 2 for preloading absolutely everything; -#ifdef LOWMEMORYMODE -#ifndef ABSOLUTE_MINIMUM -#define FORCE_MAP "runtimetown" // DARKPACK EDIT CHANGE - ORIGINAL: #define FORCE_MAP "runtimestation" -#else -#define FORCE_MAP "runtimestation_minimal" -#endif #define FORCE_MAP_DIRECTORY "_maps" -#endif //Additional code for the above flags. #ifdef TESTING diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index a44731d8213f..57842773159e 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -121,7 +121,6 @@ DEFINE_BITFIELD(clothing_flags, list( "ANTI_TINFOIL_MANEUVER" = ANTI_TINFOIL_MANEUVER, "BLOCK_GAS_SMOKE_EFFECT" = BLOCK_GAS_SMOKE_EFFECT, "CARP_STYLE_FACTOR" = CARP_STYLE_FACTOR, - "CASTING_CLOTHES" = CASTING_CLOTHES, "DANGEROUS_OBJECT" = DANGEROUS_OBJECT, "GAS_FILTERING" = GAS_FILTERING, "HEADINTERNALS" = HEADINTERNALS, @@ -559,15 +558,16 @@ DEFINE_BITFIELD(bodypart_flags, list( )) DEFINE_BITFIELD(biological_state, list( - "BIO_INORGANIC" = BIO_INORGANIC, + "BIO_BLOODED" = BIO_BLOODED, "BIO_BONE" = BIO_BONE, + "BIO_CHITIN" = BIO_CHITIN, "BIO_FLESH" = BIO_FLESH, + "BIO_INORGANIC" = BIO_INORGANIC, + "BIO_JELLY" = BIO_JELLY, + "BIO_JOINTED" = BIO_JOINTED, "BIO_METAL" = BIO_METAL, - "BIO_WOOD" = BIO_WOOD, "BIO_WIRED" = BIO_WIRED, - "BIO_BLOODED" = BIO_BLOODED, - "BIO_JOINTED" = BIO_JOINTED, - "BIO_CHITIN" = BIO_CHITIN, + "BIO_WOOD" = BIO_WOOD, )) DEFINE_BITFIELD(change_exempt_flags, list( diff --git a/code/_globalvars/lists/basic_ai.dm b/code/_globalvars/lists/basic_ai.dm deleted file mode 100644 index 561d874c4e91..000000000000 --- a/code/_globalvars/lists/basic_ai.dm +++ /dev/null @@ -1,19 +0,0 @@ -///all basic ai subtrees -GLOBAL_LIST_EMPTY(ai_subtrees) - -///basic ai controllers based on status -GLOBAL_LIST_INIT(ai_controllers_by_status, list( - AI_STATUS_ON = list(), - AI_STATUS_OFF = list(), - AI_STATUS_IDLE = list(), -)) - -///basic ai controllers based on their z level -GLOBAL_LIST_EMPTY(ai_controllers_by_zlevel) - -///basic ai controllers that are currently performing idled behaviors -GLOBAL_LIST_INIT_TYPED(unplanned_controllers, /list/datum/ai_controller, list( - AI_STATUS_ON = list(), - AI_STATUS_IDLE = list(), -)) - diff --git a/code/_globalvars/lists/bitrunning.dm b/code/_globalvars/lists/bitrunning.dm new file mode 100644 index 000000000000..fe514c83f928 --- /dev/null +++ b/code/_globalvars/lists/bitrunning.dm @@ -0,0 +1,9 @@ +/// Assoc list of bitrunning grades in ascending order and their associated fontawesome icons. +GLOBAL_LIST_INIT(bitrunning_grades, list( + BITRUNNING_GRADE_NONE = FA_ICON_MINUS, + BITRUNNING_GRADE_D = FA_ICON_D, + BITRUNNING_GRADE_C = FA_ICON_C, + BITRUNNING_GRADE_B = FA_ICON_B, + BITRUNNING_GRADE_A = FA_ICON_A, + BITRUNNING_GRADE_S = FA_ICON_S +)) diff --git a/code/_globalvars/lists/crafting.dm b/code/_globalvars/lists/crafting.dm index d0e0bdb86a3a..60b0ddd447d6 100644 --- a/code/_globalvars/lists/crafting.dm +++ b/code/_globalvars/lists/crafting.dm @@ -22,5 +22,6 @@ GLOBAL_LIST_INIT(crafting_category, list( CAT_ENTERTAINMENT, CAT_TOOLS, CAT_CULT, + CAT_CONSTRUCTION, CAT_TZIMISCE, // DARKPACK EDIT ADD )) diff --git a/code/_globalvars/lists/girder_wall_recipes.dm b/code/_globalvars/lists/girder_wall_recipes.dm index d4a015c6a4e4..75c9120d07fa 100644 --- a/code/_globalvars/lists/girder_wall_recipes.dm +++ b/code/_globalvars/lists/girder_wall_recipes.dm @@ -24,6 +24,10 @@ GLOBAL_LIST_INIT(material_girder_wall_recipes, init_material_girder_wall_recipes /turf/closed/wall/mineral/abductor, /turf/closed/wall/mineral/titanium, /turf/closed/wall/mineral/plastitanium, + /turf/closed/wall/mineral/plastitanium/wall_fill, + /turf/closed/wall/mineral/plastitanium/darkpod, + /turf/closed/wall/mineral/plastitanium/redpod, + /turf/closed/wall/mineral/plastitanium/survival, /turf/closed/wall/mineral/cult, /turf/closed/wall/mineral/bronze, /turf/closed/wall/material, @@ -44,6 +48,11 @@ GLOBAL_LIST_INIT(material_girder_wall_recipes, init_material_girder_wall_recipes /obj/structure/falsewall/abductor, /obj/structure/falsewall/titanium, /obj/structure/falsewall/plastitanium, + /obj/structure/falsewall/plastitanium, + /obj/structure/falsewall/plastitanium/wall_fill, + /obj/structure/falsewall/plastitanium/darkpod, + /obj/structure/falsewall/plastitanium/redpod, + /obj/structure/falsewall/plastitanium/survival, /obj/structure/falsewall/material, ), tram_types = list( diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index 856315a8273c..64e8d38f9b64 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -246,6 +246,7 @@ GLOBAL_LIST_INIT(uncommon_loot, list(//uncommon: useful items /obj/item/reagent_containers/cup/jerrycan/space_cleaner = 2, //If you can't find the janitor, the medbay might also appreciate some readymade cleaner. /obj/item/reagent_containers/cup/jerrycan/sus = 1, //what assitant wouldn't like a vibrant jug o' phlog? /obj/item/reagent_containers/cup/jerrycan/diethylamine = 1, //Im sure the botanist would appreciate it, but it is also really cool for maintenance science projects. + /obj/item/reagent_containers/cup/jerrycan/concrete_mix = 2, ) = 2, ) = 8, diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 4eae1a55301f..f84b17cb26ae 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -35,11 +35,19 @@ GLOBAL_LIST_EMPTY(item_to_design_list) /// Global list of all non-cooking related crafting recipes. GLOBAL_LIST_EMPTY(crafting_recipes) +/// Global list of all non-cooking related crafting recipes that don't need to be learned +GLOBAL_LIST_EMPTY(crafting_recipes_default) +/// Global list of all non-cooking related crafting recipes, indexed by their typepath. +GLOBAL_LIST_EMPTY(crafting_recipes_by_typepath) /// This is a global list of typepaths, these typepaths are atoms or reagents that are associated with crafting recipes. /// This includes stuff like recipe components and results. GLOBAL_LIST_EMPTY(crafting_recipes_atoms) /// Global list of all cooking related crafting recipes. GLOBAL_LIST_EMPTY(cooking_recipes) +/// Global list of all cooking related crafting recipes that don't need to be learned +GLOBAL_LIST_EMPTY(cooking_recipes_default) +/// Global list of all cooking related crafting recipes, indexed by their typepath. +GLOBAL_LIST_EMPTY(cooking_recipes_by_typepath) /// This is a global list of typepaths, these typepaths are atoms or reagents that are associated with cooking recipes. /// This includes stuff like recipe components and results. GLOBAL_LIST_EMPTY(cooking_recipes_atoms) diff --git a/code/_globalvars/lists/quirks.dm b/code/_globalvars/lists/quirks.dm index 341dc1c18dcc..b3d6ebb45bc1 100644 --- a/code/_globalvars/lists/quirks.dm +++ b/code/_globalvars/lists/quirks.dm @@ -124,3 +124,9 @@ GLOBAL_LIST_INIT(quirk_chipped_choice, list( "Mast-Angl-Er" = /obj/item/skillchip/master_angler, "Kommand" = /obj/item/skillchip/big_pointer, )) + +///Spacer quirk +GLOBAL_LIST_INIT(spacer_height_choices, list( + "Extra Tall" = HUMAN_HEIGHT_TALLEST, + "Tall" = HUMAN_HEIGHT_TALLER, +)) diff --git a/code/_globalvars/lists/xenobiology.dm b/code/_globalvars/lists/xenobiology.dm index 69f8e079ad4a..4a07c40dacdd 100644 --- a/code/_globalvars/lists/xenobiology.dm +++ b/code/_globalvars/lists/xenobiology.dm @@ -11,6 +11,8 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( /datum/micro_organism/cell_line/sholean_grapes = 2, /datum/micro_organism/cell_line/slime = 2, /datum/micro_organism/cell_line/goat = 1, + /datum/micro_organism/cell_line/sheep = 1, + /datum/micro_organism/cell_line/lizard = 1, ), CELL_LINE_TABLE_MOIST = list( @@ -22,6 +24,9 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( /datum/micro_organism/cell_line/snake = 1, /datum/micro_organism/cell_line/glockroach = 1, /datum/micro_organism/cell_line/hauberoach = 1, + /datum/micro_organism/cell_line/stoat = 1, + /datum/micro_organism/cell_line/mothroach = 1, + /datum/micro_organism/cell_line/penguin = 1, ), CELL_LINE_TABLE_BLOB = list( @@ -39,6 +44,8 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( /datum/micro_organism/cell_line/slime = 1, /datum/micro_organism/cell_line/vat_beast = 2, /datum/micro_organism/cell_line/pig = 1, + /datum/micro_organism/cell_line/fox = 1, + /datum/micro_organism/cell_line/rabbit = 1, ), CELL_LINE_TABLE_BEAR = list(/datum/micro_organism/cell_line/bear = 1), @@ -56,11 +63,17 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( CELL_LINE_TABLE_GRAPE = list(/datum/micro_organism/cell_line/sholean_grapes = 1), CELL_LINE_TABLE_HAUBEROACH = list(/datum/micro_organism/cell_line/hauberoach = 1), CELL_LINE_TABLE_MEGACARP = list(/datum/micro_organism/cell_line/megacarp = 1), + CELL_LINE_TABLE_PENGUIN = list(/datum/micro_organism/cell_line/penguin = 1), CELL_LINE_TABLE_MOUSE = list(/datum/micro_organism/cell_line/mouse = 1), CELL_LINE_TABLE_PINE = list(/datum/micro_organism/cell_line/pine = 1), CELL_LINE_TABLE_PUG = list(/datum/micro_organism/cell_line/pug = 1), + CELL_LINE_TABLE_FOX = list(/datum/micro_organism/cell_line/fox = 1), + CELL_LINE_TABLE_STOAT = list(/datum/micro_organism/cell_line/stoat = 1), + CELL_LINE_TABLE_RABBIT = list(/datum/micro_organism/cell_line/rabbit = 1), + CELL_LINE_TABLE_MOTHROACH = list(/datum/micro_organism/cell_line/mothroach = 1), CELL_LINE_TABLE_SLIME = list(/datum/micro_organism/cell_line/slime = 1), CELL_LINE_TABLE_SNAKE = list(/datum/micro_organism/cell_line/snake = 1), + CELL_LINE_TABLE_LIZARD = list(/datum/micro_organism/cell_line/lizard = 1), CELL_LINE_TABLE_SNAIL = list(/datum/micro_organism/cell_line/snail = 1), CELL_LINE_TABLE_VATBEAST = list(/datum/micro_organism/cell_line/vat_beast = 1), CELL_LINE_TABLE_NETHER = list(/datum/micro_organism/cell_line/netherworld = 1), @@ -80,6 +93,7 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( CELL_LINE_TABLE_BUTTERFLY = list(/datum/micro_organism/cell_line/butterfly = 1), CELL_LINE_TABLE_MEGA_ARACHNID = list(/datum/micro_organism/cell_line/mega_arachnid = 1), CELL_LINE_TABLE_GOAT = list(/datum/micro_organism/cell_line/goat = 1), + CELL_LINE_TABLE_SHEEP = list(/datum/micro_organism/cell_line/sheep = 1), CELL_LINE_TABLE_PIG = list(/datum/micro_organism/cell_line/pig = 1), CELL_LINE_TABLE_ALGAE = list( /datum/micro_organism/cell_line/frog = 2, diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index fb7e4b35981c..5f38afdd86e2 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -78,6 +78,10 @@ GLOBAL_PROTECT(investigate_signaler) GLOBAL_LIST_EMPTY(lawchanges) GLOBAL_PROTECT(lawchanges) +/// Adds something to the law change log +/proc/log_law_change(mob/living/changer, log_message) + GLOB.lawchanges += "[round_timestamp()] : [changer ? key_name(changer) : "(No mob)"] [log_message]" + #undef DECLARE_LOG #undef DECLARE_LOG_NAMED #undef START_LOG diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index 2db14ace9f2c..02ba3efaf2c0 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -350,7 +350,7 @@ GLOBAL_LIST_INIT(phobia_objs, list( /obj/machinery/door/airlock/centcom, /obj/item/clothing/mask/madness_mask, /obj/item/wallframe/painting/eldritch, - /obj/item/clothing/neck/heretic_focus/moon_amulet, + /obj/item/clothing/neck/moon_amulet, /obj/item/melee/sickly_blade/moon, )), "doctors" = typecacheof(list( @@ -464,7 +464,7 @@ GLOBAL_LIST_INIT(phobia_objs, list( /obj/item/mmi/posibrain, /obj/item/toy/figure/borg, /obj/item/toy/talking/ai, - /obj/machinery/computer/upload, + /obj/machinery/ai_law_rack, /obj/machinery/recharge_station, /obj/structure/statue/diamond/ai1, /obj/structure/statue/diamond/ai2, diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index bee13c2114d2..0e8a9b7d466c 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -132,6 +132,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "STATION_TRAIT_MEDBOT_MANIA" = STATION_TRAIT_MEDBOT_MANIA, "STATION_TRAIT_PDA_GLITCHED" = STATION_TRAIT_PDA_GLITCHED, "STATION_TRAIT_PREMIUM_INTERNALS" = STATION_TRAIT_PREMIUM_INTERNALS, + "STATION_TRAIT_PUN_PUN_GYM_DAY" = STATION_TRAIT_PUN_PUN_GYM_DAY, "STATION_TRAIT_RADIOACTIVE_NEBULA" = STATION_TRAIT_RADIOACTIVE_NEBULA, "STATION_TRAIT_RANDOM_ARRIVALS" = STATION_TRAIT_RANDOM_ARRIVALS, "STATION_TRAIT_REVOLUTIONARY_TRASHING" = STATION_TRAIT_REVOLUTIONARY_TRASHING, @@ -171,6 +172,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /mob = list( "TRAIT_ABDUCTOR_HUD" = TRAIT_ABDUCTOR_HUD, + "TRAIT_ABDUCTOR_KNOWLEDGE" = TRAIT_ABDUCTOR_KNOWLEDGE, + "TRAIT_ABDUCTOR_QUICK_SCAN" = TRAIT_ABDUCTOR_QUICK_SCAN, "TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING, "TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING, "TRAIT_ACT_AS_CULTIST" = TRAIT_ACT_AS_CULTIST, @@ -185,7 +188,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_AI_ACCESS" = TRAIT_AI_ACCESS, "TRAIT_AI_BAGATTACK" = TRAIT_AI_BAGATTACK, "TRAIT_ALCOHOL_TOLERANCE" = TRAIT_ALCOHOL_TOLERANCE, - "TRAIT_ALLOW_HERETIC_CASTING" = TRAIT_ALLOW_HERETIC_CASTING, "TRAIT_ALWAYS_WANTED" = TRAIT_ALWAYS_WANTED, "TRAIT_ANALGESIA" = TRAIT_ANALGESIA, "TRAIT_ANGELIC" = TRAIT_ANGELIC, @@ -208,6 +210,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BIRTHDAY_BOY" = TRAIT_BIRTHDAY_BOY, "TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY, "TRAIT_BLOCKING_PROJECTILES" = TRAIT_BLOCKING_PROJECTILES, + "TRAIT_BLOCK_MEDHUD" = TRAIT_BLOCK_MEDHUD, "TRAIT_BLOCK_SECHUD" = TRAIT_BLOCK_SECHUD, "TRAIT_BLOCK_SHUTTLE_MOVEMENT" = TRAIT_BLOCK_SHUTTLE_MOVEMENT, "TRAIT_BLOODSHOT_EYES" = TRAIT_BLOODSHOT_EYES, @@ -239,7 +242,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP, "TRAIT_CAN_THROW_ITEMS" = TRAIT_CAN_THROW_ITEMS, "TRAIT_CAN_USE_NUKE" = TRAIT_CAN_USE_NUKE, - "TRAIT_CATLIKE_GRACE" = TRAIT_CATLIKE_GRACE, + "TRAIT_CATLIKE_INSTINCT" = TRAIT_CATLIKE_INSTINCT, "TRAIT_CHANGELING_HIVEMIND" = TRAIT_CHANGELING_HIVEMIND, "TRAIT_CHASM_DESTROYED" = TRAIT_CHASM_DESTROYED, "TRAIT_CHEF_KISS" = TRAIT_CHEF_KISS, @@ -248,6 +251,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_CHUUNIBYOU" = TRAIT_CHUUNIBYOU, "TRAIT_CLEANBOT_WHISPERER" = TRAIT_CLEANBOT_WHISPERER, "TRAIT_CLIFF_WALKER" = TRAIT_CLIFF_WALKER, + "TRAIT_CLOTH_EATER" = TRAIT_CLOTH_EATER, "TRAIT_CLOWN_ENJOYER" = TRAIT_CLOWN_ENJOYER, "TRAIT_CLUMSY" = TRAIT_CLUMSY, "TRAIT_COAGULATING" = TRAIT_COAGULATING, @@ -255,7 +259,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_COMBAT_MODE_LOCK" = TRAIT_COMBAT_MODE_LOCK, "TRAIT_CONNECTED_TO_CIRCUIT" = TRAIT_CONNECTED_TO_CIRCUIT, "TRAIT_CORPSELOCKED" = TRAIT_CORPSELOCKED, - "TRAIT_CRITICAL_CONDITION" = TRAIT_CRITICAL_CONDITION, "TRAIT_CULT_HALO" = TRAIT_CULT_HALO, "TRAIT_CURSED" = TRAIT_CURSED, "TRAIT_DAZED" = TRAIT_DAZED, @@ -291,6 +294,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_ELITE_CHALLENGER" = TRAIT_ELITE_CHALLENGER, "TRAIT_EMOTEMUTE" = TRAIT_EMOTEMUTE, "TRAIT_ENTRAILS_READER" = TRAIT_ENTRAILS_READER, + "TRAIT_ETHEREAL_METABOLISM" = TRAIT_ETHEREAL_METABOLISM, "TRAIT_EVIL" = TRAIT_EVIL, "TRAIT_EXAMINE_DEEPER_FISH" = TRAIT_EXAMINE_DEEPER_FISH, "TRAIT_EXAMINE_FISH" = TRAIT_EXAMINE_FISH, @@ -330,6 +334,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_GAMERGOD" = TRAIT_GAMERGOD, "TRAIT_GARLIC_BREATH" = TRAIT_GARLIC_BREATH, "TRAIT_GENELESS" = TRAIT_GENELESS, + "TRAIT_GHOSTLY_MOB" = TRAIT_GHOSTLY_MOB, "TRAIT_GHOSTROLE_ON_REVIVE" = TRAIT_GHOSTROLE_ON_REVIVE, "TRAIT_GIANT" = TRAIT_GIANT, "TRAIT_GODMODE" = TRAIT_GODMODE, @@ -355,6 +360,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_HEAVY_SLEEPER" = TRAIT_HEAVY_SLEEPER, "TRAIT_HERETIC_SUMMON" = TRAIT_HERETIC_SUMMON, "TRAIT_HIDE_EXTERNAL_ORGANS" = TRAIT_HIDE_EXTERNAL_ORGANS, + "TRAIT_HIDE_THINKING_INDICATOR" = TRAIT_HIDE_THINKING_INDICATOR, "TRAIT_HIGH_VALUE_RANSOM" = TRAIT_HIGH_VALUE_RANSOM, "TRAIT_HOLY" = TRAIT_HOLY, "TRAIT_HOPELESSLY_ADDICTED" = TRAIT_HOPELESSLY_ADDICTED, @@ -391,6 +397,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_LIMBATTACHMENT" = TRAIT_LIMBATTACHMENT, "TRAIT_LITERATE" = TRAIT_LITERATE, "TRAIT_LIVERLESS_METABOLISM" = TRAIT_LIVERLESS_METABOLISM, + "TRAIT_LIZARD_METABOLISM" = TRAIT_LIZARD_METABOLISM, "TRAIT_LOUD_BINARY" = TRAIT_LOUD_BINARY, "TRAIT_LUMINESCENT_EYES" = TRAIT_LUMINESCENT_EYES, "TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE, @@ -408,7 +415,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MINDSHIELD" = TRAIT_MINDSHIELD, "TRAIT_MIND_TEMPORARILY_GONE" = TRAIT_MIND_TEMPORARILY_GONE, "TRAIT_MINING_AGGRO" = TRAIT_MINING_AGGRO, - "TRAIT_MINOR_NIGHT_VISION" = TRAIT_MINOR_NIGHT_VISION, "TRAIT_FAKE_SOULLESS" = TRAIT_FAKE_SOULLESS, "TRAIT_MOB_BREEDER" = TRAIT_MOB_BREEDER, "TRAIT_MOB_CAN_DIG" = TRAIT_MOB_CAN_DIG, @@ -431,6 +437,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NEVER_WOUNDED" = TRAIT_NEVER_WOUNDED, "TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT, "TRAIT_NIGHT_VISION" = TRAIT_NIGHT_VISION, + "TRAIT_NIGHTMARISH" = TRAIT_NIGHTMARISH, "TRAIT_NOBLOOD" = TRAIT_NOBLOOD, "TRAIT_NOBREATH" = TRAIT_NOBREATH, "TRAIT_NOCRITDAMAGE" = TRAIT_NOCRITDAMAGE, @@ -528,6 +535,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_REGEN_SHIELD" = TRAIT_REGEN_SHIELD, "TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER, "TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING, + "TRAIT_REQUIRED_ADV_HEALTH_SCANNER" = TRAIT_REQUIRED_ADV_HEALTH_SCANNER, "TRAIT_RESEARCH_SCANNER" = TRAIT_RESEARCH_SCANNER, "TRAIT_RESISTCOLD" = TRAIT_RESISTCOLD, "TRAIT_RESISTHEAT" = TRAIT_RESISTHEAT, @@ -569,7 +577,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SMOKER" = TRAIT_SMOKER, "TRAIT_SNEAK" = TRAIT_SNEAK, "TRAIT_SNOB" = TRAIT_SNOB, - "TRAIT_SOFTSPOKEN" = TRAIT_SOFTSPOKEN, + "TRAIT_FORCE_WHISPER" = TRAIT_FORCE_WHISPER, "TRAIT_SOOTHED_HEADACHE" = TRAIT_SOOTHED_HEADACHE, "TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT, "TRAIT_SOUND_DEBUGGED" = TRAIT_SOUND_DEBUGGED, @@ -581,6 +589,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SPIDER_CONSUMED" = TRAIT_SPIDER_CONSUMED, "TRAIT_SPIRITUAL" = TRAIT_SPIRITUAL, "TRAIT_SPLATTERCASTER" = TRAIT_SPLATTERCASTER, + "TRAIT_SPOOKY_INSTRUMENT_PLAYER" = TRAIT_SPOOKY_INSTRUMENT_PLAYER, "TRAIT_SPRAY_PAINTABLE" = TRAIT_SPRAY_PAINTABLE, "TRAIT_STABLEHEART" = TRAIT_STABLEHEART, "TRAIT_STABLELIVER" = TRAIT_STABLELIVER, @@ -627,6 +636,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_UNBREAKABLE" = TRAIT_UNBREAKABLE, "TRAIT_UNCONVERTABLE" = TRAIT_UNCONVERTABLE, "TRAIT_UNDENSE" = TRAIT_UNDENSE, + "TRAIT_UNHOLY_BANEABLE" = TRAIT_UNHOLY_BANEABLE, "TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE, "TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH, "TRAIT_UNKNOWN_APPEARANCE" = TRAIT_UNKNOWN_APPEARANCE, @@ -644,6 +654,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE, "TRAIT_VIRUS_RESISTANCE" = TRAIT_VIRUS_RESISTANCE, "TRAIT_VOICE_MATCHES_ID" = TRAIT_VOICE_MATCHES_ID, + "TRAIT_VOMIT_SLURPER" = TRAIT_VOMIT_SLURPER, "TRAIT_VORACIOUS" = TRAIT_VORACIOUS, "TRAIT_WAS_EVOLVED" = TRAIT_WAS_EVOLVED, "TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION, @@ -778,6 +789,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_TRANSFORM_UPDATES_ICON" = TRAIT_TRANSFORM_UPDATES_ICON, // DARKPACK EDIT ADD - WEREWOLF "TRAIT_UNAGING" = TRAIT_UNAGING, // DARKPACK EDIT ADD "TRAIT_UNBONDABLE" = TRAIT_UNBONDABLE, // DARKPACK EDIT ADD + "TRAIT_UNCONTROLLABLE" = TRAIT_UNCONTROLLABLE, //DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_UNMASQUERADE" = TRAIT_UNMASQUERADE, // DARKPACK EDIT ADD "TRAIT_VAMPIRE_TERRITORIAL" = TRAIT_VAMPIRE_TERRITORIAL, // DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_VICTIM_OF_THE_MASQUERADE" = TRAIT_VICTIM_OF_THE_MASQUERADE, // DARKPACK EDIT ADD - MERITS_FLAWS @@ -791,6 +803,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_WEAK_WILLED" = TRAIT_WEAK_WILLED, // DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_WYRMTAINTED" = TRAIT_WYRMTAINTED, // DARKPACK EDIT ADD - WEREWOLF "TRAIT_WYRMTAINTED_SPRITE" = TRAIT_WYRMTAINTED_SPRITE, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_BRASSKNUCKLES" = TRAIT_BRASSKNUCKLES, // DARKPACK EDIT ADD - CLOTHES ), /mob/living/carbon = list( "TRAIT_BRAINLESS_CARBON" = TRAIT_BRAINLESS_CARBON, @@ -807,6 +820,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT, "TRAIT_BLIND_TOOL" = TRAIT_BLIND_TOOL, "TRAIT_BYPASS_RANGED_ARMOR" = TRAIT_BYPASS_RANGED_ARMOR, + "TRAIT_CASTING_CLOTHING" = TRAIT_CASTING_CLOTHING, "TRAIT_CONTRABAND_BLOCKER" = TRAIT_CONTRABAND_BLOCKER, "TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND, "TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT, @@ -966,9 +980,11 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_LAVA_STOPPED" = TRAIT_LAVA_STOPPED, "TRAIT_NO_TERRAFORM" = TRAIT_NO_TERRAFORM, "TRAIT_SHUTTLE_CONSTRUCTION_TURF" = TRAIT_SHUTTLE_CONSTRUCTION_TURF, + "TRAIT_TURF_BLESSED" = TRAIT_TURF_BLESSED, "TRAIT_SPINNING_WEB_TURF" = TRAIT_SPINNING_WEB_TURF, "TRAIT_TURF_IGNORE_SLIPPERY" = TRAIT_TURF_IGNORE_SLIPPERY, "TRAIT_TURF_IGNORE_SLOWDOWN" = TRAIT_TURF_IGNORE_SLOWDOWN, + "TRAIT_TURF_PROJECTS_WHISPERS" = TRAIT_TURF_PROJECTS_WHISPERS, "TRAIT_TURF_SUN_BLOCKED" = TRAIT_TURF_SUN_BLOCKED, "TRAIT_UNCOVERED_TURF" = TRAIT_UNCOVERED_TURF, ), diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 8803705df590..2d57124b8531 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -37,6 +37,8 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( ), /mob = list( "TRAIT_ABDUCTOR_HUD" = TRAIT_ABDUCTOR_HUD, + "TRAIT_ABDUCTOR_KNOWLEDGE" = TRAIT_ABDUCTOR_KNOWLEDGE, + "TRAIT_ABDUCTOR_QUICK_SCAN" = TRAIT_ABDUCTOR_QUICK_SCAN, "TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING, "TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING, "TRAIT_ACT_AS_CULTIST" = TRAIT_ACT_AS_CULTIST, @@ -46,7 +48,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_AGENDER" = TRAIT_AGENDER, "TRAIT_AGEUSIA" = TRAIT_AGEUSIA, "TRAIT_ALCOHOL_TOLERANCE" = TRAIT_ALCOHOL_TOLERANCE, - "TRAIT_ALLOW_HERETIC_CASTING" = TRAIT_ALLOW_HERETIC_CASTING, "TRAIT_ALWAYS_WANTED" = TRAIT_ALWAYS_WANTED, "TRAIT_ANOSMIA" = TRAIT_ANOSMIA, "TRAIT_ANTENNAE" = TRAIT_ANTENNAE, @@ -75,18 +76,18 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP, "TRAIT_CAN_THROW_ITEMS" = TRAIT_CAN_THROW_ITEMS, "TRAIT_CAN_USE_NUKE" = TRAIT_CAN_USE_NUKE, - "TRAIT_CATLIKE_GRACE" = TRAIT_CATLIKE_GRACE, + "TRAIT_CATLIKE_INSTINCT" = TRAIT_CATLIKE_INSTINCT, "TRAIT_CHASM_DESTROYED" = TRAIT_CHASM_DESTROYED, "TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS, "TRAIT_CHUNKYFINGERS_IGNORE_BATON" = TRAIT_CHUNKYFINGERS_IGNORE_BATON, "TRAIT_CLEANBOT_WHISPERER" = TRAIT_CLEANBOT_WHISPERER, "TRAIT_CLIFF_WALKER" = TRAIT_CLIFF_WALKER, + "TRAIT_CLOTH_EATER" = TRAIT_CLOTH_EATER, "TRAIT_CLOWN_ENJOYER" = TRAIT_CLOWN_ENJOYER, "TRAIT_CLUMSY" = TRAIT_CLUMSY, "TRAIT_COLORBLIND" = TRAIT_COLORBLIND, "TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED, "TRAIT_CORPSELOCKED" = TRAIT_CORPSELOCKED, - "TRAIT_CRITICAL_CONDITION" = TRAIT_CRITICAL_CONDITION, "TRAIT_CULT_HALO" = TRAIT_CULT_HALO, "TRAIT_DAZED" = TRAIT_DAZED, "TRAIT_DEAF" = TRAIT_DEAF, @@ -295,11 +296,12 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_SLIPPERY_WHEN_WET" = TRAIT_SLIPPERY_WHEN_WET, "TRAIT_SMOKER" = TRAIT_SMOKER, "TRAIT_SNOB" = TRAIT_SNOB, - "TRAIT_SOFTSPOKEN" = TRAIT_SOFTSPOKEN, + "TRAIT_FORCE_WHISPER" = TRAIT_FORCE_WHISPER, "TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT, "TRAIT_SOOTHED_HEADACHE" = TRAIT_SOOTHED_HEADACHE, "TRAIT_SPACEWALK" = TRAIT_SPACEWALK, "TRAIT_SPIRITUAL" = TRAIT_SPIRITUAL, + "TRAIT_SPOOKY_INSTRUMENT_PLAYER" = TRAIT_SPOOKY_INSTRUMENT_PLAYER, "TRAIT_STABLEHEART" = TRAIT_STABLEHEART, "TRAIT_STABLELIVER" = TRAIT_STABLELIVER, "TRAIT_STRENGTH" = TRAIT_STRENGTH, @@ -330,6 +332,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_UI_BLOCKED" = TRAIT_UI_BLOCKED, "TRAIT_UNCONVERTABLE" = TRAIT_UNCONVERTABLE, "TRAIT_UNDENSE" = TRAIT_UNDENSE, + "TRAIT_UNHOLY_BANEABLE" = TRAIT_UNHOLY_BANEABLE, "TRAIT_UNHUSKABLE" = TRAIT_UNHUSKABLE, "TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH, "TRAIT_UNKNOWN_APPEARANCE" = TRAIT_UNKNOWN_APPEARANCE, @@ -411,6 +414,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_LOCAL_SIXTHSENSE" = TRAIT_LOCAL_SIXTHSENSE, // DARKPACK EDIT ADD "TRAIT_LOUD_WARCRY" = TRAIT_LOUD_WARCRY, // DARKPACK EDIT ADD - WEREWOLF "TRAIT_MASQUERADE_VIOLATING_EYES" = TRAIT_MASQUERADE_VIOLATING_EYES, // DARKPACK EDIT ADD + "TRAIT_MASQUERADE_VIOLATING_FACE" = TRAIT_MASQUERADE_VIOLATING_FACE, // DARKPACK EDIT ADD "TRAIT_MERIT_UNTAMABLE" = TRAIT_MERIT_UNTAMABLE, // DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_METAMORPH" = TRAIT_METAMORPH, // DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_NEEDS_BLOOD" = TRAIT_NEEDS_BLOOD, // DARKPACK EDIT ADD - Hunger and Frenzy @@ -447,6 +451,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_TRANSFORM_UPDATES_ICON" = TRAIT_TRANSFORM_UPDATES_ICON, // DARKPACK EDIT ADD - WEREWOLF "TRAIT_UNAGING" = TRAIT_UNAGING, // DARKPACK EDIT ADD "TRAIT_UNBONDABLE" = TRAIT_UNBONDABLE, // DARKPACK EDIT ADD + "TRAIT_UNCONTROLLABLE" = TRAIT_UNCONTROLLABLE, //DARKPACK EDIT ADD - MERITS_FLAWS "TRAIT_UNMASQUERADE" = TRAIT_UNMASQUERADE, // DARKPACK EDIT ADD "TRAIT_VIOLATOR" = TRAIT_VIOLATOR, // DARKPACK EDIT ADD "TRAIT_VITAE_ADDICTION" = TRAIT_VITAE_ADDICTION, // DARKPACK EDIT ADD diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index cb3eb9c3ca5c..d7e595e1a36b 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -27,6 +27,7 @@ mod *= effect.nextmove_modifier() adj += effect.nextmove_adjust() next_move = world.time + ((num + adj)*mod) + SEND_SIGNAL(src, COMSIG_LIVING_CHANGENEXT_MOVE, next_move, num) /** * Before anything else, defer these calls to a per-mobtype handler. This allows us to @@ -419,7 +420,7 @@ /// Simple helper to face what you clicked on, in case it should be needed in more than one place /mob/proc/face_atom(atom/atom_to_face) - if( buckled || stat != CONSCIOUS || !atom_to_face || !x || !y || !atom_to_face.x || !atom_to_face.y ) + if( buckled || IS_UNCONSCIOUS_OR_CRIT(src) || !atom_to_face || !x || !y || !atom_to_face.x || !atom_to_face.y ) return var/dx = atom_to_face.x - x var/dy = atom_to_face.y - y diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 92f7acb42598..e704a611dcb2 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -14,7 +14,7 @@ if(check_click_intercept(params,A)) return - if(stat || (lockcharge) || IsParalyzed() || IsStun()) + if(IS_UNCONSCIOUS_OR_CRIT(src) || (lockcharge) || IsParalyzed() || IsStun()) return var/list/modifiers = params2list(params) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 700a723c1a0e..b39ba3352c90 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -8,6 +8,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( "World-of-Darkness" = 'modular_darkpack/master_files/icons/hud/screen_darkness_new.dmi', // DARKPACK EDIT ADD - (Put at the top because it has almost full coverage now!) "Pentex-Knox" = 'modular_darkpack/master_files/icons/hud/screen_pentexknox.dmi', // DARKPACK EDIT ADD - (Trasen-Knox logo with a Pentex logo) + "Detective" = 'modular_darkpack/master_files/icons/hud/screen_detective.dmi' // DARKPACK EDIT ADD - I'd like one more waiter /* // DARKPACK EDIT REMOVAL "Midnight" = 'icons/hud/screen_midnight.dmi', "Retro" = 'icons/hud/screen_retro.dmi', @@ -478,6 +479,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /// Rebuilds our mob's hand slot screen elements /datum/hud/proc/build_hand_slots(update_hud = FALSE) + // Clean up existing hand slot objects + for (var/atom/movable/screen/inventory/hand/hand in screen_groups[HUD_GROUP_STATIC]) + remove_screen_object(hand, FALSE) for(var/i in 1 to length(mymob.held_items)) var/atom/movable/screen/inventory/hand/hand_box = add_screen_object(/atom/movable/screen/inventory/hand, HUD_KEY_HAND_SLOT(i), HUD_GROUP_STATIC, ui_style, ui_hand_position(i)) diff --git a/code/_onclick/hud/screen_objects/alert.dm b/code/_onclick/hud/screen_objects/alert.dm index 468912c5fe3f..383ccca26aef 100644 --- a/code/_onclick/hud/screen_objects/alert.dm +++ b/code/_onclick/hud/screen_objects/alert.dm @@ -557,7 +557,7 @@ SIGNAL_HANDLER if(QDELETED(offer.offered_item)) - examine_list += span_warning("[source]'s arm appears tensed up, as if [source.p_they()] plan on pulling it back suddenly...") + examine_list += span_warning("[source]'s arm appears tensed up, as if [source.p_they()] plan[source.p_s()] on pulling it back suddenly...") /atom/movable/screen/alert/give/hand screentip_override_text = "Take Hand" @@ -1171,13 +1171,12 @@ if(!.) return - var/mob/living/carbon/human/human_owner = owner - - if(!human_owner.can_resist() || !human_owner.shoes) + var/obj/item/clothing/shoes/shoes = owner.get_item_by_slot(ITEM_SLOT_FEET) + if(!owner.can_resist() || !istype(shoes, /obj/item/clothing/shoes)) return - human_owner.changeNext_move(CLICK_CD_RESIST) - human_owner.shoes.handle_tying(human_owner) + owner.changeNext_move(CLICK_CD_RESIST) + shoes.handle_tying(owner) /atom/movable/screen/alert/shoes/untied name = "Untied Shoes" diff --git a/code/_onclick/hud/screen_objects/screen_objects.dm b/code/_onclick/hud/screen_objects/screen_objects.dm index f83b22f70c61..8488181222b2 100644 --- a/code/_onclick/hud/screen_objects/screen_objects.dm +++ b/code/_onclick/hud/screen_objects/screen_objects.dm @@ -335,7 +335,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place mouse_over_pointer = MOUSE_HAND_POINTER /atom/movable/screen/drop/Click() - if(usr.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(usr)) usr.dropItemToGround(usr.get_active_held_item()) /atom/movable/screen/combattoggle @@ -600,14 +600,14 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place SetInvisibility(INVISIBILITY_ABSTRACT, INVISIBILITY_SOURCE_SLEEP_HUD_BUTTON) /atom/movable/screen/sleep/Click() - if(!isliving(usr) || HAS_TRAIT(usr, TRAIT_KNOCKEDOUT)) + if(!isliving(usr) || IS_UNCONSCIOUS(usr)) return if(!usr.client?.prefs.read_preference(/datum/preference/toggle/remove_double_click)) flick("[base_icon_state]_flick", src) return var/tgui_answer = tgui_alert(usr, "You sure you want to sleep for a while?", "Sleeping", list("Yes", "No")) - if(tgui_answer == "Yes" && !HAS_TRAIT(usr, TRAIT_KNOCKEDOUT)) + if(tgui_answer == "Yes" && !IS_UNCONSCIOUS(usr)) var/mob/living/L = usr L.Sleeping(400) @@ -974,6 +974,9 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place var/list/current_animated = LAZYLISTDUPLICATE(animated_zones) for(var/part_zone, body_part_untyped in owner.get_bodyparts_by_zones()) + // I hate that we "allow" support for more than 2 hands in the codebase + if(!limbs[part_zone]) + continue var/icon_key = 0 var/obj/item/bodypart/body_part = body_part_untyped var/list/overridable_key = list(icon_key) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f3b2a40fdb8f..1876419855c8 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -473,7 +473,7 @@ if(!attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_HEAD_INJURY_BLOCKED) && attacking_item.damtype == BRUTE) if(prob(damage_done)) adjust_organ_loss(ORGAN_SLOT_BRAIN, 20) - if(stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(src)) visible_message( span_danger("[src] is knocked senseless!"), span_userdanger("You're knocked senseless!"), @@ -487,7 +487,7 @@ // rev deconversion through blunt trauma. // this can be signalized to the rev datum - if(mind && stat == CONSCIOUS && src != attacker && prob(damage_done + ((100 - health) * 0.5))) + if(mind && !IS_UNCONSCIOUS_OR_CRIT(src) && src != attacker && prob(damage_done + ((100 - health) * 0.5))) var/datum/antagonist/rev/rev = mind.has_antag_datum(/datum/antagonist/rev) rev?.remove_revolutionary(attacker) @@ -495,7 +495,7 @@ if(.) add_blood_DNA_to_items(get_blood_dna_list(), ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING) - if(stat == CONSCIOUS && !attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) && attacking_item.damtype == BRUTE) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !attacking_item.get_sharpness() && !HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) && attacking_item.damtype == BRUTE) if(prob(damage_done)) visible_message( span_danger("[src] is knocked down!"), diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index e880bfc551b5..351e0fe2619a 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -15,7 +15,7 @@ * * Returns `COMPONENT_CANCEL_ATTACK_CHAIN` when it performs any action, to further acts on the attack chain. */ /atom/proc/attack_tk(mob/user) - if(user.stat || !tkMaxRangeCheck(user, src)) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !tkMaxRangeCheck(user, src)) return new /obj/effect/temp_visual/telekinesis(get_turf(src)) add_hiddenprint(user) @@ -24,7 +24,7 @@ /obj/attack_tk(mob/user) - if(user.stat) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return if(anchored) return ..() @@ -32,7 +32,7 @@ /obj/item/attack_tk(mob/user) - if(user.stat) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return return attack_tk_grab(user) diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm index 4102806be67f..27d5cdf3281d 100644 --- a/code/controllers/subsystem/ai_controllers.dm +++ b/code/controllers/subsystem/ai_controllers.dm @@ -1,35 +1,87 @@ +/// How many of the most expensive controllers to track per pass for the MC stat entry +#define AI_STAT_EXPENSIVE_TRACKED 5 + /// The subsystem used to tick [/datum/ai_controllers] instances. Handling the re-checking of plans. SUBSYSTEM_DEF(ai_controllers) name = "AI Controller Ticker" - ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND + ss_flags = SS_POST_FIRE_TIMING priority = FIRE_PRIORITY_NPC dependencies = list( /datum/controller/subsystem/movement/ai_movement, ) - wait = 0.5 SECONDS //Plan every half second if required, not great not terrible. + wait = 0.25 SECONDS //Plan every 1/4th second if required. In theory your AI should not be planning this much, but its useful because we want planning to be responsive when a previous plan ends. runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME var/list/currentrun = list() ///type of status we are interested in running var/planning_status = AI_STATUS_ON - /// The average tick cost of all active AI, calculated on fire. - var/our_cost - /// The tick cost of all currently processed AI, being summed together + /// CPU cost accumulated by the in-progress pass, summed across fires. var/summing_cost + /// world.time at which the in-progress pass started. + var/pass_started + /// How many controllers the in-progress pass started with. + var/pass_size + /// Average wall-clock duration for a full pass on controllers + var/average_pass_time + /// Longest gap any single controller went between two ticks. + var/longest_tick_gap + /// Running longest tick gap of the in-progress pass. + var/summing_tick_gap + /// Display strings for the most expensive controllers of the last completed pass, most expensive first. + var/list/most_expensive = list() + /// Worst SelectBehaviors cost seen this round. + var/worst_controller_cost = 0 + /// Display string for the controller responsible for worst_controller_cost. + var/worst_controller_name + /// Running top-cost candidates of the in-progress pass. Assoc list of controller -> SelectBehaviors cost in ms, has a capped amount of entries + var/list/summing_expensive = list() + /// Cheapest cost in summing_expensive once it's full; a controller must beat this to enter the list. + var/summing_expensive_cutoff = 0 + /// List of all targeting_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_targeting_strats() + var/list/targeting_strategies + /// List of all target_priority_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_target_priority_strats() + var/list/target_priority_strategies + /// List of all target_source singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_target_sources() + var/list/target_sources + ///AI controllers, sorted by their status + var/list/ai_controllers_by_status = list( + AI_STATUS_ON = list(), + AI_STATUS_ON_LOW = list(), + AI_STATUS_OFF = list(), + ) + ///AI controllers, sorted by their z level + var/list/ai_controllers_by_zlevel = list() + +/datum/controller/subsystem/ai_controllers/Recover() + if(islist(SSai_controllers.ai_controllers_by_status)) + ai_controllers_by_status = SSai_controllers.ai_controllers_by_status + if(islist(SSai_controllers.ai_controllers_by_zlevel)) + ai_controllers_by_zlevel = SSai_controllers.ai_controllers_by_zlevel /datum/controller/subsystem/ai_controllers/Initialize() - setup_subtrees() + setup_targeting_strats() + setup_target_priority_strats() + setup_target_sources() return SS_INIT_SUCCESS /datum/controller/subsystem/ai_controllers/stat_entry(msg) - var/list/planning_list = GLOB.ai_controllers_by_status[planning_status] - msg = "\n Planning AIs:[length(planning_list)]/[round(our_cost,1)]%" + msg = "\n Active:[length(SSai_controllers.ai_controllers_by_status[planning_status])]|Off:[length(SSai_controllers.ai_controllers_by_status[AI_STATUS_OFF])]" + msg += "\n Pass:[pass_size - length(currentrun)]/[pass_size]|AvgPass:[round(average_pass_time * 0.1, 0.1)]s|WorstGap:[round(longest_tick_gap * 0.1, 0.1)]s" + if(length(most_expensive)) + msg += "\n Top: [most_expensive.Join(" | ")]" + if(worst_controller_name) + msg += "\n Slowest bozo of the round: [worst_controller_name]" return ..() /datum/controller/subsystem/ai_controllers/fire(resumed) if(!resumed) - var/list/planning_list = GLOB.ai_controllers_by_status[planning_status] + var/list/planning_list = SSai_controllers.ai_controllers_by_status[planning_status] currentrun = planning_list.Copy() summing_cost = 0 + summing_tick_gap = 0 + summing_expensive = list() + summing_expensive_cutoff = 0 + pass_started = world.time + pass_size = length(currentrun) //cache for sanic speed (lists are references anyways) var/list/current_run = src.currentrun @@ -37,12 +89,35 @@ SUBSYSTEM_DEF(ai_controllers) while(length(current_run)) var/datum/ai_controller/ai_controller = current_run[length(current_run)] current_run.len-- - if(!ai_controller.able_to_plan) - continue - ai_controller.SelectBehaviors(wait * 0.1) + // Pass the real time since this controller last ticked, so SPT_PROB rolls and + // time accumulators stay time-correct even when a pass takes several seconds. + var/seconds_per_tick = wait * 0.1 + if(ai_controller.last_bt_tick) + var/tick_gap = world.time - ai_controller.last_bt_tick + summing_tick_gap = max(summing_tick_gap, tick_gap) + seconds_per_tick = tick_gap * 0.1 + ai_controller.last_bt_tick = world.time + var/controller_timer = TICK_USAGE_REAL + ai_controller.SelectBehaviors(seconds_per_tick) - if(!length(ai_controller.current_behaviors)) //Still no plan - ai_controller.planning_failed() + ///Lets check if this is an expensive controller + var/tick_cost = TICK_DELTA_TO_MS(TICK_USAGE_REAL - controller_timer) + if(tick_cost > worst_controller_cost) + worst_controller_cost = tick_cost + worst_controller_name = "[ai_controller.pawn || ai_controller] [round(tick_cost, 0.01)]ms" + if(tick_cost > summing_expensive_cutoff) + summing_expensive[ai_controller] = tick_cost + if(length(summing_expensive) > AI_STAT_EXPENSIVE_TRACKED) + var/cheapest_cost = INFINITY + var/datum/ai_controller/cheapest + for(var/datum/ai_controller/candidate as anything in summing_expensive) + if(summing_expensive[candidate] < cheapest_cost) + cheapest_cost = summing_expensive[candidate] + cheapest = candidate + summing_expensive -= cheapest + summing_expensive_cutoff = INFINITY + for(var/datum/ai_controller/candidate as anything in summing_expensive) + summing_expensive_cutoff = min(summing_expensive_cutoff, summing_expensive[candidate]) if(MC_TICK_CHECK) break @@ -51,20 +126,47 @@ SUBSYSTEM_DEF(ai_controllers) if(MC_TICK_CHECK) return - our_cost = MC_AVERAGE(our_cost, summing_cost) + average_pass_time = MC_AVERAGE(average_pass_time, world.time - pass_started) + longest_tick_gap = summing_tick_gap -///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list. -/datum/controller/subsystem/ai_controllers/proc/setup_subtrees() - if(length(GLOB.ai_subtrees)) - return - for(var/subtree_type in subtypesof(/datum/ai_planning_subtree)) - var/datum/ai_planning_subtree/subtree = new subtree_type - GLOB.ai_subtrees[subtree_type] = subtree + // Publish the pass's most expensive controllers as display strings, sorted most expensive first. + // Only a handful of entries, so a selection sort is fine. + var/list/expensive_entries = list() + while(length(summing_expensive)) + var/costliest_cost = 0 + var/datum/ai_controller/costliest + for(var/datum/ai_controller/candidate as anything in summing_expensive) + if(summing_expensive[candidate] >= costliest_cost) + costliest_cost = summing_expensive[candidate] + costliest = candidate + summing_expensive -= costliest + expensive_entries += "[costliest.pawn || costliest] [round(costliest_cost, 0.01)]ms" + most_expensive = expensive_entries ///Called when the max Z level was changed, updating our coverage. /datum/controller/subsystem/ai_controllers/proc/on_max_z_changed() - if(!length(GLOB.ai_controllers_by_zlevel)) - GLOB.ai_controllers_by_zlevel = new /list(world.maxz,0) - while (GLOB.ai_controllers_by_zlevel.len < world.maxz) - GLOB.ai_controllers_by_zlevel.len++ - GLOB.ai_controllers_by_zlevel[GLOB.ai_controllers_by_zlevel.len] = list() + if(!length(ai_controllers_by_zlevel)) + ai_controllers_by_zlevel = new /list(world.maxz,0) + while (ai_controllers_by_zlevel.len < world.maxz) + ai_controllers_by_zlevel.len++ + ai_controllers_by_zlevel[ai_controllers_by_zlevel.len] = list() + +/datum/controller/subsystem/ai_controllers/proc/setup_targeting_strats() + targeting_strategies = list() + for(var/target_type in subtypesof(/datum/targeting_strategy)) + var/datum/targeting_strategy/target_start = new target_type + targeting_strategies[target_type] = target_start + +/datum/controller/subsystem/ai_controllers/proc/setup_target_priority_strats() + target_priority_strategies = list() + for(var/target_type in subtypesof(/datum/target_priority_strategy)) + var/datum/target_priority_strategy/target_start = new target_type + target_priority_strategies[target_type] = target_start + +/datum/controller/subsystem/ai_controllers/proc/setup_target_sources() + target_sources = list() + for(var/source_type in subtypesof(/datum/target_source)) + var/datum/target_source/source = new source_type + target_sources[source_type] = source + +#undef AI_STAT_EXPENSIVE_TRACKED diff --git a/code/controllers/subsystem/ai_controllers_low_priority.dm b/code/controllers/subsystem/ai_controllers_low_priority.dm new file mode 100644 index 000000000000..a453a982f565 --- /dev/null +++ b/code/controllers/subsystem/ai_controllers_low_priority.dm @@ -0,0 +1,6 @@ +/// Plans background controllers that are active when unwatched but not critical +AI_CONTROLLER_SUBSYSTEM_DEF(low_priority_ai_controllers) + name = "AI Controller Ticker (Low)" + ss_flags = parent_type::ss_flags | SS_BACKGROUND | SS_NO_INIT + planning_status = AI_STATUS_ON_LOW + priority = FIRE_PRIORITY_NPC_LOW diff --git a/code/controllers/subsystem/ai_idle_controllers.dm b/code/controllers/subsystem/ai_idle_controllers.dm deleted file mode 100644 index 94b43207f82f..000000000000 --- a/code/controllers/subsystem/ai_idle_controllers.dm +++ /dev/null @@ -1,10 +0,0 @@ -AI_CONTROLLER_SUBSYSTEM_DEF(ai_idle_controllers) - name = "AI Idle Controllers" - ss_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND - priority = FIRE_PRIORITY_IDLE_NPC - dependencies = list( - /datum/controller/subsystem/ai_controllers, - ) - wait = 5 SECONDS - runlevels = RUNLEVEL_GAME - planning_status = AI_STATUS_IDLE diff --git a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm index f39f5de7a9e0..1b8c8d73e1fc 100644 --- a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm @@ -92,7 +92,7 @@ config_tag = "Heavy Pirates" midround_type = HEAVY_MIDROUND jobban_flag = ROLE_TRAITOR - ruleset_flags = RULESET_INVADER + ruleset_flags = RULESET_INVADER|RULESET_ADMIN_CONFIGURABLE weight = 3 min_pop = 25 min_antag_cap = 0 // ship will spawn if there are no ghosts around @@ -176,23 +176,31 @@ if(!ship.load(T)) CRASH("Loading pirate ship failed!") + //if the pirate has a "leader" type, fill that first + var/list/spawners = list() for(var/turf/area_turf as anything in ship.get_affected_turfs(T)) for(var/obj/effect/mob_spawn/ghost_role/human/pirate/spawner in area_turf) - if(candidates.len > 0) - var/mob/our_candidate = candidates[1] - var/mob/spawned_mob = spawner.create_from_ghost(our_candidate) - candidates -= our_candidate - notify_ghosts( - "The [chosen_gang.ship_name] has an object of interest: [spawned_mob]!", - source = spawned_mob, - header = "Pirates!", - ) + if(spawner.is_leader) + spawners.Insert(1, spawner) else - notify_ghosts( - "The [chosen_gang.ship_name] has an object of interest: [spawner]!", - source = spawner, - header = "Pirate Spawn Here!", - ) + spawners += spawner + + for(var/obj/effect/mob_spawn/ghost_role/human/pirate/spawner as anything in spawners) + if(candidates.len > 0) + var/mob/our_candidate = candidates[1] + var/mob/spawned_mob = spawner.create_from_ghost(our_candidate) + candidates -= our_candidate + notify_ghosts( + "The [chosen_gang.ship_name] has an object of interest: [spawned_mob]!", + source = spawned_mob, + header = "Pirates!", + ) + else + notify_ghosts( + "The [chosen_gang.ship_name] has an object of interest: [spawner]!", + source = spawner, + header = "Pirate Spawn Here!", + ) priority_announce(chosen_gang.arrival_announcement, sender_override = chosen_gang.ship_name) @@ -802,7 +810,7 @@ var/list/possible_targets = list() for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!player.client || !player.mind || player.stat != CONSCIOUS) + if(!player.client || !player.mind || IS_UNCONSCIOUS_OR_CRIT(player)) continue if(!(player.mind.assigned_role.job_flags & JOB_CREW_MEMBER)) continue @@ -1210,6 +1218,16 @@ /datum/dynamic_ruleset/midround/from_living/malf_ai/assign_role(datum/mind/candidate) candidate.add_antag_datum(/datum/antagonist/malf_ai) + if(!prob(33) || !isAI(candidate.current)) + return + priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM) + var/mob/living/silicon/new_malf_ai = candidate.current + var/obj/machinery/ai_law_rack/base/rack = new_malf_ai.get_law_rack() + rack?.scramble_ai_rack( + base_ion_prob = 100, + sub_ion_prob = 10, + ion_limit = 1, + ) /datum/dynamic_ruleset/midround/from_living/malf_ai/can_be_selected() return ..() && !HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index ac6640a76cf4..637aa1bab12b 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -37,7 +37,7 @@ VERB_MANAGER_SUBSYSTEM_DEF(input) "Any+UP" = "\"KeyUp \[\[*\]\] \[\[map.mouse-pos\]\] \[\[map.size\]\]\"", "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", "Tab" = "\".winset \\\"input.focus=true?map.focus=true:input.focus=true\\\"\"", - "Escape" = "Open-Escape-Menu", + "Escape" = "\".winset \\\"[SKIN_ESCAPE_MENU].is-visible=true?[SKIN_ESCAPE_MENU].is-visible=false:[SKIN_ESCAPE_MENU].is-visible=true\\\"\\n.output [SKIN_ESCAPE_MENU]:update [ESCAPE_MENU_TOGGLE_MESSAGE]\\nReset-Held-Keys\"", ) // Badmins just wanna have fun ♪ diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 6b8ef97dec9a..c7323cb15a31 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -20,6 +20,9 @@ SUBSYSTEM_DEF(mapping) ///Assoc list of all ruins spawned, key center of ruin spawn -> value ruin instance var/list/active_ruins = alist() + ///Ordered list of ruins that have been reserved. Each entry is list(ruin template, central turf, clear_below). Populated by seedRuins(), consumed by load_reserved_ruins(). + var/list/reserved_ruins = list() + ///List of ruins, separated by their theme var/list/themed_ruins = list() @@ -55,7 +58,6 @@ SUBSYSTEM_DEF(mapping) /// The largest plane offset we've generated so far var/max_plane_offset = 0 - var/loading_ruins = FALSE var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs) var/list/datum/turf_reservations //list of turf reservations var/list/used_turfs = list() //list of turf = datum/turf_reservation @@ -130,7 +132,7 @@ SUBSYSTEM_DEF(mapping) process_teleport_locs() //Sets up the wizard teleport locations preloadTemplates() -#ifndef LOWMEMORYMODE +#ifndef SKIP_SPACE_LEVELS // Create space ruin levels while (space_levels_so_far < current_map.space_ruin_levels) add_new_zlevel("Ruin Area [space_levels_so_far+1]", ZTRAITS_SPACE) @@ -156,15 +158,15 @@ SUBSYSTEM_DEF(mapping) else if (SSmapping.current_map.load_all_away_missions) // we're likely in a local testing environment, so punch it. load_all_away_missions() - loading_ruins = TRUE setup_ruins() - loading_ruins = FALSE #endif - // Run map generation after ruin generation to prevent issues + // Run map generation after ruin space is reserved, since this space is used for the cave gen. run_map_terrain_generation() // Generate our rivers, we do this here so the map doesn't load on top of them setup_rivers() + // Now that terrain generation is done, actually load the ruin maps in. + load_reserved_ruins() // now that the terrain is generated, including rivers, we can safely populate it with objects and mobs run_map_terrain_population() // Add the first transit level @@ -276,6 +278,26 @@ SUBSYSTEM_DEF(mapping) var/proportional_budget = round(CONFIG_GET(number/space_budget) * (space_ruins.len / DEFAULT_SPACE_RUIN_LEVELS)) seedRuins(space_ruins, proportional_budget, list(/area/space), themed_ruins[ZTRAIT_SPACE_RUINS], mineral_budget = 0, ruins_type = ZTRAIT_SPACE_RUINS) +///loads all of the ruins we previously reserved space for +/datum/controller/subsystem/mapping/proc/load_reserved_ruins() + for(var/list/reservation in reserved_ruins) + var/datum/map_template/ruin/reserved_ruin = reservation[1] + var/turf/central_turf = reservation[2] + var/clear_below = reservation[3] + load_ruin_now(reserved_ruin, central_turf, clear_below) + reserved_ruins.Cut() + +/** + * Immediately loads a single reserved ruin's map, and runs terrain generation for any + * of the areas, since they get spawned AFTER normal terrain gen runs its pass + */ +/datum/controller/subsystem/mapping/proc/load_ruin_now(datum/map_template/ruin/reserved_ruin, turf/central_turf, clear_below) + var/starting_area_count = GLOB.areas.len + reserved_ruin.load_reserved(central_turf, clear_below) + for(var/i in starting_area_count + 1 to GLOB.areas.len) + var/area/ruin_area = GLOB.areas[i] + ruin_area.RunTerrainGeneration() + /// Sets up rivers, and things that behave like rivers. So lava/plasma rivers, and chasms /// It is important that this happens AFTER generating mineral walls and such, since we rely on them for river logic /datum/controller/subsystem/mapping/proc/setup_rivers() @@ -456,7 +478,7 @@ Used by the AI doomsday and the self-destruct nuke. query_round_map_name.Execute() qdel(query_round_map_name) -#ifndef LOWMEMORYMODE +#ifndef SKIP_LAVALAND // DARKPACK EDIT CHANGE START - UMBRA if(current_map.minetype == MINETYPE_LAVALAND) diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm index ea1e7f90cda0..dcf01e4bfd4c 100644 --- a/code/controllers/subsystem/movement/movement_types.dm +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -129,12 +129,11 @@ owner?.processing_move_loop_flags = flags var/result = move() //Result is an enum value. Enums defined in __DEFINES/movement.dm - if(result) - EVLOG_PATH(moving, EVLOG_CATEGORY_MOVELOOPS, "Moved using [src]", list(old_loc, moving.loc)) //You might think, this runs a lot; but if not logging, it only does a lookup on the event logger. - if(moving) var/direction = get_dir(old_loc, moving.loc) SEND_SIGNAL(moving, COMSIG_MOVABLE_MOVED_FROM_LOOP, src, old_dir, direction) + if(result) + EVLOG_PATH(moving, EVLOG_CATEGORY_MOVELOOPS, "Moved using [src]", list(old_loc, moving.loc)) //You might think, this runs a lot; but if not logging, it only does a lookup on the event logger. owner?.processing_move_loop_flags = NONE SEND_SIGNAL(src, COMSIG_MOVELOOP_POSTPROCESS, result, delay * visual_delay) @@ -438,7 +437,8 @@ /datum/move_loop/has_target/jps/proc/on_finish_pathing(list/path) movement_path = path is_pathing = FALSE - EVLOG_PATH(moving, EVLOG_CATEGORY_JPS, "Planned AI path", movement_path) + if(moving) + EVLOG_PATH(moving, EVLOG_CATEGORY_JPS, "Planned AI path", movement_path) SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, path) /datum/move_loop/has_target/jps/move() diff --git a/code/controllers/subsystem/networks/bitrunning.dm b/code/controllers/subsystem/networks/bitrunning.dm index 1bb8eb49e4d7..7eaae94304f5 100644 --- a/code/controllers/subsystem/networks/bitrunning.dm +++ b/code/controllers/subsystem/networks/bitrunning.dm @@ -24,6 +24,8 @@ SUBSYSTEM_DEF(bitrunning) var/can_view = domain.can_view_name(scanner_tier, points) var/can_view_reward = domain.can_view_reward(scanner_tier, points) + var/grade_symbol = GLOB.bitrunning_grades[domain.best_grade] + UNTYPED_LIST_ADD(levels, list( "announce_ghosts" = domain.announce_to_ghosts, "cost" = domain.cost, @@ -34,6 +36,7 @@ SUBSYSTEM_DEF(bitrunning) "has_secondary_objectives" = counterlist_sum(domain.secondary_loot) ? TRUE : FALSE, "name" = can_view ? domain.name : REDACTED, "reward" = can_view_reward ? domain.reward_points : REDACTED, + "grade_symbol" = grade_symbol, )) return levels diff --git a/code/controllers/subsystem/processing/ai_behaviors.dm b/code/controllers/subsystem/processing/ai_behaviors.dm deleted file mode 100644 index aa5ef4eb55ce..000000000000 --- a/code/controllers/subsystem/processing/ai_behaviors.dm +++ /dev/null @@ -1,40 +0,0 @@ -/// The subsystem used to tick [/datum/ai_behavior] instances. Handling the individual actions an AI can take like punching someone in the fucking NUTS -PROCESSING_SUBSYSTEM_DEF(ai_behaviors) - name = "AI Behavior Ticker" - ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND - priority = FIRE_PRIORITY_NPC_ACTIONS - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - dependencies = list( - /datum/controller/subsystem/movement/ai_movement, - ) - wait = 1 - /// List of all ai_behavior singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_ai_behaviors() - var/list/ai_behaviors - /// List of all targeting_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_targeting_strats() - var/list/targeting_strategies - /// List of all target_priority_strategy singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_target_priority_strats() - var/list/target_priority_strategies - -/datum/controller/subsystem/processing/ai_behaviors/Initialize() - setup_ai_behaviors() - setup_targeting_strats() - setup_target_priority_strats() - return SS_INIT_SUCCESS - -/datum/controller/subsystem/processing/ai_behaviors/proc/setup_ai_behaviors() - ai_behaviors = list() - for(var/behavior_type in subtypesof(/datum/ai_behavior)) - var/datum/ai_behavior/ai_behavior = new behavior_type - ai_behaviors[behavior_type] = ai_behavior - -/datum/controller/subsystem/processing/ai_behaviors/proc/setup_targeting_strats() - targeting_strategies = list() - for(var/target_type in subtypesof(/datum/targeting_strategy)) - var/datum/targeting_strategy/target_start = new target_type - targeting_strategies[target_type] = target_start - -/datum/controller/subsystem/processing/ai_behaviors/proc/setup_target_priority_strats() - target_priority_strategies = list() - for(var/target_type in subtypesof(/datum/target_priority_strategy)) - var/datum/target_priority_strategy/target_start = new target_type - target_priority_strategies[target_type] = target_start diff --git a/code/controllers/subsystem/processing/ai_idle_behaviors.dm b/code/controllers/subsystem/processing/ai_idle_behaviors.dm deleted file mode 100644 index 2b631da96f3b..000000000000 --- a/code/controllers/subsystem/processing/ai_idle_behaviors.dm +++ /dev/null @@ -1,19 +0,0 @@ -PROCESSING_SUBSYSTEM_DEF(idle_ai_behaviors) - name = "AI Idle Behaviors" - ss_flags = SS_BACKGROUND - wait = 1.5 SECONDS - priority = FIRE_PRIORITY_IDLE_NPC - dependencies = list( - /datum/controller/subsystem/ai_controllers, - ) - ///List of all the idle ai behaviors - var/list/idle_behaviors = list() - -/datum/controller/subsystem/processing/idle_ai_behaviors/Initialize() - setup_idle_behaviors() - return SS_INIT_SUCCESS - -/datum/controller/subsystem/processing/idle_ai_behaviors/proc/setup_idle_behaviors() - for(var/behavior_type in subtypesof(/datum/idle_behavior)) - var/datum/idle_behavior/behavior = new behavior_type - idle_behaviors[behavior_type] = behavior diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 99623edb6288..5d97b05e5995 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -92,14 +92,18 @@ PROCESSING_SUBSYSTEM_DEF(quirks) for(var/type in quirk_list) var/datum/quirk/quirk_type = type - // DARKPACK EDIT ADD START - MERITS_FLAWS + // DARKPACK EDIT CHANGE START - MERITS_FLAWS if(!quirk_type::darkpack_allowed) continue if(quirk_type::roleplay_only && !CONFIG_GET(flag/roleplay_only_merits)) continue - // DARKPACK EDIT ADD END - quirk_prototypes[type] = new type + var/datum/quirk/quirk_datum = new type + if(!quirk_datum.soure_book_allowed(CONFIG_GET(string/ttrpg_accurate_cuttoff_merits))) + continue + + quirk_prototypes[type] = quirk_datum + // DARKPACK EDIT CHANGE END quirks[initial(quirk_type.name)] = quirk_type quirk_points[initial(quirk_type.name)] = initial(quirk_type.value) diff --git a/code/controllers/subsystem/processing/timed_actions.dm b/code/controllers/subsystem/processing/timed_actions.dm new file mode 100644 index 000000000000..e9fad49aeaf5 --- /dev/null +++ b/code/controllers/subsystem/processing/timed_actions.dm @@ -0,0 +1,6 @@ +PROCESSING_SUBSYSTEM_DEF(timed_actions) + name = "Timed Actions" + priority = FIRE_PRIORITY_TIMED_ACTIONS + ss_flags = SS_TICKER|SS_NO_INIT + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + wait = 1 diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 0b29ee9f042d..828069ba0033 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -538,7 +538,7 @@ SUBSYSTEM_DEF(shuttle) var/mob/living/silicon/ai/AI = thing if(AI.deployed_shell && !AI.deployed_shell.client) continue - if(AI.stat || !AI.client) + if(IS_UNCONSCIOUS_OR_CRIT(AI) || !AI.client) continue else if(istype(thing, /obj/machinery/computer/communications)) var/obj/machinery/computer/communications/C = thing diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index df33cf68c8c7..c548b4a5b14a 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -96,10 +96,11 @@ SUBSYSTEM_DEF(tgui) return null var/list/windows = user.client.tgui_windows var/window_id + var/window_limit = user.client.prefs?.read_preference(/datum/preference/toggle/tgui_unlimited_windows) ? TGUI_WINDOW_UNLIMITED_LIMIT : TGUI_WINDOW_HARD_LIMIT var/datum/tgui_window/window var/window_found = FALSE // Find a usable window - for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT) + for(var/i in 1 to window_limit) window_id = TGUI_WINDOW_ID(i) window = windows[window_id] // As we are looping, create missing window datums @@ -131,7 +132,8 @@ SUBSYSTEM_DEF(tgui) log_tgui(user, context = "SStgui/force_close_all_windows") if(user.client) user.client.tgui_windows = list() - for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT) + var/window_limit = user.client.prefs.read_preference(/datum/preference/toggle/tgui_unlimited_windows) ? TGUI_WINDOW_UNLIMITED_LIMIT : TGUI_WINDOW_HARD_LIMIT + for(var/i in 1 to window_limit) var/window_id = TGUI_WINDOW_ID(i) user << browse(null, "window=[window_id]") diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 00a7fd0fccd6..a5dc4ab1d1a7 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -14,7 +14,7 @@ SUBSYSTEM_DEF(ticker) /// or a "round-ending" event, like summoning Nar'Sie, a blob victory, the nuke going off, etc. ([FORCE_END_ROUND]) var/force_ending = END_ROUND_AS_NORMAL /// If TRUE, there is no lobby phase, the game starts immediately. - #ifdef ABSOLUTE_MINIMUM + #ifdef AUTOSTART_GAME var/start_immediately = TRUE #else var/start_immediately = FALSE @@ -402,11 +402,11 @@ SUBSYSTEM_DEF(ticker) if(L.client.inactivity >= GLOB.logout_timer_set) //Connected, but inactive (alt+tabbed or something) msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n" failed = TRUE //AFK client - if(!failed && L.stat) + if(!failed && IS_UNCONSCIOUS_OR_CRIT(L)) if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider msg += "[L.name] ([L.key]), the [L.job] ([span_bolddanger("Suicide")])\n" failed = TRUE //Disconnected client - if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT)) + if(!failed && (L.stat == HARD_CRIT)) msg += "[L.name] ([L.key]), the [L.job] (Dying)\n" failed = TRUE //Unconscious if(!failed && L.stat == DEAD) diff --git a/code/controllers/subsystem/unplanned_ai_idle_controllers.dm b/code/controllers/subsystem/unplanned_ai_idle_controllers.dm deleted file mode 100644 index 6385239e18c7..000000000000 --- a/code/controllers/subsystem/unplanned_ai_idle_controllers.dm +++ /dev/null @@ -1,4 +0,0 @@ -UNPLANNED_CONTROLLER_SUBSYSTEM_DEF(idle_unplanned_controllers) - name = "Unplanned AI Idle Controllers" - wait = 2.5 SECONDS - target_status = AI_STATUS_IDLE diff --git a/code/controllers/subsystem/unplanned_controllers.dm b/code/controllers/subsystem/unplanned_controllers.dm deleted file mode 100644 index e2e78e0951ee..000000000000 --- a/code/controllers/subsystem/unplanned_controllers.dm +++ /dev/null @@ -1,39 +0,0 @@ -GLOBAL_LIST_EMPTY(unplanned_controller_subsystems) -/// Handles making mobs perform lightweight "idle" behaviors such as wandering around when they have nothing planned -SUBSYSTEM_DEF(unplanned_controllers) - name = "Unplanned AI Controllers" - ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND - priority = FIRE_PRIORITY_UNPLANNED_NPC - dependencies = list( - /datum/controller/subsystem/movement/ai_movement, - ) - wait = 0.25 SECONDS - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - ///what ai status are we interested in - var/target_status = AI_STATUS_ON - var/list/current_run = list() - -/datum/controller/subsystem/unplanned_controllers/Initialize() - ..() - GLOB.unplanned_controller_subsystems += src - return SS_INIT_SUCCESS - -/datum/controller/subsystem/unplanned_controllers/Destroy() - GLOB.unplanned_controller_subsystems -= src - return ..() - -/datum/controller/subsystem/unplanned_controllers/stat_entry(msg) - msg = "\n Planning AIs:[length(GLOB.unplanned_controllers[target_status])]" - return ..() - -/datum/controller/subsystem/unplanned_controllers/fire(resumed) - if(!resumed) - src.current_run = GLOB.unplanned_controllers[target_status].Copy() - var/list/current_run = src.current_run // cache for sonic speed - while(length(current_run)) - var/datum/ai_controller/unplanned = current_run[current_run.len] - current_run.len-- - if(!QDELETED(unplanned)) - unplanned.idle_behavior.perform_idle_behavior(wait * 0.1, unplanned) - if (MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/wardrobe.dm b/code/controllers/subsystem/wardrobe.dm index f394c9b30d2b..7202227190d9 100644 --- a/code/controllers/subsystem/wardrobe.dm +++ b/code/controllers/subsystem/wardrobe.dm @@ -41,6 +41,9 @@ SUBSYSTEM_DEF(wardrobe) var/stock_hit = 0 /// How many items would we make just by loading the master list once? var/one_go_master = 0 + // Hit/Miss Tracking + var/list/hit_map = list() + var/list/miss_map = list() /// Item types that should not ever be recycled, only generated (like modsuits) var/static/list/recycle_blacklist = typecacheof(list( /obj/item/mod/control/pre_equipped, @@ -98,7 +101,7 @@ SUBSYSTEM_DEF(wardrobe) order_list[type_to_stock] = (amount_to_stock - (i - 1)) // Account for types we've already created return var/atom/movable/new_member = new type_to_stock() - stash_object(new_member) + yield_object(new_member) order_list -= type_to_stock if(MC_TICK_CHECK) @@ -163,7 +166,7 @@ SUBSYSTEM_DEF(wardrobe) * * type to stock - What type exactly do you want us to remember? * */ -/datum/controller/subsystem/wardrobe/proc/canonize_type(type_to_stock) +/datum/controller/subsystem/wardrobe/proc/canonize_type(type_to_stock, amount = 1) if(!type_to_stock) return if(!ispath(type_to_stock)) @@ -179,7 +182,7 @@ SUBSYSTEM_DEF(wardrobe) master_info[WARDROBE_CACHE_CALL_INSERT] = callback_info[WARDROBE_CALLBACK_INSERT] master_info[WARDROBE_CACHE_CALL_REMOVAL] = callback_info[WARDROBE_CALLBACK_REMOVE] canon_minimum[type_to_stock] = master_info - master_info[WARDROBE_CACHE_COUNT] += 1 + master_info[WARDROBE_CACHE_COUNT] += amount one_go_master++ /datum/controller/subsystem/wardrobe/proc/add_queue_item(queued_type, amount) @@ -217,17 +220,21 @@ SUBSYSTEM_DEF(wardrobe) if(is_type_in_typecache(object, recycle_blacklist)) qdel(object) return - stash_object(object) + yield_object(object) -/// Take an existing object, and insert it into our storage -/// If we can't or won't take it, it's deleted. You do not own this object after passing it in +/// Yields an object to the wardrobe subsystem, you no longer own it after calling this proc +/datum/controller/subsystem/wardrobe/proc/yield_object(obj/item/object) + if(!stash_object(object)) + qdel(object) + +/// Take an existing object, and tries to insert it into our storage +/// Returns TRUE if we've taken it (you no longer own it), FALSE if we have not /datum/controller/subsystem/wardrobe/proc/stash_object(obj/item/object) var/object_type = object.type var/list/master_info = canon_minimum[object_type] // I will not permit objects you didn't reserve ahead of time if(!master_info) - qdel(object) - return + return FALSE var/stock_target = master_info[WARDROBE_CACHE_COUNT] * cache_intensity var/amount_held = 0 @@ -237,12 +244,11 @@ SUBSYSTEM_DEF(wardrobe) // Doublely so for things we already have too much of if(amount_held - stock_target >= overflow_lienency) - qdel(object) - return + return FALSE // Fuck off if(QDELETED(object)) stack_trace("We tried to stash a qdeleted object, what did you do") - return + return FALSE if(!stock_info) stock_info = new /list(WARDROBE_STOCK_CALL_REMOVAL) @@ -251,20 +257,26 @@ SUBSYSTEM_DEF(wardrobe) stock_info[WARDROBE_STOCK_CALL_REMOVAL] = master_info[WARDROBE_CACHE_CALL_REMOVAL] preloaded_stock[object_type] = stock_info + if(object.loc != null) + object.moveToNullspace() var/datum/callback/do_on_insert = stock_info[WARDROBE_STOCK_CALL_INSERT] if(do_on_insert) do_on_insert.object = object do_on_insert.Invoke() do_on_insert.object = null - object.moveToNullspace() stock_info[WARDROBE_STOCK_CONTENTS] += object + return TRUE /datum/controller/subsystem/wardrobe/proc/provide_type(datum/requested_type, atom/movable/location) var/atom/movable/requested_object + if(!canon_minimum[requested_type]) + return new requested_type(location) + var/list/stock_info = preloaded_stock[requested_type] if(!stock_info) stock_miss++ + miss_map[requested_type]++ requested_object = new requested_type(location) return requested_object @@ -289,6 +301,7 @@ SUBSYSTEM_DEF(wardrobe) do_on_removal.object = null stock_hit++ + hit_map[requested_type]++ add_queue_item(requested_type, 1) // Requeue the item, under the assumption we'll never see it again if(!(contents_length - 1)) preloaded_stock -= requested_type @@ -313,6 +326,113 @@ SUBSYSTEM_DEF(wardrobe) if(!length(stock_info[WARDROBE_STOCK_CONTENTS])) preloaded_stock -= unload_type +/// Runs a speed test for all our various types +/// Measures the average cost of providing vs spawning, and deletion vs stashing +/datum/controller/subsystem/wardrobe/proc/speed_test(turf/spawn_on, attempt_count = 200) + var/old_overflow_lienency = overflow_lienency + overflow_lienency = INFINITY + + // Stock everything I want IMMEDIATELY + force_stock_wardrobe(attempt_count) + + // List of lists in the form list(type, new_cost, provide_cost, qdel_cost, stash_cost) + var/list/type_info = list() + for(var/atom/movable/type_to_test as anything in preloaded_stock) + var/new_cost = 0 + var/provide_cost = 0 + var/qdel_cost = 0 + var/stash_cost = 0 + var/stash_success = FALSE + for(var/i in 1 to attempt_count) + // Stopwatches are one per proc, so lets give them their own scope + do { + STAT_START_STOPWATCH + var/atom/hh = new type_to_test(spawn_on) + STAT_STOP_STOPWATCH + new_cost += STAT_TIME + qdel(hh) + } while(FALSE) + // In case subobjects are also tracked, this avoids any mistakes + force_stock_wardrobe(attempt_count) + // Now provide() + do { + STAT_START_STOPWATCH + var/atom/hh = SSwardrobe.provide_type(type_to_test, spawn_on) + STAT_STOP_STOPWATCH + provide_cost += STAT_TIME + qdel(hh) + } while(FALSE) + force_stock_wardrobe(attempt_count) + // qdel() + do { + var/atom/hh = SSwardrobe.provide_type(type_to_test, spawn_on) + STAT_START_STOPWATCH + /// Assignment is meaningless, just to make the comparison the same for stashing + stash_success = qdel(hh) + STAT_STOP_STOPWATCH + qdel_cost += STAT_TIME + } while(FALSE) + force_stock_wardrobe(attempt_count) + // stash_object() + do { + var/atom/hh = SSwardrobe.provide_type(type_to_test, spawn_on) + STAT_START_STOPWATCH + stash_success = SSwardrobe.stash_object(hh) + STAT_STOP_STOPWATCH + if(!stash_success) + qdel(hh) + stash_cost += STAT_TIME + } while(FALSE) + force_stock_wardrobe(attempt_count) + type_info += list(list(type_to_test, new_cost, provide_cost, qdel_cost, stash_cost)) + + overflow_lienency = old_overflow_lienency + // Ok now we have our data, time to print it + var/list/wardrobe_info = list("Performance information ([attempt_count] runs)

    ") + sortTim(type_info, cmp=/proc/cmp_wardrobe_performance) + for(var/list/deets in type_info) + wardrobe_info += "
  1. [deets[1]]
      " + wardrobe_info += "
    • New: [deets[2]]ms
    • " + wardrobe_info += "
    • Provide: [deets[3]]ms
    • " + wardrobe_info += "
    • Qdel: [deets[4]]ms
    • " + wardrobe_info += "
    • Stash: [deets[5]]ms
    • " + wardrobe_info += "
  2. " + wardrobe_info += "
" + + usr << browse(wardrobe_info.Join(), "window=wardrobe_perf") + +/proc/cmp_wardrobe_performance(list/A, list/B) + var/create_delta_a = A[3] - A[2] + var/create_delta_b = B[3] - B[2] + var/del_delta_a = A[5] - A[4] + var/del_delta_b = B[5] - B[4] + if((create_delta_a + del_delta_a) / 2 > (create_delta_b + del_delta_b) / 2) + return 1 + return -1 + +/// Stocks the wardrobe to stock_to items, no more no less +/// Only usable for testing, will not work well in production as it'll likely just get run over by other code +/datum/controller/subsystem/wardrobe/proc/force_stock_wardrobe(stock_to) + for(var/datum/loaded_type as anything in canon_minimum) + var/list/stock_info = preloaded_stock[loaded_type] + var/amount_held = 0 + if(stock_info) + amount_held = length(stock_info[WARDROBE_STOCK_CONTENTS]) + + var/target_delta = amount_held - stock_to + + // If we've got anything over the line, cut it back down + if(target_delta > 0) + unload_stock(loaded_type, target_delta, force = TRUE) + continue + // If we have more then we target, just don't you feel me? + else if (target_delta == 0) + continue + + // If we don't have enough, queue enough to make up the remainder + for(var/i in 1 to abs(target_delta)) + yield_object(new loaded_type()) + /// Sets up insertion and removal callbacks by typepath /// We will always use the deepest path. So /obj/item/blade/knife superceeds the entries of /obj/item and /obj/item/blade /// Mind this @@ -343,8 +463,8 @@ SUBSYSTEM_DEF(wardrobe) continue var/datum/species/fossil_record = new to_record() for(var/obj/item/species_request as anything in fossil_record.get_types_to_preload()) - for(var/i in 1 to 5) // Store 5 of each species, since that seems on par with 1 of each outfit - canonize_type(species_request) + // Store 5 of each species, since that seems on par with 1 of each outfit + canonize_type(species_request, 5) CHECK_TICK /datum/controller/subsystem/wardrobe/proc/load_storage_contents() diff --git a/code/datums/actions/action.dm b/code/datums/actions/action.dm index d453b15e5e66..b7dc4c6848e3 100644 --- a/code/datums/actions/action.dm +++ b/code/datums/actions/action.dm @@ -179,15 +179,14 @@ return FALSE if(action_disabled) return FALSE - if((check_flags & AB_CHECK_CONSCIOUS) && owner.stat != CONSCIOUS) + if((check_flags & AB_CHECK_CONSCIOUS) && IS_UNCONSCIOUS_OR_CRIT(owner)) if (feedback) - switch(owner.stat) - if(SOFT_CRIT) - owner.balloon_alert(owner, "downed!") - if(DEAD) - owner.balloon_alert(owner, "dead!") - else - owner.balloon_alert(owner, "unconscious!") + if(owner.stat == DEAD) + owner.balloon_alert(owner, "dead!") + else if(IS_UNCONSCIOUS(owner)) + owner.balloon_alert(owner, "unconscious!") + else + owner.balloon_alert(owner, "in critical!") return FALSE if((check_flags & AB_CHECK_HANDS_BLOCKED) && HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED)) if (feedback) @@ -342,6 +341,7 @@ current_button.color = rgb(255,255,255,255) else current_button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0) + SEND_SIGNAL(src, COMSIG_ACTION_STATUS_UPDATE, current_button, force) /// Gives our action to the passed viewer. /// Puts our action in their actions list and shows them the button. diff --git a/code/datums/actions/items/flight.dm b/code/datums/actions/items/flight.dm index 7df839830b5d..42b56657a09a 100644 --- a/code/datums/actions/items/flight.dm +++ b/code/datums/actions/items/flight.dm @@ -93,7 +93,7 @@ /// Largely lifted off of wing's can_fly proc, tailored to the jet boots functionality. /datum/action/item_action/toggle_flight/proc/can_fly() var/mob/living/carbon/human/human = owner - if(human.stat || human.body_position == LYING_DOWN || isnull(human.client)) + if(IS_UNCONSCIOUS_OR_CRIT(human) || human.body_position == LYING_DOWN || isnull(human.client)) return FALSE var/turf/location = get_turf(human) diff --git a/code/datums/actions/mobs/create_legion_skull.dm b/code/datums/actions/mobs/create_legion_skull.dm index 4f86eb7993d2..ae07efb2e0cf 100644 --- a/code/datums/actions/mobs/create_legion_skull.dm +++ b/code/datums/actions/mobs/create_legion_skull.dm @@ -16,4 +16,4 @@ /datum/action/cooldown/mob_cooldown/create_legion_skull/proc/create(atom/target) var/mob/living/basic/mining/legion_brood/gray_masses/minion = new(owner.loc) // APOC EDIT CHANGE minion.assign_creator(owner) - minion.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] = target + minion.ai_controller.blackboard[BB_CURRENT_TARGET] = target diff --git a/code/datums/actions/mobs/create_legion_turrets.dm b/code/datums/actions/mobs/create_legion_turrets.dm index d7a70d0c87fa..1d7cd169bdb9 100644 --- a/code/datums/actions/mobs/create_legion_turrets.dm +++ b/code/datums/actions/mobs/create_legion_turrets.dm @@ -62,7 +62,7 @@ /// Handles an extremely basic AI /obj/structure/legionturret/proc/set_up_shot() for(var/mob/living/possible_target in oview(9, src)) - if(possible_target.stat == DEAD || possible_target.stat == UNCONSCIOUS) + if(IS_UNCONSCIOUS(possible_target)) continue if(faction_check_atom(possible_target)) continue diff --git a/code/datums/ai/README.md b/code/datums/ai/README.md index fb4022775e8b..937d869ccc9b 100644 --- a/code/datums/ai/README.md +++ b/code/datums/ai/README.md @@ -8,19 +8,15 @@ Our AI controller system is an attempt at making it possible to create modulariz A datum that can be added to any atom in the game. Similarly to components, they might only support a given subtype (e.g. /mob/living), but the idea is that theoretically, you could apply a specific AI controller to a big a group of different types as possible and it would still work. -These datums handle both the normal movement of mobs, but also their decision making, deciding which actions they will take based on the checks you put into their SelectBehaviors proc. - -If behaviors are selected, and the AI is in range, it will try to perform them. It runs all the behaviors it currently has in parallel; allowing for it to for example screech at someone while trying to attack them. As long as it has behaviors running, it will not try to generate new plans, making it not waste CPU when it already has an active goal. - They also hold data for any of the actions they might need to use, such as cooldowns, whether or not they're currently fighting, etcetera this is stored in the blackboard, more information on that below. ### Blackboard -The blackboard is an associated list keyed with strings and with values of whatever you want. These store information the mob has such as "Am I attacking someone", "Do I have a weapon". By using an associated list like this, no data needs to be stored on the actions themselves, and you could make actions that work on multiple ai controllers if you so pleased by making the key to use a variable. +The blackboard is an associated list keyed with strings and with values of whatever you want. These store information the mob has such as "Am I attacking someone", "Do I have a weapon". By using an associated list like this, You could make actions that work on multiple ai controllers if you so pleased by making the key to use a variable. ## AI Behavior -AI behaviors are the actions an AI can take. These can range from "Do an emote" to "Attack this target until he is dead". They are singletons and should contain nothing but static data. Any dynamic data should be stored in the blackboard, to allow different controllers to use the same behaviors. +AI behaviors are the actions an AI can take. These can range from "Do an emote" to "Attack this target". Any dynamic data such as target should be stored in the blackboard # Guides: diff --git a/code/datums/ai/_ai_behavior.dm b/code/datums/ai/_ai_behavior.dm index a73ca1672147..dd26ab204a8b 100644 --- a/code/datums/ai/_ai_behavior.dm +++ b/code/datums/ai/_ai_behavior.dm @@ -1,46 +1,131 @@ -///Abstract class for an action an AI can take, can range from movement to grabbing a nearby weapon. -/datum/ai_behavior - ///What distance you need to be from the target to perform the action - var/required_distance = 1 - ///Flags for extra behavior +/// Base type for AI behavior leaf nodes in the behavior tree system. +/// setup() is called once on first activation, perform() each tick while running. +/// Returns BT_SUCCESS / BT_FAILURE on completion, BT_RUNNING while active. +/datum/bt_node/ai_behavior + ///Flags for extra behavior (see AI_BEHAVIOR_* defines) var/behavior_flags = NONE - ///Cooldown between actions performances, defaults to the value of CLICK_CD_MELEE because that seemed like a nice standard for the speed of AI behavior - ///Do not read directly or mutate, instead use get_cooldown() - var/action_cooldown = CLICK_CD_MELEE - -/// Returns the delay to use for this behavior in the moment -/// Override to return a conditional delay -/datum/ai_behavior/proc/get_cooldown(datum/ai_controller/cooldown_for) - return action_cooldown - -/// Called by the ai controller when first being added. Additional arguments depend on the behavior type. -/// Return FALSE to cancel -/datum/ai_behavior/proc/setup(datum/ai_controller/controller, ...) + ///Cooldown between perform() calls; do not read directly use get_cooldown() + var/time_between_perform = 0 + /// TRUE after setup() has been called and before finish_action() completes. + var/running = FALSE + /// world.time when perform() may next be called. + var/next_perform_time = 0 + /// TRUE when the last perform() failed and we are waiting out next_perform_time to say we failed + var/failed_last_perform = FALSE + /// TRUE while an async perform kicked off by start_async() is going + VAR_PRIVATE/async_running = FALSE + /// TRUE once async finished and via finish_async + VAR_PRIVATE/async_finished = FALSE + /// AI_BEHAVIOR_* flags that came out of async perform + VAR_PRIVATE/async_result_flags = NONE + +/datum/bt_node/ai_behavior/has_active_descendants() + return running + +/datum/bt_node/ai_behavior/get_status_marker() + if(running) + return "*" + return ..() + +/datum/bt_node/ai_behavior/append_active_nodes(list/lines, indent) + if(running) + lines += "[indent][span_bold("● [label]")]" + +/** + * ai behavior tick. Runs setup() once on first activation, then perform() each tick. + * Respects per-controller cooldowns set by AI_BEHAVIOR_DELAY. + * Returns BT_SUCCESS / BT_FAILURE on completion, BT_RUNNING while active. + */ +/datum/bt_node/ai_behavior/tick(datum/ai_controller/controller, seconds_per_tick) + if(next_perform_time > world.time) + if(!running && failed_last_perform) + return BT_FAILURE + controller.active_execution_index = execution_index + return BT_RUNNING + + if(controller.bt_execution_log != null) // dont track if we're not viewing + if(length(controller.bt_execution_log) < BT_EXECUTION_LOG_MAX) + controller.bt_execution_log += execution_index + + if(!running) + if(!setup(controller)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] [type]: setup() failed") + return BT_FAILURE + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] starting [type]") + running = TRUE + + var/process_flags = perform(seconds_per_tick, controller) + + if(process_flags & AI_BEHAVIOR_DELAY) + next_perform_time = world.time + get_cooldown(controller) + if(process_flags & AI_BEHAVIOR_SUCCEEDED) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] [type]: succeeded") + failed_last_perform = FALSE + finish_action(controller, TRUE) + return BT_SUCCESS + if(process_flags & AI_BEHAVIOR_FAILED) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] [type]: failed") + failed_last_perform = TRUE + finish_action(controller, FALSE) + return BT_FAILURE + controller.active_execution_index = execution_index + return BT_RUNNING + +/// Returns the cooldown to apply after a AI_BEHAVIOR_DELAY perform(). Override for conditional delays. +/datum/bt_node/ai_behavior/proc/get_cooldown(datum/ai_controller/cooldown_for) + return time_between_perform + +/// Called when this behavior first activates on a controller. Return FALSE to abort (returns BT_FAILURE). +/datum/bt_node/ai_behavior/proc/setup(datum/ai_controller/controller) return TRUE -///Called by the AI controller when this action is performed -///Returns a set of flags defined in [code/__DEFINES/ai/ai.dm] -/datum/ai_behavior/proc/perform(seconds_per_tick, datum/ai_controller/controller, ...) +/// Called each tick while the behavior is running. Returns AI_BEHAVIOR_* flags. +/datum/bt_node/ai_behavior/proc/perform(seconds_per_tick, datum/ai_controller/controller) + SHOULD_NOT_SLEEP(TRUE) return -///Called when the action is finished. This needs the same args as perform besides the default ones -/datum/ai_behavior/proc/finish_action(datum/ai_controller/controller, succeeded, ...) - controller.dequeue_behavior(src) - controller.behavior_args -= type - if(!(behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT)) //If this was a movement task, reset our movement target if necessary - return - if(behavior_flags & AI_BEHAVIOR_KEEP_MOVE_TARGET_ON_FINISH) - return - clear_movement_target(controller) - controller.ai_movement.stop_moving_towards(controller) - EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] has [succeeded ? "succeeded" : "failed"] at performing [src]", get_turf(controller.pawn), "Behavior finished: [succeeded ? "Success" : "Failure"]") +/// Called when the behavior finishes (succeeded or failed). Subtypes should call ..(). +/datum/bt_node/ai_behavior/proc/finish_action(datum/ai_controller/controller, succeeded) + SHOULD_CALL_PARENT(TRUE) + running = FALSE + async_running = FALSE + async_finished = FALSE + async_result_flags = NONE -/// Helper proc to ensure consistency in setting the source of the movement target -/datum/ai_behavior/proc/set_movement_target(datum/ai_controller/controller, atom/target, datum/ai_movement/new_movement) - controller.set_movement_target(type, target, new_movement) +///Checks if we're running async behavior, and if its finished, returns result flags +/datum/bt_node/ai_behavior/proc/handle_async() + if(async_running) + return AI_BEHAVIOR_DELAY + if(async_finished) + return async_result_flags | AI_BEHAVIOR_DELAY + return NONE -/// Clear the controller's movement target only if it was us who last set it -/datum/ai_behavior/proc/clear_movement_target(datum/ai_controller/controller) - if (controller.movement_target_source != type) +///Marks that async behavior has started and runs perform_async +/datum/bt_node/ai_behavior/proc/start_async() + async_running = TRUE + INVOKE_ASYNC(src, PROC_REF(perform_async), owning_controller) + return AI_BEHAVIOR_DELAY + +///Override this if you have sleeping behavior, be sure to implement the other async procs in perform() +/datum/bt_node/ai_behavior/proc/perform_async(datum/ai_controller/controller) + return + +/// Call from an async behavior after its sleeping call, before committing side effects. FALSE means the behavior was aborted/reset mid-flight bail out without side effects. +/datum/bt_node/ai_behavior/proc/async_still_valid() + return async_running && !QDELETED(owning_controller?.pawn) + +/// Call from an async behavior to commit its result. No-op if the behavior was aborted mid-flight. +/datum/bt_node/ai_behavior/proc/finish_async(result_flags) + if(!async_still_valid()) return - controller.set_movement_target(type, null) + async_result_flags = result_flags + async_finished = TRUE + async_running = FALSE + +/datum/bt_node/ai_behavior/proc/modify_cooldown(new_next_perform_time) + next_perform_time = new_next_perform_time + +/datum/bt_node/ai_behavior/reset_tick_state() + if(running) + finish_action(owning_controller, FALSE) + ..() diff --git a/code/datums/ai/_ai_bt_composites.dm b/code/datums/ai/_ai_bt_composites.dm new file mode 100644 index 000000000000..765472df88f7 --- /dev/null +++ b/code/datums/ai/_ai_bt_composites.dm @@ -0,0 +1,307 @@ +/** + * Base composite node. Holds an ordered list of child bt_node instances. + * */ +/datum/bt_node/composite + /// Typepaths of child nodes declared on the type. Resolved to instances at tree construction. + var/list/children_typepaths = null + /// Resolved child instances. Populated at tree construction. Do not set directly. + var/list/children = null + +/datum/bt_node/composite/Destroy() + QDEL_LIST(children) + return ..() + +/datum/bt_node/composite/get_children() + return children + +/datum/bt_node/composite/has_active_descendants() + if(!children) + return FALSE + for(var/datum/bt_node/child as anything in children) + if(child.has_active_descendants()) + return TRUE + return FALSE + +/datum/bt_node/composite/finalize_node(datum/ai_controller/controller, list/to_visit) + ..() + if(!children) + return + for(var/datum/bt_node/child as anything in children) + child.parent_node = src + to_visit += children + +/datum/bt_node/composite/set_descriptor_children(list/children_descs, datum/ai_controller/controller) + var/list/resolved = list() + for(var/child_entry in children_descs) + var/datum/bt_node/child_node = controller.get_or_build_node(child_entry) + if(!isnull(child_node)) + resolved += child_node + children = resolved + +/datum/bt_node/composite/collect_reset_children(list/to_visit) + if(children) + to_visit += children + +/datum/bt_node/composite/assign_execution_indices(counter) + execution_index = counter + counter++ + for(var/datum/bt_node/c in children) + counter = c.assign_execution_indices(counter) + last_execution_index = counter - 1 + return counter + +/** + * Sequence node: ticks children in order. + * Returns BT_FAILURE on first child failure. + * Returns BT_RUNNING on first child returning BT_RUNNING (stops further evaluation). + * Returns BT_SUCCESS only if all children succeed. + * + * Resumes from the last RUNNING child index rather than restarting from child 1. + */ +/datum/bt_node/composite/sequence + node_type = BT_NODE_SEQUENCE + label = "SEQUENCE" + /// Index of the child that last returned BT_RUNNING. + var/running_child_index = 0 + +/datum/bt_node/composite/sequence/tick(datum/ai_controller/controller, seconds_per_tick) + var/result = BT_SUCCESS + var/start = running_child_index || 1 + for(var/i in start to length(children)) + var/datum/bt_node/child = children[i] + var/child_result = child.tick(controller, seconds_per_tick) + if(controller.cancelled_during_tick) + return BT_FAILURE + if(child_result != BT_SUCCESS) + result = child_result + if(child_result == BT_RUNNING) + running_child_index = i + else + running_child_index = 0 + return result + + running_child_index = 0 + return result + +/datum/bt_node/composite/sequence/reset_tick_state() + . = ..() + running_child_index = 0 + +/datum/bt_node/composite/sequence/append_active_nodes(list/lines, indent) + var/found_active = FALSE + for(var/datum/bt_node/child as anything in children) + if(found_active) + lines += "[indent]↑ [child.label]" + else if(child.has_active_descendants()) + found_active = TRUE + child.append_active_nodes(lines, indent) + +/datum/bt_node/composite/sequence/append_full_tree_state(list/lines, indent) + var/child_info = running_child_index ? " (child [running_child_index]/[length(children)])" : "" + lines += "[indent][get_status_marker()] SEQUENCE[child_info]" + for(var/datum/bt_node/child as anything in children) + child.append_full_tree_state(lines, "[indent] ") + +/** + * Selector node: ticks children in order. + * Returns the first non-BT_FAILURE result (BT_SUCCESS or BT_RUNNING), stopping further evaluation. + * Returns BT_FAILURE only if all children fail. + * + * Resumes from the last RUNNING child index rather than restarting from child 1. + + */ +/datum/bt_node/composite/selector + node_type = BT_NODE_SELECTOR + label = "SELECTOR" + /// Index of the child that last returned BT_RUNNING. + var/running_child_index = 0 + +/datum/bt_node/composite/selector/tick(datum/ai_controller/controller, seconds_per_tick) + var/result = BT_FAILURE + var/start = running_child_index || 1 + for(var/i in start to length(children)) + var/datum/bt_node/child = children[i] + var/child_result = child.tick(controller, seconds_per_tick) + if(controller.cancelled_during_tick) + return BT_FAILURE + if(child_result != BT_FAILURE) + result = child_result + if(child_result == BT_RUNNING) + running_child_index = i + else + running_child_index = 0 + return result + + running_child_index = 0 + return result + +/datum/bt_node/composite/selector/reset_tick_state() + . = ..() + running_child_index = 0 + +/datum/bt_node/composite/selector/append_active_nodes(list/lines, indent) + for(var/datum/bt_node/child as anything in children) + if(child.has_active_descendants()) + child.append_active_nodes(lines, indent) + return + +/datum/bt_node/composite/selector/append_full_tree_state(list/lines, indent) + var/child_info = running_child_index ? " (child [running_child_index]/[length(children)])" : "" + lines += "[indent][get_status_marker()] SELECTOR[child_info]" + for(var/datum/bt_node/child as anything in children) + child.append_full_tree_state(lines, "[indent] ") + +/** + * Subplan node: Runs child and applies configurable restart policies + * when the child completes instead of propagating completion directly. + * + * success_policy: + * BT_SUBPLAN_SUCCEED_ON_SUCCESS (default) propagates BT_SUCCESS when all children succeed. + * BT_SUBPLAN_LOOP_ON_SUCCESS resets all children and returns BT_RUNNING, restarting next tick. + * + * failure_policy: + * BT_SUBPLAN_FAIL_ON_FAILURE (default) propagates BT_FAILURE when a child fails. + * BT_SUBPLAN_LOOP_ON_FAILURE resets all children and returns BT_RUNNING, restarting next tick. + * + * Combining both loop policies creates an infinite loop that only exits via an external observer abort or cancel_current_plan(), so be careful pls + */ +/datum/bt_node/composite/subplan + node_type = BT_NODE_SUBPLAN + /// BT_SUBPLAN_SUCCEED_ON_SUCCESS: propagate success (default). BT_SUBPLAN_LOOP_ON_SUCCESS: restart. + var/success_policy = BT_SUBPLAN_SUCCEED_ON_SUCCESS + /// BT_SUBPLAN_FAIL_ON_FAILURE: propagate failure (default). BT_SUBPLAN_LOOP_ON_FAILURE: restart. + var/failure_policy = BT_SUBPLAN_FAIL_ON_FAILURE + /// Minimum delay before ticking again after a loop policy restarts the child. 0 = immediate (default). + var/loop_delay = 0 + /// world.time when this subplan is next allowed to tick after a loop restart. + var/next_loop_time = 0 + +/datum/bt_node/composite/subplan/tick(datum/ai_controller/controller, seconds_per_tick) + if(loop_delay > 0 && next_loop_time > world.time) + return BT_RUNNING + + var/datum/bt_node/child = LAZYACCESS(children, 1) + if(isnull(child)) + next_loop_time = 0 + return BT_FAILURE + + var/child_result = child.tick(controller, seconds_per_tick) + if(controller.cancelled_during_tick) + return BT_FAILURE + + if(child_result == BT_RUNNING) + return BT_RUNNING + + if(child_result == BT_FAILURE) + if(failure_policy == BT_SUBPLAN_LOOP_ON_FAILURE) + child.reset_tick_state() + if(loop_delay > 0) + next_loop_time = world.time + loop_delay + return BT_RUNNING + next_loop_time = 0 + return BT_FAILURE + + if(success_policy == BT_SUBPLAN_LOOP_ON_SUCCESS) + child.reset_tick_state() + if(loop_delay > 0) + next_loop_time = world.time + loop_delay + return BT_RUNNING + next_loop_time = 0 + return BT_SUCCESS + +/datum/bt_node/composite/subplan/reset_tick_state() + . = ..() + next_loop_time = 0 + +/datum/bt_node/composite/subplan/set_descriptor_children(list/children_descs, datum/ai_controller/controller) + ..() + if(length(children) > 1) + var/datum/bt_node/composite/sequence/legacy_subplan_sequence = new + legacy_subplan_sequence.children = children + children = list(legacy_subplan_sequence) + +/** + * Parallel node: ticks ALL children every planning cycle, regardless of intermediate results. + * Success and failure are determined by the configurable success_policy and failure_policy. + * + * Intended use: run multiple independent branches simultaneously, e.g. action + locomotion + */ +/datum/bt_node/composite/parallel + node_type = BT_NODE_PARALLEL + label = "PARALLEL" + /// BT_PARALLEL_SUCCESS_CHILD_ONE: succeed when child 1 succeeds (default). + /// BT_PARALLEL_SUCCESS_ALL: succeed only when all children succeed. + var/success_policy = BT_PARALLEL_SUCCESS_CHILD_ONE + /// BT_PARALLEL_FAILURE_CHILD_ONE: fail when child 1 fails (default). + /// BT_PARALLEL_FAILURE_ANY: fail when any child fails. + var/failure_policy = BT_PARALLEL_FAILURE_CHILD_ONE + /// If TRUE, children 2+ that complete are reset and reticked + var/repeat_secondary = FALSE + /// Minimum delay before a repeat_secondary child can be re-ticked after completing. 0 = immediate (default). + var/repeat_secondary_delay = 0 + /// world.time values for when each secondary child is next allowed to tick. Null when no delays are active. + var/list/secondary_ready_at = null + /// If TRUE, when child 1 finishes (non-RUNNING), all children 2+ are cancelled and the parallel immediately returns child 1's result. + var/finish_on_primary = FALSE + +/datum/bt_node/composite/parallel/tick(datum/ai_controller/controller, seconds_per_tick) + var/succeeded = 0 + var/failed = 0 + var/primary_result + + for(var/i in 1 to length(children)) + var/datum/bt_node/child = children[i] + + if(i > 1 && repeat_secondary && repeat_secondary_delay > 0 && LAZYACCESS(secondary_ready_at, i) > world.time) + continue // secondary child is waiting out its repeat delay + + var/child_result = child.tick(controller, seconds_per_tick) + if(controller.cancelled_during_tick) + return BT_FAILURE + + if(i == 1) + primary_result = child_result + if(child_result == BT_SUCCESS) + succeeded++ + else if(child_result == BT_FAILURE) + failed++ + else if(repeat_secondary && child_result != BT_RUNNING) + child.reset_tick_state() + if(repeat_secondary_delay > 0) + if(length(secondary_ready_at) < i) + LAZYSETLEN(secondary_ready_at, i) + secondary_ready_at[i] = world.time + repeat_secondary_delay + else + if(child_result == BT_SUCCESS) + succeeded++ + else if(child_result == BT_FAILURE) + failed++ + + if(finish_on_primary && primary_result != BT_RUNNING) + // Secondaries may be mid-RUNNING here, so the reset must recurse to cancel any deeper active behaviors. + for(var/i in 2 to length(children)) + var/datum/bt_node/child = children[i] + child.reset_subtree_tick_states() + return primary_result + + if((failure_policy == BT_PARALLEL_FAILURE_CHILD_ONE && primary_result == BT_FAILURE) || \ + (failure_policy == BT_PARALLEL_FAILURE_ANY && failed > 0)) + return BT_FAILURE + if((success_policy == BT_PARALLEL_SUCCESS_CHILD_ONE && primary_result == BT_SUCCESS) || \ + (success_policy == BT_PARALLEL_SUCCESS_ALL && succeeded == length(children))) + return BT_SUCCESS + return BT_RUNNING + +/datum/bt_node/composite/parallel/reset_tick_state() + . = ..() + secondary_ready_at = null + +/datum/bt_node/composite/parallel/append_active_nodes(list/lines, indent) + for(var/datum/bt_node/child as anything in children) + if(child.has_active_descendants()) + child.append_active_nodes(lines, indent) + +/datum/bt_node/composite/parallel/append_full_tree_state(list/lines, indent) + lines += "[indent][get_status_marker()] PARALLEL" + for(var/datum/bt_node/child as anything in children) + child.append_full_tree_state(lines, "[indent] ") diff --git a/code/datums/ai/_ai_bt_decorators.dm b/code/datums/ai/_ai_bt_decorators.dm new file mode 100644 index 000000000000..14472eadd0fd --- /dev/null +++ b/code/datums/ai/_ai_bt_decorators.dm @@ -0,0 +1,241 @@ +/** + * Base decorator node. Wraps a single child with a condition check. + * + * + * Supports observer aborts: register to watch specific signals, which triggers a re-check of the condition, potentially aborting the plan depending on the observer_abort settings. + */ +/datum/bt_node/decorator + node_type = BT_NODE_DECORATOR + /// Typepath of the single child node. Resolved to an instance at tree construction. + var/child_typepath = null + /// Resolved child instance. Populated at tree construction. Do not set directly. + var/datum/bt_node/child = null + /// Observer abort mode. Controls reactive re-planning when watched keys change. BT_ABORT_NONE (default) means no reactivity. BT_ABORT_SELF triggers re-plan if we're inside one of our children and the condition changes, while BT_ABORT_LOWER_PRIORITY triggers re-plan if we are in a lower priority (e.g. further to the right) node and the condition changes. BT_ABORT_BOTH does both. + var/observer_abort = BT_ABORT_NONE + /// If TRUE, the result of check_condition() is inverted before gating the child. + var/invert = FALSE + /// Whether the child is currently BT_RUNNING. This makes tick() skip check_condition() and delegate directly to child.tick(). + var/child_active = FALSE + /// Set to TRUE once register_observe_signals() has been called for this instance. + var/observers_registered = FALSE + /// Set to TRUE when register_observe_signals() registered at least one signal. If this is not true but we are observing; then we need to check the condition every tick; not efficient, but allows for reactivity. + var/has_observer_signals = FALSE + /// Last result seen by poll_condition(). null = not yet polled. Used to detect condition changes when no signal is available. + var/last_poll_result = null + /// TRUE when this decorator is registered in the controller's polling_observers list. + var/is_polled = FALSE + /// When polling (no observer signals), minimum deciseconds between condition re-evaluations. 0 = every controller tick. Please don't run viewers() every tick bro. + var/polling_rate = 0 + /// world.time of the last poll_condition() evaluation. Only meaningful when polling_rate > 0. + VAR_PRIVATE/last_poll_time = 0 + + +/datum/bt_node/decorator/Destroy() + if(observers_registered) + unregister_observe_signals(owning_controller?.pawn) + if(is_polled) + LAZYREMOVE(owning_controller?.polling_observers, src) + is_polled = FALSE + observers_registered = FALSE + has_observer_signals = FALSE + QDEL_NULL(child) + return ..() + +/datum/bt_node/decorator/get_children() + return child ? list(child) : null + +/datum/bt_node/decorator/has_active_descendants() + return child && child.has_active_descendants() + +/datum/bt_node/decorator/finalize_node(datum/ai_controller/controller, list/to_visit) + ..() + if(child) + child.parent_node = src + to_visit += child + +/datum/bt_node/decorator/append_active_nodes(list/lines, indent) + if(child && child.has_active_descendants()) + lines += "[indent][label]" + child.append_active_nodes(lines, "[indent] ") + +/datum/bt_node/decorator/set_descriptor_children(list/children_descs, datum/ai_controller/controller) + var/datum/bt_node/resolved = controller.get_or_build_node(children_descs[1]) + if(!isnull(resolved)) + child = resolved + +/datum/bt_node/decorator/collect_reset_children(list/to_visit) + if(child) + to_visit += child + +/datum/bt_node/decorator/append_full_tree_state(list/lines, indent) + var/observer_text = "" + if(observer_abort != BT_ABORT_NONE) + var/abort_name = "" + if(observer_abort == BT_ABORT_SELF) + abort_name = "SELF" + else if(observer_abort == BT_ABORT_LOWER_PRIORITY) + abort_name = "LOWER" + else if(observer_abort == BT_ABORT_BOTH) + abort_name = "BOTH" + observer_text = " (abort-[abort_name])" + lines += "[indent][get_status_marker()] [label][observer_text]" + if(child) + child.append_full_tree_state(lines, "[indent] ") + +/datum/bt_node/decorator/tick(datum/ai_controller/controller, seconds_per_tick) + if(!observers_registered) + observers_registered = TRUE + if(observer_abort != BT_ABORT_NONE) + has_observer_signals = register_observe_signals(controller.pawn) + if(!has_observer_signals) + is_polled = TRUE + LAZYADDASSOC(controller.polling_observers, src, TRUE) + + var/child_ticked = FALSE + var/result + if(!child) + return BT_FAILURE + var/no_ticking_condition = observer_abort == BT_ABORT_NONE || has_observer_signals + if((no_ticking_condition || is_polled) && child_active) + child_ticked = TRUE + result = child.tick(controller, seconds_per_tick) + else if(!!check_condition(controller) == invert) + result = BT_FAILURE + else + child_ticked = TRUE + result = child.tick(controller, seconds_per_tick) + + if(controller.cancelled_during_tick) + child_active = FALSE + return BT_FAILURE + + if(no_ticking_condition || is_polled) + child_active = (result == BT_RUNNING) + if(child_ticked && !child_active) + on_child_complete(controller, result) + + return result + +/** + * Called when the child finishes (returns a non-RUNNING result after being ticked). + * NOT called when the condition gate blocks the child, or when the tree is cancelled mid-tick. + */ +/datum/bt_node/decorator/proc/on_child_complete(datum/ai_controller/controller, result) + return + +/** + * Override to implement custom condition logic. + */ +/datum/bt_node/decorator/proc/check_condition(datum/ai_controller/controller) + return TRUE + +/** + * Proc called by the observer system when a watched key changes. + * Return TRUE if the decorator's condition would pass, FALSE otherwise. + */ +/datum/bt_node/decorator/proc/evaluate_for_observer(datum/ai_controller/controller) + return !!check_condition(controller) != invert + +/** + * Called by the controller's observer handler when a watched blackboard key changes. + * Re-evaluates evaluate_for_observer() and aborts based on observer_abort policy. + * + * BT_ABORT_SELF: condition became FALSE and we're running our children > cancel actions + * BT_ABORT_LOWER_PRIORITY: condition became TRUE and we're running lower priority nodes > cancel actions + */ +/// Called by the controller's polling loop for decorators that have no signal observers. +/// Sets a baseline on first call, then fires on_observed_change() only when the result changes. +/datum/bt_node/decorator/proc/poll_condition(datum/ai_controller/controller) + if(polling_rate && last_poll_time + polling_rate > world.time) + return + last_poll_time = world.time + var/current = evaluate_for_observer(controller) + if(last_poll_result == null) + last_poll_result = current + return + if(current != last_poll_result) + last_poll_result = current + on_observed_change(controller, null) + +/datum/bt_node/decorator/proc/on_observed_change(datum/ai_controller/controller, key) + var/condition_result = evaluate_for_observer(controller) + + if(!condition_result && (observer_abort & BT_ABORT_SELF)) + var/active = controller.active_execution_index + if(!execution_index || (active >= execution_index && active <= last_execution_index)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_DECISIONMAKING, "[controller.pawn] [type]: ABORT_SELF on key=[key] condition lost, replanning") + controller.cancel_current_plan() + + if(condition_result && (observer_abort & BT_ABORT_LOWER_PRIORITY)) + var/active = controller.active_execution_index + if(!execution_index || !active || active > last_execution_index) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_DECISIONMAKING, "[controller.pawn] [type]: ABORT_LOWER on key=[key] condition gained, replanning") + controller.cancel_current_plan() + +/datum/bt_node/decorator/reset_tick_state() + if(observers_registered) + unregister_observe_signals(owning_controller?.pawn) + if(is_polled) + LAZYREMOVE(owning_controller?.polling_observers, src) + is_polled = FALSE + observers_registered = FALSE + has_observer_signals = FALSE + last_poll_result = null + last_poll_time = 0 + child_active = FALSE + ..() + +/datum/bt_node/decorator/assign_execution_indices(counter) + execution_index = counter + counter++ + if(child) + counter = child.assign_execution_indices(counter) + last_execution_index = counter - 1 + return counter + +/// Override to register all signal observers for this decorator. Return TRUE if any were registered. If a decorator does not handle this and we have an observer_abort mode that isn't BT_ABORT_NONE, the system will fall back to ticking the condition every tick, which is less efficient but allows for reactivity without signals. +/datum/bt_node/decorator/proc/register_observe_signals(atom/pawn) + return FALSE + +/// Override to unregister all observers registered by register_observe_signals(). +/datum/bt_node/decorator/proc/unregister_observe_signals(atom/pawn) + return + +/// Shared signal handler. Calls on_observed_change() with owning_controller. +/datum/bt_node/decorator/proc/on_signal_changed(atom/source, ...) + SIGNAL_HANDLER + if(owning_controller) + on_observed_change(owning_controller, null) + + +/// Returns TRUE if the blackboard key holds a non-null, non-deleted value. +/datum/bt_node/decorator/proc/bb_key_exists(datum/ai_controller/controller, key) + return controller.blackboard_key_exists(key) + +/// Gates on whether the named override slot currently has an active override installed. +/// Observes COMSIG_AI_OVERRIDE_SLOT_CHANGED so it reacts immediately when a command is set or cleared. +/datum/bt_node/decorator/override_id_set + /// SUBPLAN_ID_* constant matching the override slot to watch. + var/override_id = null + +/datum/bt_node/decorator/override_id_set/check_condition(datum/ai_controller/controller) + var/datum/bt_node/subtree/potential_subtree = LAZYACCESS(controller.override_slots, override_id) + return !isnull(potential_subtree.override_node) + +/datum/bt_node/decorator/override_id_set/register_observe_signals(atom/pawn) + if(isnull(override_id)) + return FALSE + RegisterSignal(pawn, COMSIG_AI_OVERRIDE_SLOT_CHANGED(override_id), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/override_id_set/unregister_observe_signals(atom/pawn) + if(!isnull(override_id)) + UnregisterSignal(pawn, COMSIG_AI_OVERRIDE_SLOT_CHANGED(override_id)) + +/// Returns TRUE if the blackboard value at key equals the given value. +/datum/bt_node/decorator/proc/bb_key_equals(datum/ai_controller/controller, key, value) + return controller.blackboard[key] == value + +/// Returns TRUE if the blackboard value at key is strictly greater than threshold. +/datum/bt_node/decorator/proc/bb_key_greater(datum/ai_controller/controller, key, threshold) + return controller.blackboard[key] > threshold diff --git a/code/datums/ai/_ai_bt_node.dm b/code/datums/ai/_ai_bt_node.dm new file mode 100644 index 000000000000..fdff4feae20c --- /dev/null +++ b/code/datums/ai/_ai_bt_node.dm @@ -0,0 +1,109 @@ +///Base node for behavior tree nodes +/datum/bt_node + /// Node type identifier. + var/node_type = BT_NODE_LEAF + /// Pre-order depth-first index of this node in the tree. Assigned by finalize_tree(). + var/execution_index = 0 + /// Index of the last descendant node in this subtree. Equal to execution_index for leaves. + var/last_execution_index = 0 + /// Reference to this node's parent in the resolved tree. Set by ai_controller/finalize_tree(). + /// Null for root-level nodes. + var/datum/bt_node/parent_node = null + ///Owning controller for this node + var/datum/ai_controller/owning_controller = null + /// Short display label, set at New() by stripping standard path prefixes from the type. + var/label = "" + +/datum/bt_node/New() + . = ..() + if(!label) + var/t = "[type]" + t = replacetext(t, "/datum/bt_node/decorator/", "") + t = replacetext(t, "/datum/bt_node/ai_behavior/", "") + t = replacetext(t, "/datum/bt_node/subtree/", "") + label = t + +///Ticked by the ai_controller. Returns BT_SUCCESS, BT_FAILURE, or BT_RUNNING which can change how the parent responds. +/datum/bt_node/proc/tick(datum/ai_controller/controller, seconds_per_tick) + SHOULD_NOT_SLEEP(TRUE) + return BT_FAILURE + +/// Resets per-tick state for this node instance. Override in subtypes that hold tick state. +/datum/bt_node/proc/reset_tick_state() + return + +/// Resets this node and all of its descendants, cancelling any behaviors still running in the subtree. +/datum/bt_node/proc/reset_subtree_tick_states() + var/list/to_visit = list(src) + var/index = 1 + while(index <= length(to_visit)) + var/datum/bt_node/node = to_visit[index++] + node.reset_tick_state() + node.collect_reset_children(to_visit) + +/** + * Assigns pre-order depth-first execution indices to this node and its subtree. + * Called once per controller tree by finalize_tree(). + */ +/datum/bt_node/proc/assign_execution_indices(counter) + execution_index = counter + last_execution_index = counter + return counter + 1 + +/// Apply a configuration list to this node instance by assigning vars directly. +/datum/bt_node/proc/configure(list/config) + for(var/var_name in config) + vars[var_name] = config[var_name] + +/** + * Returns the list of direct child bt_node instances for tree traversal. + * Returns null for leaf nodes (default). Overridden in composites, decorators, and subtrees. + */ +/datum/bt_node/proc/get_children() + return null + +/// Returns TRUE if this node or any descendant has an active (running) ai_behavior leaf. +/datum/bt_node/proc/has_active_descendants() + return FALSE + +/// Walks descendants to find the node with the given execution_index. Returns null if not found. +/datum/bt_node/proc/find_by_index(target_index) + if(execution_index == target_index) + return src + var/list/ch = get_children() + if(!ch) + return null + for(var/datum/bt_node/child as anything in ch) + var/found = child.find_by_index(target_index) + if(found) + return found + return null + +/// Appends this node's active/upcoming state to lines for display. No-op for plain leaf nodes. +/datum/bt_node/proc/append_active_nodes(list/lines, indent) + return + +/// Called during finalize_tree() to set owning_controller, register overrides, and enqueue children. +/datum/bt_node/proc/finalize_node(datum/ai_controller/controller, list/to_visit) + owning_controller = controller + +/// Called during build_node_from_descriptor() to resolve and assign child nodes from JSON descriptors. +/datum/bt_node/proc/set_descriptor_children(list/children_descs, datum/ai_controller/controller) + return + +/// Returns a single-character status marker for display. Overridden by ai_behavior to check running. +/datum/bt_node/proc/get_status_marker() + return "o" + +/// Appends this node's full tree state (status + label + children) to lines for display. +/datum/bt_node/proc/append_full_tree_state(list/lines, indent) + lines += "[indent][get_status_marker()] [label]" + +/// Adds all children that must be visited during reset to to_visit. No-op for leaf nodes. +/datum/bt_node/proc/collect_reset_children(list/to_visit) + return + +/datum/bt_node/Destroy() + parent_node = null + owning_controller = null + return ..() diff --git a/code/datums/ai/_ai_bt_subtree.dm b/code/datums/ai/_ai_bt_subtree.dm new file mode 100644 index 000000000000..ea9874e78f75 --- /dev/null +++ b/code/datums/ai/_ai_bt_subtree.dm @@ -0,0 +1,88 @@ +/** + * Subtree node: a named, re-usable BT subgraph. + * + * Subtypes override New() to build an arbitrary internal tree and assign its root node + * to `root`. tick() delegates entirely to root.tick() and returns whatever it returns. + * + * This decouples the subtree's identity (its type path) from any specific composite + * semantics (selector/parallel/sequence). The root can be any node type. + * + * The controller's tree-walk helpers (setup_bt_observers, reset_bt_tick_states) descend + * through the `root` pointer to reach all internal nodes. + */ +/datum/bt_node/subtree + node_type = BT_NODE_SUBTREE + /// Repo-relative path to the .bt.json source file for this subtree (e.g. "code/datums/ai/bots/bot_patrol.bt.json"). + /// resolve_node_children() derives the compiled path from this and loads the tree at runtime. + var/behavior_tree_json = null + /// list of BT node descriptors defining this subtree's root. + /// resolve_node_children() builds `root` from this during tree construction. + var/list/behavior_nodes = null + /// The internal root node. Populated by resolve_node_children(). Do not set directly. + var/datum/bt_node/root = null + /// Set this to allow runtime overriding of this subtree, useful for things like pet commands! + var/override_id = null + /// Active override subtree. When set, tick() delegates to this node instead of root. + /// Set to null to deactivate the override. Managed by set_behavior_tree_override() only. + var/datum/bt_node/subtree/override_node = null + ///Any bindings this subtree has; assigned by the json + var/list/bindings = null + +/datum/bt_node/subtree/Destroy() + QDEL_NULL(root) + QDEL_NULL(override_node) + return ..() + +/datum/bt_node/subtree/tick(datum/ai_controller/controller, seconds_per_tick) + if(override_node) + return override_node.tick(controller, seconds_per_tick) + if(!root) + return BT_FAILURE + return root.tick(controller, seconds_per_tick) + +/datum/bt_node/subtree/get_children() + if(override_node) + return override_node.root ? list(override_node.root) : null + return root ? list(root) : null + +/datum/bt_node/subtree/has_active_descendants() + if(override_node) + return override_node.has_active_descendants() + return root && root.has_active_descendants() + +/datum/bt_node/subtree/finalize_node(datum/ai_controller/controller, list/to_visit) + ..() + if(!isnull(override_id)) + LAZYINITLIST(controller.override_slots) + controller.override_slots[override_id] = src + if(root) + root.parent_node = src + to_visit += root + if(override_node) + override_node.parent_node = src + to_visit += override_node + +/datum/bt_node/subtree/append_active_nodes(list/lines, indent) + if(root && root.has_active_descendants()) + root.append_active_nodes(lines, indent) + +/datum/bt_node/subtree/collect_reset_children(list/to_visit) + if(root) + to_visit += root + if(override_node) + to_visit += override_node + +/datum/bt_node/subtree/append_full_tree_state(list/lines, indent) + ..() + if(root) + root.append_full_tree_state(lines, "[indent] ") + +/datum/bt_node/subtree/assign_execution_indices(counter) + execution_index = counter + counter++ + if(root) + counter = root.assign_execution_indices(counter) + if(override_node) + counter = override_node.assign_execution_indices(counter) + last_execution_index = counter - 1 + return counter diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 520b2ae259a8..2c5740b7c406 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -15,35 +15,38 @@ multiple modular subtrees with behaviors * DO NOT set values in the blackboard directly, and especially not if you're adding a datum reference to this! * Use the setters, this is important for reference handing. */ - var/list/blackboard = list() + var/alist/blackboard = alist() ///Bitfield of traits for this AI to handle extra behavior var/ai_traits = DEFAULT_AI_FLAGS - ///Current actions planned to be performed by the AI in the upcoming plan - var/list/planned_behaviors = list() - ///Current actions being performed by the AI. - var/list/current_behaviors = list() - ///Current actions and their respective last time ran as an assoc list. - var/list/behavior_cooldowns = list() ///Current status of AI (OFF/ON) var/ai_status - ///Current movement target of the AI, generally set by decision making. - var/atom/current_movement_target - ///Identifier for what last touched our movement target, so it can be cleared conditionally - var/movement_target_source - ///Stored arguments for behaviors given during their initial creation - var/list/behavior_args = list() + ///Set by force_ai_off() when an outside system deliberately disables this AI. While TRUE, get_expected_ai_status() always returns AI_STATUS_OFF, so status recalculations (stat changes, z changes, client login/logout) cannot re-enable us. Cleared via clear_forced_off(). + var/forced_off = FALSE ///Tracks recent pathing attempts, if we fail too many in a row we fail our current plans. var/consecutive_pathing_attempts ///Can the AI remain in control if there is a client? var/continue_processing_when_client = FALSE ///distance to give up on target var/max_target_distance = 14 - ///All subtrees this AI has available, will run them in order, so make sure they're in the order you want them to run. On initialization of this type, it will start as a typepath(s) and get converted to references of ai_subtrees found in SSai_controllers when init_subtrees() is called - var/list/planning_subtrees - - ///The idle behavior this AI performs when it has no actions. - var/datum/idle_behavior/idle_behavior = null + /// Repo-relative path to the .bt.json source file for this controller (e.g. "code/datums/ai/basic_mobs/cleanbot.bt.json"). + /// initialize_behavior_tree() derives the compiled path from this and loads the BT tree at runtime. + var/behavior_tree_json = null + ///The root of our tree, which will contain + var/list/behavior_nodes + /// Execution index of the leaf node currently returning BT_RUNNING. 0 = nothing active. + var/active_execution_index = 0 + /// Set to TRUE by cancel_current_plan() when it fires mid-tick. Checked by composites to abort the current tick loop early, preventing running_child_index from being re-established after a reset. Cleared at the start of SelectBehaviors(). + var/cancelled_during_tick = FALSE + /// Draining log of all leaf execution indices that fired since the last bt_viewer poll. Null when no viewer is attached. + var/list/bt_execution_log = null + /// assoc list of override_id -> /datum/bt_node/subtree for runtime subtree replacement. + /// Populated by finalize_tree() when subtrees with override_id are found. Null until then. + var/list/override_slots = null + /// Decorators in polling mode (observer_abort set, no signal registered). Iterated after each SelectBehaviors tick so their condition is re-evaluated even when skipped by composite resume logic. + var/list/polling_observers = null + /// world.time of our last SelectBehaviors() tick from SSai_controllers. Used to derive the real seconds_per_tick under load; 0 means no tick since the last status change, so the first tick falls back to the subsystem wait. + var/last_bt_tick = 0 ///our current cell grid var/datum/cell_tracker/our_cells @@ -56,8 +59,6 @@ multiple modular subtrees with behaviors // The variables below are fucking stupid and should be put into the blackboard at some point. ///AI paused time var/paused_until = 0 - ///Can this AI idle? - var/can_idle = TRUE ///What distance should we be checking for interesting things when considering idling/deidling? Defaults to AI_DEFAULT_INTERESTING_DIST var/interesting_dist = AI_DEFAULT_INTERESTING_DIST /// TRUE if we're able to run, FALSE if we aren't @@ -65,18 +66,10 @@ multiple modular subtrees with behaviors /// Make sure you hook update_able_to_run() in setup_able_to_run() to whatever parameters changing that you added /// Otherwise we will not pay attention to them changing var/able_to_run = FALSE - /// are we even able to plan? - var/able_to_plan = TRUE - /// are we currently on failed planning timeout? - var/on_failed_planning_timeout = FALSE - /datum/ai_controller/New(atom/new_pawn) change_ai_movement_type(ai_movement) - init_subtrees() - - if(idle_behavior) - idle_behavior = SSidle_ai_behaviors.idle_behaviors[idle_behavior] + initialize_behavior_tree() if(!isnull(new_pawn)) // unit tests need the ai_controller to exist in isolation due to list schenanigans i hate it here PossessPawn(new_pawn) @@ -84,51 +77,232 @@ multiple modular subtrees with behaviors /datum/ai_controller/Destroy(force) UnpossessPawn(FALSE) if(ai_status) - GLOB.ai_controllers_by_status[ai_status] -= src + SSai_controllers.ai_controllers_by_status[ai_status] -= src for(var/datum/controller/subsystem/ai_controllers/controller_subsystem in Master.subsystems) if(controller_subsystem.planning_status == ai_status) controller_subsystem.currentrun -= src break our_cells = null - set_movement_target(type, null) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) + QDEL_LIST(behavior_nodes) return ..() -///Sets the current movement target, with an optional param to override the movement behavior -/datum/ai_controller/proc/set_movement_target(source, atom/target, datum/ai_movement/new_movement) - if(current_movement_target) - UnregisterSignal(current_movement_target, list(COMSIG_MOVABLE_MOVED, COMSIG_PREQDELETED)) - if(!isnull(target) && !isatom(target)) - stack_trace("[pawn]'s current movement target is not an atom, rather a [target.type]! Did you accidentally set it to a weakref?") - CancelActions() - return - movement_target_source = source - current_movement_target = target - if(!isnull(current_movement_target)) - RegisterSignal(current_movement_target, COMSIG_MOVABLE_MOVED, PROC_REF(on_movement_target_move)) - RegisterSignal(current_movement_target, COMSIG_PREQDELETED, PROC_REF(on_movement_target_delete)) - if(new_movement) - change_ai_movement_type(new_movement) - ///Overrides the current ai_movement of this controller with a new one /datum/ai_controller/proc/change_ai_movement_type(datum/ai_movement/new_movement) ai_movement = SSai_movement.movement_types[new_movement] -///Completely replaces the planning_subtrees with a new set based on argument provided, list provided must contain specifically typepaths -/datum/ai_controller/proc/replace_planning_subtrees(list/typepaths_of_new_subtrees) - planning_subtrees = typepaths_of_new_subtrees - init_subtrees() +///Completely replaces the behavior_nodes with a new set based on argument provided. +/datum/ai_controller/proc/replace_behavior_nodes(list/typepaths_of_new_subtrees) + var/list/old_nodes = behavior_nodes + behavior_nodes = typepaths_of_new_subtrees + initialize_behavior_tree() + QDEL_LIST(old_nodes) + +/// Resolves the children/child of a composite or decorator node, creating configured instances. +/// Safe to call on any node type; non-composite/non-decorator nodes are a no-op. +/datum/ai_controller/proc/resolve_node_children(datum/bt_node/node) + if(istype(node, /datum/bt_node/composite)) + var/datum/bt_node/composite/comp = node + if(!LAZYLEN(comp.children_typepaths) || LAZYLEN(comp.children)) + return + var/list/resolved_children = list() + for(var/child_type in comp.children_typepaths) + var/list/config = comp.children_typepaths[child_type] + var/datum/bt_node/child = resolve_child_node(child_type, config) + if(isnull(child)) + stack_trace("BT composite [node.type] references unknown child type [child_type]") + continue + resolved_children += child + comp.children = resolved_children + else if(istype(node, /datum/bt_node/decorator)) + var/datum/bt_node/decorator/dec = node + if(isnull(dec.child_typepath) || !isnull(dec.child)) + return + dec.child = resolve_child_node(dec.child_typepath, null) + if(isnull(dec.child)) + stack_trace("BT decorator [node.type] references unknown child type [dec.child_typepath]") + else if(istype(node, /datum/bt_node/subtree)) + var/datum/bt_node/subtree/sub = node + if(!isnull(sub.behavior_nodes) && isnull(sub.root)) + sub.root = build_node_from_descriptor(sub.behavior_nodes) + else if(!isnull(sub.behavior_tree_json) && isnull(sub.root)) + var/file = file(BT_COMPILED_PATH(sub.behavior_tree_json)) + var/list/raw_desc = json_decode(file2text(file)) + if(LAZYLEN(sub.bindings) || !isnull(raw_desc[BT_DESC_BINDINGS])) + raw_desc = apply_bindings_to_descriptor(raw_desc, sub.bindings) + sub.root = build_node_from_descriptor(raw_desc) + +// Always creates a fresh instance regardless of whether config is provided. +/datum/ai_controller/proc/resolve_child_node(child_type, list/config) + if(!ispath(child_type, /datum/bt_node)) + return null + var/datum/bt_node/child = new child_type + if(config) + child.configure(config) + resolve_node_children(child) + return child + +/datum/ai_controller/proc/get_or_build_node(entry) + if(ispath(entry)) + if(!ispath(entry, /datum/bt_node)) + stack_trace("get_or_build_node() received non-BT typepath: [entry]") + return null + var/datum/bt_node/node = new entry + resolve_node_children(node) + return node + if(islist(entry)) + return build_node_from_descriptor(entry) + stack_trace("get_or_build_node() received unexpected entry type: [entry]") + return null + +///Loads and decodes a compiled BT JSON file into a node tree. +/datum/ai_controller/proc/load_tree_from_json(path) + var/file = file(path) + var/list/desc = json_decode(file2text(file)) + return build_node_from_descriptor(desc) -///Loops over the subtrees in planning_subtrees and looks at the ai_controllers to grab a reference, ENSURE planning_subtrees ARE TYPEPATHS AND NOT INSTANCES/REFERENCES BEFORE EXECUTING THIS -/datum/ai_controller/proc/init_subtrees() - if(!LAZYLEN(planning_subtrees)) +/** + * Merges call-site binding overrides with the subtree's declared defaults, + * then substitutes all $name placeholders in the descriptor tree. + * Returns a new descriptor with BT_DESC_BINDINGS stripped and placeholders resolved. + */ +/datum/ai_controller/proc/apply_bindings_to_descriptor(list/desc, list/call_site_bindings) + var/list/merged = list() + var/list/declared = desc[BT_DESC_BINDINGS] + for(var/name in declared) + merged[name] = declared[name]["default"] + for(var/name in call_site_bindings) + merged[name] = call_site_bindings[name] + return _substitute_bindings(desc, merged) + +/// Recursively walks a descriptor list, replacing "$name" strings with their bound values. +/datum/ai_controller/proc/_substitute_bindings(list/desc, list/merged) + var/list/out = list() + for(var/key in desc) + if(key == BT_DESC_BINDINGS) + continue + var/value = desc[key] + if(islist(value)) + out[key] = _substitute_bindings_in_list(value, merged) + else if(istext(value) && copytext(value, 1, 2) == "$") + var/binding_name = copytext(value, 2) + out[key] = isnull(merged[binding_name]) ? value : merged[binding_name] + else + out[key] = value + return out + +/// Substitutes bindings inside a descriptor value list, preserving assoc entries +/datum/ai_controller/proc/_substitute_bindings_in_list(list/input, list/merged) + var/list/resolved_list = list() + for(var/item in input) + var/assoc_value = isnum(item) ? null : input[item] + if(!isnull(assoc_value)) + if(islist(assoc_value)) + resolved_list[item] = _substitute_bindings_in_list(assoc_value, merged) //recursion baby + else if(istext(assoc_value) && copytext(assoc_value, 1, 2) == "$") + var/binding_name = copytext(assoc_value, 2) + resolved_list[item] = isnull(merged[binding_name]) ? assoc_value : merged[binding_name] + else + resolved_list[item] = assoc_value + else if(islist(item)) + resolved_list += list(_substitute_bindings(item, merged)) + else if(istext(item) && copytext(item, 1, 2) == "$") + var/binding_name = copytext(item, 2) + var/resolved = isnull(merged[binding_name]) ? item : merged[binding_name] + if(islist(resolved)) + resolved_list += list(resolved) + else + resolved_list += resolved + else + resolved_list += item + return resolved_list + +/** + * Recursively builds a BT node tree from a descriptor list. + * BT_DESC_TYPE and BT_DESC_CHILDREN are consumed internally; all other keys are written + * as vars onto the node. String values starting with "/" are resolved via text2path so + * typepath args (e.g. "/datum/ai_movement/basic_avoidance") arrive as actual types. + * If you put / in a string then yeah that might cause issues, should probably fix that later! + */ +/datum/ai_controller/proc/build_node_from_descriptor(list/desc) + var/raw_type = desc[BT_DESC_TYPE] + if(!raw_type) // This can happen if we have an overriden type with no binding. (e.g. subtrees not being overriden and default to null) + return null + var/node_type = ispath(raw_type) ? raw_type : text2path(raw_type) + if(isnull(node_type)) + stack_trace("build_node_from_descriptor(): unknown typepath '[raw_type]'") + return null + var/datum/bt_node/node = new node_type + for(var/key in desc) + if(key == BT_DESC_TYPE || key == BT_DESC_CHILDREN || key == BT_DESC_BINDINGS) + continue + var/value = desc[key] + if(islist(value)) + var/list/resolved = value + for(var/i in 1 to length(resolved)) + if(istext(resolved[i])) + var/as_path = text2path(resolved[i]) + if(!isnull(as_path)) + resolved[i] = as_path + else if(istext(value)) + var/as_path = text2path(value) + if(!isnull(as_path)) + value = as_path + node.vars[key] = value + resolve_node_children(node) + var/list/children_descs = desc[BT_DESC_CHILDREN] + if(LAZYLEN(children_descs)) + node.set_descriptor_children(children_descs, src) + return node + +/// Builds the per-controller BT node tree from behavior_nodes typepaths or descriptors, then finalizes it. +/datum/ai_controller/proc/initialize_behavior_tree() + if(!isnull(behavior_tree_json) && !LAZYLEN(behavior_nodes)) + var/compiled_path = BT_COMPILED_PATH(behavior_tree_json) //Find the compiled version of this BT + var/datum/bt_node/root = load_tree_from_json(compiled_path) + if(isnull(root)) + stack_trace("[type] failed to load behavior tree from compiled JSON: [compiled_path]") + return + behavior_nodes = list(root) + finalize_tree() + return + if(!LAZYLEN(behavior_nodes)) return var/list/temp_subtree_list = list() - for(var/subtree in planning_subtrees) - var/subtree_instance = GLOB.ai_subtrees[subtree] - temp_subtree_list += subtree_instance - planning_subtrees = temp_subtree_list + if(!isnull(behavior_nodes[BT_DESC_TYPE])) + var/datum/bt_node/node_instance = get_or_build_node(behavior_nodes) + if(isnull(node_instance)) + stack_trace("[type]'s behavior_nodes BT descriptor could not be built") + else + temp_subtree_list += node_instance + else + for(var/entry in behavior_nodes) + var/datum/bt_node/node_instance = get_or_build_node(entry) + if(isnull(node_instance)) + stack_trace("[type]'s behavior_nodes contains unknown entry: [entry]") + continue + temp_subtree_list += node_instance + behavior_nodes = temp_subtree_list + finalize_tree() + +/// Walks the resolved tree to set owning_controller and parent_node on all nodes, populates +/// override_slots, and assigns pre-order execution indices. Called after initialize_behavior_tree() and +/// after set_behavior_tree_override() installs or removes an override node. +/datum/ai_controller/proc/finalize_tree() + if(!LAZYLEN(behavior_nodes)) + return + override_slots = null + var/list/to_visit = behavior_nodes.Copy() + for(var/datum/bt_node/root in behavior_nodes) + root.parent_node = null + var/index = 1 + while(index <= length(to_visit)) //while loop so we can recursively keep populating this list + var/datum/bt_node/node = to_visit[index++] + node.finalize_node(src, to_visit) + var/counter = 1 + for(var/datum/bt_node/root in behavior_nodes) + counter = root.assign_execution_indices(counter) ///Proc to move from one pawn to another, this will destroy the target's existing controller. /datum/ai_controller/proc/PossessPawn(atom/new_pawn) @@ -145,12 +319,14 @@ multiple modular subtrees with behaviors pawn = new_pawn pawn.ai_controller = src + set_blackboard_key(BB_MY_PAWN, pawn, FALSE) //Don't track the datum we already handle qdel of pawn here. var/turf/pawn_turf = get_turf(pawn) if(pawn_turf) - GLOB.ai_controllers_by_zlevel[pawn_turf.z] += src + SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] += src SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_POSSESSED_PAWN) + SEND_SIGNAL(pawn, COMSIG_PAWN_POSSESSED_BY_AI_CONTROLLER, src) reset_ai_status() RegisterSignal(pawn, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_changed_z_level)) @@ -159,32 +335,19 @@ multiple modular subtrees with behaviors RegisterSignal(pawn, COMSIG_QDELETING, PROC_REF(on_pawn_qdeleted)) RegisterSignal(pawn, COMSIG_EVLOGGING_ENABLED, PROC_REF(on_pawn_evlogging_enabled)) RegisterSignal(pawn, COMSIG_EVLOGGING_DISABLED, PROC_REF(on_pawn_evlogging_disabled)) - update_able_to_run() - setup_able_to_run() our_cells = new(interesting_dist, interesting_dist, 1) set_new_cells() + update_able_to_run() + setup_able_to_run() + RegisterSignal(pawn, COMSIG_MOVABLE_MOVED, PROC_REF(update_grid)) /datum/ai_controller/proc/update_grid(datum/source, datum/spatial_grid_cell/new_cell) SIGNAL_HANDLER set_new_cells() - if(current_movement_target) - check_target_max_distance() - -/datum/ai_controller/proc/on_movement_target_move(atom/source) - SIGNAL_HANDLER - check_target_max_distance() - -/datum/ai_controller/proc/on_movement_target_delete(atom/source) - SIGNAL_HANDLER - set_movement_target(source = type, target = null) - -/datum/ai_controller/proc/check_target_max_distance() - if(get_dist(current_movement_target, pawn) > max_target_distance) - CancelActions() /datum/ai_controller/proc/set_new_cells() if(isnull(our_cells)) @@ -206,14 +369,16 @@ multiple modular subtrees with behaviors recalculate_idle() -/datum/ai_controller/proc/should_idle() - if(!can_idle || isnull(our_cells)) +///Returns TRUE if a living mob with a client is in one of our tracked spatial grid cells +/datum/ai_controller/proc/has_nearby_client() + if(isnull(our_cells)) return FALSE for(var/datum/spatial_grid_cell/grid as anything in our_cells.member_cells) if(locate(/mob/living) in grid.client_contents) - return FALSE - return TRUE + return TRUE + return FALSE +///Check if mob should go into idle/low-priority (from spatial cells) /datum/ai_controller/proc/recalculate_idle(datum/exited) if(ai_status == AI_STATUS_OFF) return @@ -229,17 +394,18 @@ multiple modular subtrees with behaviors if(distance <= interesting_dist) //is our target in between interesting cells? return - if(should_idle()) - set_ai_status(AI_STATUS_IDLE) + reset_ai_status() /datum/ai_controller/proc/on_client_enter(datum/source, list/target_list) SIGNAL_HANDLER + if(ai_status == AI_STATUS_ON) + return + if (!(locate(/mob/living) in target_list)) return - if(ai_status == AI_STATUS_IDLE) - set_ai_status(AI_STATUS_ON) + reset_ai_status() /datum/ai_controller/proc/on_client_exit(datum/source, datum/exited) SIGNAL_HANDLER @@ -250,12 +416,37 @@ multiple modular subtrees with behaviors /datum/ai_controller/proc/reset_ai_status() set_ai_status(get_expected_ai_status()) +/** + * Deliberately disables this AI until clear_forced_off() is called. + * Unlike a bare set_ai_status(AI_STATUS_OFF), this survives status recalculations + * from stat changes, z-level changes, client login/logout and the like. + */ +/datum/ai_controller/proc/force_ai_off(additional_flags = NONE) + forced_off = TRUE + set_ai_status(AI_STATUS_OFF, additional_flags) + +/// Undoes force_ai_off() and recalculates what status we should be in. +/datum/ai_controller/proc/clear_forced_off() + forced_off = FALSE + reset_ai_status() + /** * Gets the AI status we expect the AI controller to be on at this current moment. - * Returns AI_STATUS_OFF if it's inhabited by a Client and shouldn't be, if it's dead and cannot act while dead, or is on a z level without clients. - * Returns AI_STATUS_ON otherwise. + * Returns AI_STATUS_OFF if it has been forced off, is inhabited by a Client and shouldn't be, is dead and cannot act while dead, + * or is sleeping for performance (off-station with no nearby client and not flagged RUN_WHILE_UNWATCHED; client arrival wakes these automatically). + * Otherwise returns AI_STATUS_ON or AI_STATUS_ON_LOW, see get_active_ai_status(). */ /datum/ai_controller/proc/get_expected_ai_status() + + +/* +#ifdef AI_PERFORMANCE_TESTING + return AI_STATUS_ON +#endif +*/ + if (forced_off) + return AI_STATUS_OFF + if (isnull(get_turf(pawn))) return AI_STATUS_OFF @@ -271,16 +462,35 @@ multiple modular subtrees with behaviors return AI_STATUS_ON return AI_STATUS_OFF - var/turf/pawn_turf = get_turf(mob_pawn) #ifdef TESTING + var/turf/pawn_turf = get_turf(mob_pawn) if(!pawn_turf) CRASH("AI controller [src] controlling pawn ([pawn]) is not on a turf.") #endif - if(!length(SSmobs.clients_by_zlevel[pawn_turf.z]) || on_failed_planning_timeout || !able_to_run) + if(!able_to_run) return AI_STATUS_OFF - if(should_idle()) - return AI_STATUS_IDLE - return AI_STATUS_ON + return get_active_ai_status() + +/** + * Classifies an active AI controller into a priority tier. + * Returns AI_STATUS_ON for controllers on station/shuttle territory, with a nearby client, + * Returns AI_STATUS_ON_LOW for other active controllers unless they have ALWAYS_HIGH_PRIORITY. + * else its AI_STATUS_OFF + */ +/datum/ai_controller/proc/get_active_ai_status() + var/turf/pawn_turf = get_turf(pawn) + var/area/pawn_area = pawn_turf ? get_area(pawn_turf) : null + // AI actually standing on the station or a shuttle always stays high priority + if(istype(pawn_area, /area/station) || istype(pawn_area, /area/shuttle)) + return AI_STATUS_ON + if(has_nearby_client()) + return AI_STATUS_ON + if(ai_traits & RUN_WHILE_UNWATCHED) + if(ai_traits & ALWAYS_HIGH_PRIORITY) + return AI_STATUS_ON + else + return AI_STATUS_ON_LOW + return AI_STATUS_OFF ///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is. /datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents) @@ -290,10 +500,10 @@ multiple modular subtrees with behaviors if((mob_pawn?.client && !continue_processing_when_client)) return if(old_turf) - GLOB.ai_controllers_by_zlevel[old_turf.z] -= src + SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src if(isnull(new_turf)) return - GLOB.ai_controllers_by_zlevel[new_turf.z] += src + SSai_controllers.ai_controllers_by_zlevel[new_turf.z] += src reset_ai_status() ///Abstract proc for initializing the pawn to the new controller @@ -307,6 +517,7 @@ multiple modular subtrees with behaviors return // instantiated without an applicable pawn, fine SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_UNPOSSESSED_PAWN) + reset_bt_tick_states() set_ai_status(AI_STATUS_OFF) UnregisterSignal(pawn, list(COMSIG_MOVABLE_Z_CHANGED, COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING, COMSIG_EVLOGGING_ENABLED)) clear_able_to_run() @@ -314,13 +525,54 @@ multiple modular subtrees with behaviors ai_movement.stop_moving_towards(src) var/turf/pawn_turf = get_turf(pawn) if(pawn_turf) - GLOB.ai_controllers_by_zlevel[pawn_turf.z] -= src - remove_from_unplanned_controllers() + SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] -= src pawn.ai_controller = null pawn = null if(destroy) qdel(src) +///Call reset tick state on every node in the tree. +/datum/ai_controller/proc/reset_bt_tick_states() + if(!LAZYLEN(behavior_nodes)) + return + var/list/to_visit = behavior_nodes.Copy() + var/index = 1 + while(index <= length(to_visit)) + var/datum/bt_node/node = to_visit[index++] + node.reset_tick_state() + node.collect_reset_children(to_visit) + +/** + * Installs or removes a runtime override on the subtree slot registered with the given id. + * + * id - The ID for this slot + * override_subtree - actual subtree we're setting + */ +/datum/ai_controller/proc/set_behavior_tree_override(id, override_subtree) + var/datum/bt_node/subtree/slot = LAZYACCESS(override_slots, id) + if(isnull(slot)) + return + + var/current_type = isnull(slot.override_node) ? null : slot.override_node.type + if(current_type == override_subtree) + return + + if(slot.override_node) + slot.override_node.reset_tick_state() + QDEL_NULL(slot.override_node) + + if(isnull(override_subtree)) + finalize_tree() + SEND_SIGNAL(pawn, COMSIG_AI_OVERRIDE_SLOT_CHANGED(id), null) + return + + var/datum/bt_node/subtree/new_node = new override_subtree + resolve_node_children(new_node) + slot.override_node = new_node + finalize_tree() + cancel_current_plan() // Reset, not ideal; Maybe later on we can do this more gracefully. + SEND_SIGNAL(pawn, COMSIG_AI_OVERRIDE_SLOT_CHANGED(id), override_subtree) + /datum/ai_controller/proc/setup_able_to_run() // paused_until is handled by PauseAi() manually RegisterSignals(pawn, list(SIGNAL_ADDTRAIT(TRAIT_AI_PAUSED), SIGNAL_REMOVETRAIT(TRAIT_AI_PAUSED)), PROC_REF(update_able_to_run)) @@ -333,7 +585,7 @@ multiple modular subtrees with behaviors var/run_flags = get_able_to_run() if(run_flags & AI_UNABLE_TO_RUN) able_to_run = FALSE - GLOB.move_manager.stop_looping(pawn) //stop moving + ai_movement.fail_movement(src) else able_to_run = TRUE set_ai_status(get_expected_ai_status(), run_flags) @@ -347,19 +599,16 @@ multiple modular subtrees with behaviors return NONE ///Can this pawn interact with objects? -/datum/ai_controller/proc/ai_can_interact() - SHOULD_CALL_PARENT(TRUE) - return !QDELETED(pawn) +/datum/ai_controller/proc/ai_can_interact(atom/target) + return !QDELETED(pawn) && !QDELETED(target) ///Interact with objects /datum/ai_controller/proc/ai_interact(target, combat_mode, list/modifiers) - if(!ai_can_interact()) - return FALSE - var/atom/final_target = isdatum(target) ? target : blackboard[target] //incase we got a blackboard key instead - if(QDELETED(final_target)) + if(!ai_can_interact(final_target)) return FALSE + var/params = list2params(modifiers) var/mob/living/living_pawn = pawn if(isnull(combat_mode)) @@ -372,217 +621,43 @@ multiple modular subtrees with behaviors living_pawn.set_combat_mode(old_combat_mode) return TRUE -///Runs any actions that are currently running -/datum/ai_controller/process(seconds_per_tick) - - for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) - - // Convert the current behaviour action cooldown to realtime seconds from deciseconds.current_behavior - // Then pick the max of this and the seconds_per_tick passed to ai_controller.process() - // Action cooldowns cannot happen faster than seconds_per_tick, so seconds_per_tick should be the value used in this scenario. - var/action_seconds_per_tick = max(current_behavior.get_cooldown(src) * 0.1, seconds_per_tick) - - if(!(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT)) - if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown - continue - ProcessBehavior(action_seconds_per_tick, current_behavior) - return - - if(isnull(current_movement_target)) - fail_behavior(current_behavior) - return - - ///Stops pawns from performing such actions that should require the target to be adjacent. - var/atom/movable/moving_pawn = pawn - var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || current_movement_target.IsReachableBy(moving_pawn) - if(can_reach && current_behavior.required_distance >= get_dist(moving_pawn, current_movement_target)) ///Are we close enough to engage? - if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop. - ai_movement.stop_moving_towards(src) - - if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown - continue - ProcessBehavior(action_seconds_per_tick, current_behavior) - return - - if(ai_movement.moving_controllers[src] != current_movement_target) //We're too far, if we're not already moving start doing it. - ai_movement.start_moving_towards(src, current_movement_target, current_behavior.required_distance) //Then start moving - - if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //If we can move and perform then do so. - if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown - continue - ProcessBehavior(action_seconds_per_tick, current_behavior) - return ///This is where you decide what actions are taken by the AI. /datum/ai_controller/proc/SelectBehaviors(seconds_per_tick) - SHOULD_NOT_SLEEP(TRUE) //Fuck you don't sleep in procs like this. - planned_behaviors.Cut() - - for(var/datum/ai_planning_subtree/subtree as anything in planning_subtrees) - if(subtree.SelectBehaviors(src, seconds_per_tick) == SUBTREE_RETURN_FINISH_PLANNING) + SHOULD_NOT_SLEEP(TRUE) + cancelled_during_tick = FALSE + if(LAZYLEN(polling_observers)) + for(var/datum/bt_node/decorator/dec as anything in polling_observers.Copy()) + dec.poll_condition(src) + for(var/datum/bt_node/node as anything in behavior_nodes) + if(node.tick(src, seconds_per_tick) == BT_RUNNING) break - SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS, current_behaviors, planned_behaviors) - for(var/datum/ai_behavior/forgotten_behavior as anything in current_behaviors - planned_behaviors) - var/list/arguments = list(src, FALSE) - var/list/stored_arguments = behavior_args[type] - if(stored_arguments) - arguments += stored_arguments - forgotten_behavior.finish_action(arglist(arguments)) - - if(IS_EVLOGGING) - var/list/event_text = list() - event_text += "New plan starting!" - - for(var/datum/ai_behavior/behavior in planned_behaviors) - event_text += "Queued behavior [behavior.type]" - - EVLOG_TEXT(src, EVLOG_CATEGORY_AI_DECISIONMAKING, jointext(event_text, "\n")) - -///This proc handles changing ai status, and starts/stops processing if required. +///This proc handles changing ai status and updates the planning subsystem list. /datum/ai_controller/proc/set_ai_status(new_ai_status, additional_flags = NONE) if(ai_status == new_ai_status) return FALSE //no change //remove old status, if we've got one if(ai_status) - GLOB.ai_controllers_by_status[ai_status] -= src + SSai_controllers.ai_controllers_by_status[ai_status] -= src for(var/datum/controller/subsystem/ai_controllers/controller_subsystem in Master.subsystems) if(controller_subsystem.planning_status == ai_status) controller_subsystem.currentrun -= src break - remove_from_unplanned_controllers() - stop_previous_processing() ai_status = new_ai_status - GLOB.ai_controllers_by_status[new_ai_status] += src + last_bt_tick = 0 // don't count time spent in the previous status towards the next tick's seconds_per_tick + SSai_controllers.ai_controllers_by_status[new_ai_status] += src if(ai_status == AI_STATUS_OFF) if(!(additional_flags & AI_PREVENT_CANCEL_ACTIONS)) - CancelActions() - return - if(!length(current_behaviors)) - add_to_unplanned_controllers() - return - start_ai_processing() - -/datum/ai_controller/proc/start_ai_processing() - switch(ai_status) - if(AI_STATUS_ON) - START_PROCESSING(SSai_behaviors, src) - if(AI_STATUS_IDLE) - START_PROCESSING(SSidle_ai_behaviors, src) - -/datum/ai_controller/proc/stop_previous_processing() - switch(ai_status) - if(AI_STATUS_ON) - STOP_PROCESSING(SSai_behaviors, src) - if(AI_STATUS_IDLE) - STOP_PROCESSING(SSidle_ai_behaviors, src) - -/datum/ai_controller/proc/PauseAi(time) - paused_until = world.time + time - update_able_to_run() - addtimer(CALLBACK(src, PROC_REF(update_able_to_run)), time) - -/datum/ai_controller/proc/add_to_unplanned_controllers() - if(isnull(ai_status) || ai_status == AI_STATUS_OFF || isnull(idle_behavior)) - return - GLOB.unplanned_controllers[ai_status][src] = TRUE - -/datum/ai_controller/proc/remove_from_unplanned_controllers() - if(isnull(ai_status) || ai_status == AI_STATUS_OFF) - return - GLOB.unplanned_controllers[ai_status] -= src - for(var/datum/controller/subsystem/unplanned_controllers/potential_holder as anything in GLOB.unplanned_controller_subsystems) - if(potential_holder.target_status == ai_status) - potential_holder.current_run -= src - -/datum/ai_controller/proc/modify_cooldown(datum/ai_behavior/behavior, new_cooldown) - behavior_cooldowns[behavior] = new_cooldown - -///Call this to add a behavior to the stack. -/datum/ai_controller/proc/queue_behavior(behavior_type, ...) - var/datum/ai_behavior/behavior = GET_AI_BEHAVIOR(behavior_type) - if(!behavior) - CRASH("Behavior [behavior_type] not found.") - var/list/arguments = args.Copy() - arguments[1] = src - - if(current_behaviors[behavior]) ///It's still in the plan, don't add it again to current_behaviors but do keep it in the planned behavior list so its not cancelled - planned_behaviors[behavior] = TRUE - return - - if(!behavior.setup(arglist(arguments))) - return - - var/should_exit_unplanned = !length(current_behaviors) - planned_behaviors[behavior] = TRUE - current_behaviors[behavior] = TRUE - - arguments.Cut(1, 2) - if(length(arguments)) - behavior_args[behavior_type] = arguments - else - behavior_args -= behavior_type + cancel_current_plan() - if(!(behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //this one blocks planning! - able_to_plan = FALSE - if(should_exit_unplanned) - exit_unplanned_mode() - SEND_SIGNAL(src, AI_CONTROLLER_BEHAVIOR_QUEUED(behavior_type), arguments) - -/datum/ai_controller/proc/check_able_to_plan() - for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) - if(!(current_behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //We have a behavior that blocks planning - return FALSE - return TRUE - -/datum/ai_controller/proc/dequeue_behavior(datum/ai_behavior/behavior) - current_behaviors -= behavior - able_to_plan = check_able_to_plan() - if(!length(current_behaviors)) - enter_unplanned_mode() - -/datum/ai_controller/proc/exit_unplanned_mode() - remove_from_unplanned_controllers() - start_ai_processing() - -/datum/ai_controller/proc/enter_unplanned_mode() - add_to_unplanned_controllers() - stop_previous_processing() - -/datum/ai_controller/proc/ProcessBehavior(seconds_per_tick, datum/ai_behavior/behavior) - var/list/arguments = list(seconds_per_tick, src) - var/list/stored_arguments = behavior_args[behavior.type] - if(stored_arguments) - arguments += stored_arguments - - var/process_flags = behavior.perform(arglist(arguments)) - if(process_flags & AI_BEHAVIOR_DELAY) - behavior_cooldowns[behavior] = world.time + behavior.get_cooldown(src) - if(process_flags & AI_BEHAVIOR_FAILED) - arguments[1] = src - arguments[2] = FALSE - behavior.finish_action(arglist(arguments)) - else if (process_flags & AI_BEHAVIOR_SUCCEEDED) - arguments[1] = src - arguments[2] = TRUE - behavior.finish_action(arglist(arguments)) - -/datum/ai_controller/proc/CancelActions() - if(!length(current_behaviors)) - return - for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) - fail_behavior(current_behavior) - EVLOG_TEXT(src, EVLOG_CATEGORY_AI_DECISIONMAKING, "Actions were cancelled!") - -/datum/ai_controller/proc/fail_behavior(datum/ai_behavior/current_behavior) - var/list/arguments = list(src, FALSE) - var/list/stored_arguments = behavior_args[current_behavior.type] - if(stored_arguments) - arguments += stored_arguments - current_behavior.finish_action(arglist(arguments)) +/datum/ai_controller/proc/cancel_current_plan() + active_execution_index = 0 + cancelled_during_tick = TRUE + reset_bt_tick_states() /// Turn the controller on or off based on if you're alive, we only register to this if the flag is present so don't need to check again /datum/ai_controller/proc/on_stat_changed(mob/living/source, new_stat) @@ -600,14 +675,14 @@ multiple modular subtrees with behaviors /datum/ai_controller/proc/on_sentience_lost() SIGNAL_HANDLER UnregisterSignal(pawn, COMSIG_MOB_LOGOUT) - set_ai_status(AI_STATUS_IDLE) //Can't do anything while player is connected + reset_ai_status() //resume AI control now that the client is gone RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) // Turn the controller off if the pawn has been qdeleted -/datum/ai_controller/proc/on_pawn_qdeleted() +/datum/ai_controller/proc/on_pawn_qdeleted(datum/source) SIGNAL_HANDLER + sig_remove_from_blackboard(source) set_ai_status(AI_STATUS_OFF) - set_movement_target(type, null) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) @@ -618,25 +693,19 @@ multiple modular subtrees with behaviors var/mob/living/living_pawn = pawn return living_pawn.get_access() -///Returns the minimum required distance to preform one of our current behaviors. Honestly this should just be cached or something but fuck you -/datum/ai_controller/proc/get_minimum_distance() - var/minimum_distance = max_target_distance - // right now I'm just taking the shortest minimum distance of our current behaviors, at some point in the future - // we should let whatever sets the current_movement_target also set the min distance and max path length - // (or at least cache it on the controller) - for(var/datum/ai_behavior/iter_behavior as anything in current_behaviors) - if(iter_behavior.required_distance < minimum_distance) - minimum_distance = iter_behavior.required_distance - return minimum_distance - -/datum/ai_controller/proc/planning_failed() - on_failed_planning_timeout = TRUE - set_ai_status(get_expected_ai_status()) - addtimer(CALLBACK(src, PROC_REF(resume_planning)), AI_FAILED_PLANNING_COOLDOWN) +/// Returns TRUE if the pawn can path to the target. minimum_distance is how close the path must get (0 = onto/adjacent to the target's turf); searches pass it from their own acquire_target leaf. +/datum/ai_controller/proc/can_reach_target(atom/target, distance = 10, minimum_distance = 0) + if(!isdatum(target)) //we dont need to check if its not a datum! + return TRUE + if(get_turf(pawn) == get_turf(target)) + return TRUE + var/list/path = get_path_to(pawn, target, simulated_only = !HAS_TRAIT(pawn, TRAIT_SPACEWALK), mintargetdist = minimum_distance, max_distance = distance, access = get_access()) + return (!!length(path)) -/datum/ai_controller/proc/resume_planning() - on_failed_planning_timeout = FALSE - set_ai_status(get_expected_ai_status()) + +/// Called when a target was found but couldn't be reached. Base no-op; override to record the target (e.g. add it to an ignore list). +/datum/ai_controller/proc/note_unreachable_target(atom/target) + return /// Returns true if we have a blackboard key with the provided key and it is not qdeleting /datum/ai_controller/proc/blackboard_key_exists(key) @@ -697,8 +766,9 @@ multiple modular subtrees with behaviors * * * key - A blackboard key * * thing - a value to set the blackboard key to. + * * track_datum - whether we should track this ref for deletion, this should always be TRUE unless you really know wtf you're doing */ -/datum/ai_controller/proc/set_blackboard_key(key, thing) +/datum/ai_controller/proc/set_blackboard_key(key, thing, track_datum = TRUE) // Assume it is an error when trying to set a value overtop a list if(islist(blackboard[key])) CRASH("set_blackboard_key attempting to set a blackboard value to key [key] when it's a list!") @@ -710,7 +780,8 @@ multiple modular subtrees with behaviors if(!isnull(blackboard[key])) clear_blackboard_key(key) - TRACK_AI_DATUM_TARGET(thing, key) + if(track_datum) + TRACK_AI_DATUM_TARGET(thing, key) blackboard[key] = thing post_blackboard_key_set(key) @@ -828,6 +899,7 @@ multiple modular subtrees with behaviors LAZYINITLIST(blackboard[key]) TRACK_AI_DATUM_TARGET(thing, key) blackboard[key] |= thing + post_blackboard_key_set(key) /** * Adds the value to the inner list at key with the inner key set to "thing" @@ -875,7 +947,7 @@ multiple modular subtrees with behaviors blackboard[key] = null if(isnull(pawn)) return - SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)) + SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(key), key) /** * Remove the passed thing from the associated blackboard key @@ -983,19 +1055,31 @@ multiple modular subtrees with behaviors . = ..() UnregisterSignal(src, COMSIG_EVLOG_EVENT_ADDED) + /// Called whenever an event is logged for this controller. Attaches a snapshot of current behaviors and blackboard state to the event via track_info. /datum/ai_controller/proc/on_evlog_event_added(datum/source, datum/event_logger_track/track, list/event_data) SIGNAL_HANDLER var/list/track_info = list() - var/list/current_behavior_info = list() - for(var/datum/ai_behavior/behavior in planned_behaviors) - if(behavior in current_behaviors) //Not really ideal; we should find a better way to do this. - current_behavior_info += "ACTIVE: [span_bold("[behavior.type]")]" - else - current_behavior_info += "[behavior.type]" - EVLOG_TRACK_INFO_ENTRY(track_info, "Behaviors", "Current Behavior", jointext(current_behavior_info, "\n")) + // Build full tree state view showing all nodes with status markers + var/list/tree_lines = list() + for(var/datum/bt_node/root_node as anything in behavior_nodes) + root_node.append_full_tree_state(tree_lines, "") + EVLOG_TRACK_INFO_ENTRY(track_info, "Behaviors", "Full Tree State", length(tree_lines) ? jointext(tree_lines, "\n") : "(none)") + + // Add execution context section + var/active_node_label = "(none)" + if(active_execution_index) + for(var/datum/bt_node/root_node as anything in behavior_nodes) + var/datum/bt_node/found = root_node.find_by_index(active_execution_index) + if(found) + active_node_label = found.label + break + EVLOG_TRACK_INFO_ENTRY(track_info, "Execution Context", "Active Execution Index", "[active_execution_index] ([active_node_label])") + EVLOG_TRACK_INFO_ENTRY(track_info, "Execution Context", "AI Status", ai_status) + EVLOG_TRACK_INFO_ENTRY(track_info, "Execution Context", "Able to Run", able_to_run ? "TRUE" : "FALSE") + // Blackboard snapshot for(var/blackboard_key_name, blackboard_value in blackboard) var/value_string if(isatom(blackboard_value)) diff --git a/code/datums/ai/_ai_planning_subtree.dm b/code/datums/ai/_ai_planning_subtree.dm deleted file mode 100644 index b3f0ae9fb533..000000000000 --- a/code/datums/ai/_ai_planning_subtree.dm +++ /dev/null @@ -1,10 +0,0 @@ -///A subtree is attached to a controller and is occasionally called by /ai_controller/SelectBehaviors(), this mainly exists to act as a way to subtype and modify SelectBehaviors() without needing to subtype the ai controller itself -/datum/ai_planning_subtree - /// A list of typepaths of "operational datums" (elements/components) we absolutely NEED to run. Checked in unit tests, as well as be a nice reminder to developers that such a thing might be needed. - /// Note that in the Attach/Inititalize/New (or any future equivalent for these procs), you will need to add the trait TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM to the mob in question - /// in order for unit tests to succeed. This will break obviously enough if you don't do this and declare the required datum here however. - var/list/operational_datums = null - -///Determines what behaviors should the controller try processing; if this returns SUBTREE_RETURN_FINISH_PLANNING then the controller won't go through the other subtrees should multiple exist in controller.planning_subtrees -/datum/ai_planning_subtree/proc/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - return diff --git a/code/datums/ai/_item_behaviors.dm b/code/datums/ai/_item_behaviors.dm index c9f3441fdd68..b07ccb4f5f3e 100644 --- a/code/datums/ai/_item_behaviors.dm +++ b/code/datums/ai/_item_behaviors.dm @@ -1,60 +1,31 @@ -///This behavior is for obj/items, it is used to free themselves out of the hands of whoever is holding them -/datum/ai_behavior/item_escape_grasp - -/datum/ai_behavior/item_escape_grasp/perform(seconds_per_tick, datum/ai_controller/controller) - var/obj/item/item_pawn = controller.pawn - var/mob/item_holder = item_pawn.loc - if(!istype(item_holder)) - //We're no longer being held. abort abort!! - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - item_pawn.visible_message(span_warning("[item_pawn] slips out of the hands of [item_holder]!")) - item_holder.dropItemToGround(item_pawn, TRUE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - -///This behavior is for obj/items, it is used to move closer to a target and throw themselves towards them. -/datum/ai_behavior/item_move_close_and_attack - required_distance = 3 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - action_cooldown = 20 - ///Sound to use - var/attack_sound - ///Max attemps to make - var/max_attempts = 3 - -/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, target_key, throw_count_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if (isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/item_move_close_and_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, throw_count_key) +///yeet yourself at a thing +/datum/bt_node/ai_behavior/throw_attack + /// Sound played on each throw. + var/attack_sound = 'sound/items/haunted/ghostitemattack.ogg' + /// Maximum throws before the attack is exhausted. + var/max_attempts = 4 + /// Blackboard key holding the throw target. + var/target_key + /// Blackboard key tracking how many throws have happened. + var/throw_count_key + +/datum/bt_node/ai_behavior/throw_attack/perform(seconds_per_tick, datum/ai_controller/controller) var/obj/item/item_pawn = controller.pawn var/atom/throw_target = controller.blackboard[target_key] - + if(QDELETED(throw_target)) + controller.clear_blackboard_key(target_key) + controller.set_blackboard_key(throw_count_key, 0) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!")) - item_pawn.throw_at(throw_target, rand(4,5), 9) + item_pawn.throw_at(throw_target, rand(4, 5), 9) playsound(item_pawn.loc, attack_sound, 100, TRUE) controller.add_blackboard_key(throw_count_key, 1) if(controller.blackboard[throw_count_key] >= max_attempts) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/item_move_close_and_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key) - . = ..() - reset_blackboard(controller, succeeded, target_key, throw_count_key) + return on_throws_exhausted(controller, throw_target, target_key, throw_count_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/item_move_close_and_attack/proc/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key) +/// Clears target and resets throw count. Override to add extra on-exhaust logic. +/datum/bt_node/ai_behavior/throw_attack/proc/on_throws_exhausted(datum/ai_controller/controller, atom/throw_target, target_key, throw_count_key) controller.clear_blackboard_key(target_key) controller.set_blackboard_key(throw_count_key, 0) - -/datum/ai_behavior/item_move_close_and_attack/ghostly - attack_sound = 'sound/items/haunted/ghostitemattack.ogg' - max_attempts = 4 - -/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted - -/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key) - controller.add_blackboard_key_assoc(BB_TO_HAUNT_LIST, controller.blackboard[target_key], -1) - return ..() + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED diff --git a/code/datums/ai/babies/babies_behaviors.dm b/code/datums/ai/babies/babies_behaviors.dm index c12f4acc6fac..f152db38b4c2 100644 --- a/code/datums/ai/babies/babies_behaviors.dm +++ b/code/datums/ai/babies/babies_behaviors.dm @@ -1,23 +1,30 @@ #define FIND_PARTNER_COOLDOWN 1 MINUTES -/** - * Find a compatible, living partner, if we're also alone. - */ -/datum/ai_behavior/find_partner - action_cooldown = 5 SECONDS - /// Range to look. +/// Find someone to erp with +/datum/bt_node/ai_behavior/find_partner + var/target_key + var/partner_types_key + var/child_types_key + time_between_perform = 5 SECONDS var/range = 7 - /// Maximum number of nearby pop var/max_nearby_pop = 3 -/datum/ai_behavior/find_partner/perform(seconds_per_tick, datum/ai_controller/controller, target_key, partner_types_key, child_types_key) - var/maximum_pop = controller.blackboard[BB_MAX_CHILDREN] || max_nearby_pop +/datum/bt_node/ai_behavior/find_partner/setup(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(living_pawn.gender == FEMALE) + return FALSE + if(!controller.blackboard[partner_types_key] || !controller.blackboard[child_types_key]) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/find_partner/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/pawn_mob = controller.pawn - var/list/similar_species_types = controller.blackboard[partner_types_key] + controller.blackboard[child_types_key] var/mob/living/living_pawn = controller.pawn + var/maximum_pop = controller.blackboard[BB_MAX_CHILDREN] || max_nearby_pop + var/list/similar_species_types = controller.blackboard[partner_types_key] + controller.blackboard[child_types_key] var/list/possible_partners = list() - var/nearby_pop = 0 + for(var/mob/living/other in oview(range, pawn_mob)) if(!pawn_mob.faction_check_atom(other)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED @@ -32,10 +39,10 @@ if(!HAS_TRAIT(other, TRAIT_MOB_BREEDER) || other.ckey) continue - if(other.stat != CONSCIOUS) //Check if it's conscious FIRST. + if(IS_UNCONSCIOUS_OR_CRIT(other)) //Check if it's conscious FIRST. continue - if(other.gender != living_pawn.gender && !(other.flags_1 & HOLOGRAM_1)) //Better safe than sorry ;_; + if(other.gender != living_pawn.gender && !(other.flags_1 & HOLOGRAM_1)) possible_partners += other if(!length(possible_partners)) @@ -44,28 +51,25 @@ controller.set_blackboard_key(target_key, pick(possible_partners)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/** - * Reproduce. - */ -/datum/ai_behavior/make_babies - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH +/// I'm not saying what this behavior does :flushed: +/datum/bt_node/ai_behavior/make_babies + var/target_key + var/child_types_key -/datum/ai_behavior/make_babies/setup(datum/ai_controller/controller, target_key, child_types_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(!target) - return FALSE - set_movement_target(controller, target) +/datum/bt_node/ai_behavior/make_babies/setup(datum/ai_controller/controller) + var/mob/target = controller.blackboard[target_key] + return !QDELETED(target) && !IS_UNCONSCIOUS_OR_CRIT(target) -/datum/ai_behavior/make_babies/perform(seconds_per_tick, datum/ai_controller/controller, target_key, child_types_key) +/datum/bt_node/ai_behavior/make_babies/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/target = controller.blackboard[target_key] - if(QDELETED(target) || target.stat != CONSCIOUS) + if(QDELETED(target) || IS_UNCONSCIOUS_OR_CRIT(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.ai_interact(target = target, combat_mode = FALSE) + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, FALSE) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/make_babies/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/make_babies/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) + #undef FIND_PARTNER_COOLDOWN diff --git a/code/datums/ai/babies/babies_subtrees.dm b/code/datums/ai/babies/babies_subtrees.dm deleted file mode 100644 index 7cadd1e8c551..000000000000 --- a/code/datums/ai/babies/babies_subtrees.dm +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Reproduce with a similar mob. - */ -/datum/ai_planning_subtree/make_babies - operational_datums = list(/datum/component/breed) - /// Chance to make babies - var/chance = 5 - /// Make babies behavior we will use - var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies - /// Find partner behavior we will use - var/datum/ai_behavior/partner_behavior = /datum/ai_behavior/find_partner - -/datum/ai_planning_subtree/make_babies/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - - if(!SPT_PROB(chance, seconds_per_tick) || controller.blackboard[BB_PARTNER_SEARCH_TIMEOUT] >= world.time) - return - - if(controller.blackboard_key_exists(BB_BABIES_TARGET)) - controller.queue_behavior(reproduce_behavior, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES) - return SUBTREE_RETURN_FINISH_PLANNING - - if(controller.pawn.gender == FEMALE || !controller.blackboard[BB_BREED_READY]) - return - - if(!controller.blackboard[BB_BABIES_PARTNER_TYPES] || !controller.blackboard[BB_BABIES_CHILD_TYPES]) - return - - // Find target - controller.queue_behavior(partner_behavior, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES) diff --git a/code/datums/ai/babies/make_babies.bt.json b/code/datums/ai/babies/make_babies.bt.json new file mode 100644 index 000000000000..6051613a60d7 --- /dev/null +++ b/code/datums/ai/babies/make_babies.bt.json @@ -0,0 +1,31 @@ +{ + "dm_type": "/datum/bt_node/subtree/make_babies", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_BABIES_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BABIES_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/make_babies", + "vars": { + "target_key": "BB_BABIES_TARGET", + "child_types_key": "BB_BABIES_CHILD_TYPES" + } + } + ] + } +} diff --git a/code/datums/ai/bane/bane.bt.json b/code/datums/ai/bane/bane.bt.json new file mode 100644 index 000000000000..b0e92e321eba --- /dev/null +++ b/code/datums/ai/bane/bane.bt.json @@ -0,0 +1,43 @@ +{ + "dm_type": "/datum/ai_controller/bane", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BANE_BATMAN" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BANE_BATMAN", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_spine/bane", + "vars": { + "target_key": "BB_BANE_BATMAN" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_BANE_BATMAN", + "target_source": "/datum/target_source/oview_single_type/living_mob", + "targeting_strategy": "/datum/targeting_strategy/conscious_mob", + "vision_range": 7 + } + } + ] +} diff --git a/code/datums/ai/bane/bane_behaviors.dm b/code/datums/ai/bane/bane_behaviors.dm deleted file mode 100644 index c47c455f7f9c..000000000000 --- a/code/datums/ai/bane/bane_behaviors.dm +++ /dev/null @@ -1,9 +0,0 @@ - -/datum/ai_behavior/break_spine/bane/finish_action(datum/ai_controller/controller, succeeded, target_key) - if(succeeded) - var/list/bane_quotes = strings("bane.json", "bane") - var/mob/living/bane = controller.pawn - if(QDELETED(bane)) // pawn can be null at this point - return ..() - bane.say(pick(bane_quotes)) - return ..() diff --git a/code/datums/ai/bane/bane_controller.dm b/code/datums/ai/bane/bane_controller.dm index 580e80440a41..e717f5495ebd 100644 --- a/code/datums/ai/bane/bane_controller.dm +++ b/code/datums/ai/bane/bane_controller.dm @@ -4,8 +4,8 @@ And the only victory you achieved was a lie. Now you understand Gotham is beyond */ /datum/ai_controller/bane movement_delay = 0.4 SECONDS + behavior_tree_json = "code/datums/ai/bane/bane.bt.json" blackboard = list(BB_BANE_BATMAN = null) - planning_subtrees = list(/datum/ai_planning_subtree/bane_hunting) /datum/ai_controller/bane/TryPossessPawn(atom/new_pawn) if(!isliving(new_pawn)) @@ -26,6 +26,6 @@ And the only victory you achieved was a lie. Now you understand Gotham is beyond /datum/ai_controller/bane/get_able_to_run() var/mob/living/living_pawn = pawn - if(IS_DEAD_OR_INCAP(living_pawn)) + if(living_pawn.incapacitated) return AI_UNABLE_TO_RUN return ..() diff --git a/code/datums/ai/bane/bane_subtrees.dm b/code/datums/ai/bane/bane_subtrees.dm deleted file mode 100644 index e7f651737bfc..000000000000 --- a/code/datums/ai/bane/bane_subtrees.dm +++ /dev/null @@ -1,16 +0,0 @@ -///The bat is broken! -/datum/ai_planning_subtree/bane_hunting/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/batman = controller.blackboard[BB_BANE_BATMAN] - if(QDELETED(batman)) - for(var/mob/living/possibly_the_dark_knight in oview(7, controller.pawn)) - if(IS_DEAD_OR_INCAP(possibly_the_dark_knight)) //I HAVE BROKEN THE BAT - continue - controller.set_blackboard_key(BB_BANE_BATMAN, possibly_the_dark_knight) - batman = possibly_the_dark_knight - break - - if(isnull(batman)) - return - - controller.queue_behavior(/datum/ai_behavior/break_spine/bane, BB_BANE_BATMAN) - return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/datums/ai/basic_mobs/admin_ai_templates.dm b/code/datums/ai/basic_mobs/admin_ai_templates.dm index 96aedf36ec47..14935531e2d3 100644 --- a/code/datums/ai/basic_mobs/admin_ai_templates.dm +++ b/code/datums/ai/basic_mobs/admin_ai_templates.dm @@ -40,9 +40,8 @@ if (isnull(minimum_stat)) var/static/list/stat_types = list( - "Conscious" = CONSCIOUS, + "Stable" = STABLE, "Soft Crit" = SOFT_CRIT, - "Unconscious" = UNCONSCIOUS, "Hard Crit" = HARD_CRIT, "Dead (will probably get stuck punching a corpse forever)" = DEAD, ) @@ -350,14 +349,14 @@ /// Coward /datum/admin_ai_template/fearful name = "Runs Away" - minimum_stat = CONSCIOUS + minimum_stat = STABLE make_hostile = FALSE controller_type = /datum/ai_controller/basic_controller/simple/simple_fearful /// Doesn't like violence /datum/admin_ai_template/skittish name = "Runs Away From Attackers" - minimum_stat = CONSCIOUS + minimum_stat = STABLE make_hostile = FALSE controller_type = /datum/ai_controller/basic_controller/simple/simple_skittish diff --git a/code/datums/ai/basic_mobs/base_basic_controller.dm b/code/datums/ai/basic_mobs/base_basic_controller.dm index 9537869bd051..85ec0290d740 100644 --- a/code/datums/ai/basic_mobs/base_basic_controller.dm +++ b/code/datums/ai/basic_mobs/base_basic_controller.dm @@ -1,5 +1,6 @@ /datum/ai_controller/basic_controller movement_delay = 0.4 SECONDS + behavior_tree_json = ABSTRACT_AI_CLASS /datum/ai_controller/basic_controller/TryPossessPawn(atom/new_pawn) if(!isliving(new_pawn)) @@ -34,7 +35,7 @@ if(. & AI_UNABLE_TO_RUN) return . var/mob/living/living_pawn = pawn - if (living_pawn.stat && !(ai_traits & CAN_ACT_WHILE_DEAD)) + if (IS_UNCONSCIOUS_OR_CRIT(living_pawn) && !(ai_traits & CAN_ACT_WHILE_DEAD)) return AI_UNABLE_TO_RUN var/ignore_incap_flags = NONE @@ -62,9 +63,9 @@ /datum/ai_controller/proc/on_tamed(datum/source, mob/living/new_friend) SIGNAL_HANDLER forgive_target(new_friend) - clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + clear_blackboard_key(BB_CURRENT_TARGET) clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST) //we have just been tamed by a new party, clean slate for everyone! - CancelActions() + cancel_current_plan() RegisterSignal(new_friend, COMSIG_LIVING_MADE_NEW_FRIEND, PROC_REF(on_master_tame)) /datum/ai_controller/proc/on_untamed(datum/source, mob/living/old_friend) @@ -77,7 +78,7 @@ /datum/ai_controller/proc/forgive_target(atom/target) var/static/list/keys_to_check = list( - BB_BASIC_MOB_CURRENT_TARGET, + BB_CURRENT_TARGET, BB_CURRENT_PET_TARGET, ) for(var/key in keys_to_check) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index 5b41ae5254d2..2be8c0b21ee1 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm @@ -1,144 +1,121 @@ /// Amount of time to wait before executing attack if not specified #define DEFAULT_ATTACK_DELAY (0.4 SECONDS) -/datum/ai_behavior/basic_melee_attack - action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - ///do we finish this action after hitting once? - var/terminate_after_action = FALSE - ///do we have any alternate movement behavior? - var/movement_behavior - -/datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) +/// Perform a melee attack on the target specified. +/datum/bt_node/ai_behavior/basic_melee_attack + var/target_key + var/targeting_strategy = BB_TARGETING_STRATEGY + var/hiding_location_key + +/datum/bt_node/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller) . = ..() - if(!controller.blackboard[targeting_strategy_key]) + + + if(!ispath(targeting_strategy)) + targeting_strategy = controller.blackboard[targeting_strategy] + + if(!targeting_strategy) CRASH("No targeting strategy was supplied in the blackboard for [controller.pawn]") - //Hiding location is priority var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] if(QDELETED(target)) return FALSE - set_movement_target(controller, target, movement_behavior) - -/datum/ai_behavior/basic_melee_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) +/datum/bt_node/ai_behavior/basic_melee_attack/perform(seconds_per_tick, datum/ai_controller/controller) var/atom/target = controller.blackboard[target_key] if (isnull(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if (!can_attack(controller, target)) - return AI_BEHAVIOR_INSTANT - - if (isliving(controller.pawn)) - var/mob/living/pawn = controller.pawn - if (world.time < pawn.next_move) - return AI_BEHAVIOR_INSTANT - - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if(!targeting_strategy.can_attack(controller.pawn, target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/hiding_target = targeting_strategy.find_hidden_mobs(controller.pawn, target) //If this is valid, theyre hidden in something! - - controller.set_blackboard_key(hiding_location_key, hiding_target) - - var/atom/final_target = hiding_target || target - controller.ai_interact(target = final_target, combat_mode = TRUE) - if(terminate_after_action) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/basic_melee_attack/proc/can_attack(datum/ai_controller/controller, atom/target) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED if (!target.IsReachableBy(controller.pawn)) controller.clear_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER) - return FALSE + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/can_attack_time = controller.blackboard[BB_BASIC_MOB_MELEE_COOLDOWN_TIMER] if (isnull(can_attack_time)) var/blackboard_delay = controller.blackboard[BB_BASIC_MOB_MELEE_DELAY] var/attack_delay = isnull(blackboard_delay) ? DEFAULT_ATTACK_DELAY : blackboard_delay controller.set_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER, world.time + attack_delay) - return FALSE - + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED if (can_attack_time > world.time) - return FALSE + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - return TRUE + if (isliving(controller.pawn)) + var/mob/living/pawn = controller.pawn + if (world.time < pawn.next_move) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED -/datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - controller.clear_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER) - if(movement_behavior) - controller.change_ai_movement_type(initial(controller.ai_movement)) - if(!succeeded) + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(targeting_strategy) + if(!strategy.is_valid_target(controller.pawn, target, controller = controller)) controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED -/datum/ai_behavior/basic_melee_attack/interact_once - terminate_after_action = TRUE + var/hiding_target = strategy.find_hidden_mobs(controller.pawn, target) //If this is valid, theyre hidden in something! -/datum/ai_behavior/basic_melee_attack/interact_once/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) + controller.set_blackboard_key(hiding_location_key, hiding_target) + + var/atom/final_target = hiding_target || target + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), final_target, TRUE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/// Single-hit variant: terminates after one successful attack and always clears the target key. +/datum/bt_node/ai_behavior/basic_melee_attack/interact_once + +/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) -/datum/ai_behavior/basic_ranged_attack - action_cooldown = 0.6 SECONDS - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - required_distance = 3 - /// range we will try chasing the target before giving up - var/chase_range = 9 - ///do we care about avoiding friendly fire? +//Basic ranged attack behavior +/datum/bt_node/ai_behavior/basic_ranged_attack + var/target_key + var/targeting_strategy = BB_TARGETING_STRATEGY + var/hiding_location_key + time_between_perform = 0.6 SECONDS + /// Max range at which we can fire. Make sure your movement actually gets you this close please + var/max_range = 9 + /// Avoid shooting through friendlies. var/avoid_friendly_fire = FALSE -/datum/ai_behavior/basic_ranged_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) +/datum/bt_node/ai_behavior/basic_ranged_attack/setup(datum/ai_controller/controller) . = ..() if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED)) return FALSE var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] if(QDELETED(target)) return FALSE - set_movement_target(controller, target) + return TRUE -/datum/ai_behavior/basic_ranged_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) +/datum/bt_node/ai_behavior/basic_ranged_attack/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/basic_mob = controller.pawn - //targeting strategy will kill the action if not real anymore var/atom/target = controller.blackboard[target_key] - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if(!targeting_strategy.can_attack(basic_mob, target, chase_range)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!ispath(targeting_strategy)) + targeting_strategy = controller.blackboard[targeting_strategy] + + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(targeting_strategy) - var/atom/hiding_target = targeting_strategy.find_hidden_mobs(basic_mob, target) //If this is valid, theyre hidden in something! + var/atom/hiding_target = strategy.find_hidden_mobs(basic_mob, target) var/atom/final_target = hiding_target ? hiding_target : target + controller.set_blackboard_key(hiding_location_key, hiding_target) - if(!can_see(basic_mob, final_target, required_distance)) - return AI_BEHAVIOR_INSTANT + if(!can_see(basic_mob, final_target, max_range)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - if(avoid_friendly_fire && check_friendly_in_path(basic_mob, target, targeting_strategy)) + if(avoid_friendly_fire && check_friendly_in_path(basic_mob, target, strategy)) adjust_position(basic_mob, target) - return AI_BEHAVIOR_DELAY + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(hiding_location_key, hiding_target) basic_mob.RangedAttack(final_target) - return AI_BEHAVIOR_DELAY //only start the cooldown when the shot is shot + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/basic_ranged_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - if(!succeeded) - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/basic_ranged_attack/proc/check_friendly_in_path(mob/living/source, atom/target, datum/targeting_strategy/targeting_strategy) +/datum/bt_node/ai_behavior/basic_ranged_attack/proc/check_friendly_in_path(mob/living/source, atom/target, datum/targeting_strategy/targeting_strategy) var/list/turfs_list = calculate_trajectory(source, target) for(var/turf/possible_turf as anything in turfs_list) - for(var/mob/living/potential_friend in possible_turf) - if(!targeting_strategy.can_attack(source, potential_friend)) + if(!targeting_strategy.is_valid_target(source, potential_friend)) return TRUE - return FALSE -/datum/ai_behavior/basic_ranged_attack/proc/adjust_position(mob/living/living_pawn, atom/target) +/datum/bt_node/ai_behavior/basic_ranged_attack/proc/adjust_position(mob/living/living_pawn, atom/target) var/turf/our_turf = get_turf(living_pawn) var/list/possible_turfs = list() - for(var/direction in GLOB.alldirs) var/turf/target_turf = get_step(our_turf, direction) if(isnull(target_turf)) @@ -146,13 +123,12 @@ if(target_turf.is_blocked_turf() || get_dist(target_turf, target) > get_dist(living_pawn, target)) continue possible_turfs += target_turf - if(!length(possible_turfs)) return var/turf/picked_turf = get_closest_atom(/turf, possible_turfs, target) step(living_pawn, get_dir(living_pawn, picked_turf)) -/datum/ai_behavior/basic_ranged_attack/proc/calculate_trajectory(mob/living/source , atom/target) +/datum/bt_node/ai_behavior/basic_ranged_attack/proc/calculate_trajectory(mob/living/source, atom/target) var/list/turf_list = get_line(source, target) var/list_length = length(turf_list) - 1 for(var/i in 1 to list_length) @@ -161,17 +137,14 @@ var/direction_to_turf = get_dir(current_turf, next_turf) if(!ISDIAGONALDIR(direction_to_turf)) continue - for(var/cardinal_direction in GLOB.cardinals) if(cardinal_direction & direction_to_turf) turf_list += get_step(current_turf, cardinal_direction) - turf_list -= get_turf(source) turf_list -= get_turf(target) - return turf_list -/datum/ai_behavior/basic_ranged_attack/avoid_friendly_fire +/datum/bt_node/ai_behavior/basic_ranged_attack/avoid_friendly_fire avoid_friendly_fire = TRUE #undef DEFAULT_ATTACK_DELAY diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm index 865b028e156c..cd50aa734d73 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm @@ -1,19 +1,23 @@ -///behavior to befriend any targets -/datum/ai_behavior/befriend_target +/datum/bt_node/ai_behavior/befriend_target + var/target_key + var/befriend_message + var/long_range_friendship = FALSE + var/forget_target = TRUE -/datum/ai_behavior/befriend_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, befriend_message) +/datum/bt_node/ai_behavior/befriend_target/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/living_pawn = controller.pawn var/mob/living/living_target = controller.blackboard[target_key] if(QDELETED(living_target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - + if(!long_range_friendship && get_dist(living_pawn, living_target) > 1) + return AI_BEHAVIOR_INSTANT living_pawn.befriend(living_target) var/befriend_text = controller.blackboard[befriend_message] if(befriend_text) to_chat(living_target, span_nicegreen("[living_pawn] [befriend_text]")) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/befriend_target/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/befriend_target/finish_action(datum/ai_controller/controller, succeeded) . = ..() - controller.clear_blackboard_key(target_key) + if(forget_target) + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/call_reinforcements.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/call_reinforcements.dm new file mode 100644 index 000000000000..4888f3d9054e --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/call_reinforcements.dm @@ -0,0 +1,50 @@ +/// Emotes a reinforcement call and alerts nearby faction members, adding the current target to their retaliate lists. +/// Returns FAILURE when there is no valid target or the target is a friend. +/datum/bt_node/ai_behavior/call_reinforcements + /// How far to look for reinforcements + var/reinforcements_range = 15 + ///Target to call reinforcements on + var/target_key = BB_CURRENT_TARGET + +/datum/bt_node/ai_behavior/call_reinforcements/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/target = controller.blackboard[target_key] + if(!istype(target, /mob)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/mob/pawn_mob = controller.pawn + var/list/friends = controller.blackboard[BB_FRIENDS_LIST] + if(friends && (target in friends)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/say_text = controller.blackboard[BB_REINFORCEMENTS_SAY] + if(!isnull(say_text)) + pawn_mob.say(say_text, forced = "AI Controller") + else + var/emote_text = controller.blackboard[BB_REINFORCEMENTS_EMOTE] + if(!isnull(emote_text)) + pawn_mob.manual_emote(emote_text) + + for(var/mob/other_mob in oview(reinforcements_range, pawn_mob)) + if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller)) + continue + other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, target, world.time) + other_mob.ai_controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENT_TARGET, pawn_mob) + + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Mining/swarm variant: boosts priority rather than forcing retaliation, shorter range and faster cooldown. +/datum/bt_node/ai_behavior/call_reinforcements/mining + reinforcements_range = 7 + +/datum/bt_node/ai_behavior/call_reinforcements/mining/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/target = controller.blackboard[target_key] + if(!istype(target, /mob)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/mob/pawn_mob = controller.pawn + for(var/mob/other_mob in oview(reinforcements_range, pawn_mob)) + if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller)) + continue + var/list/existing_requests = other_mob.ai_controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS] + if(!existing_requests || !existing_requests[target]) + other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, list()) + other_mob.ai_controller.add_blackboard_key_assoc(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, world.time) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/clear_key.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/clear_key.dm deleted file mode 100644 index ae0711789faa..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/clear_key.dm +++ /dev/null @@ -1,14 +0,0 @@ -/// Clears a blackboard key (or keys), simply if you want to do this after an action without making a subtype -/datum/ai_behavior/clear_key - -/datum/ai_behavior/clear_key/perform(seconds_per_tick, datum/ai_controller/controller, list/to_clear) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/clear_key/finish_action(datum/ai_controller/controller, succeeded, list/to_clear) - . = ..() - if (!to_clear) - return - if (!islist(to_clear)) - to_clear = list(to_clear) - for (var/key in to_clear) - controller.clear_blackboard_key(key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm deleted file mode 100644 index c30943caf50c..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/climb_tree.dm +++ /dev/null @@ -1,35 +0,0 @@ -/datum/ai_behavior/find_and_set/valid_tree - -/datum/ai_behavior/find_and_set/valid_tree/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/valid_trees = list() - for (var/obj/structure/flora/tree/tree_target in oview(search_range, controller.pawn)) - if(istype(tree_target, /obj/structure/flora/tree/dead)) //no died trees - continue - valid_trees += tree_target - - if(valid_trees.len) - return pick(valid_trees) - -/datum/ai_behavior/climb_tree - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/climb_tree/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - - set_movement_target(controller, target) - -/datum/ai_behavior/climb_tree/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/obj/structure/flora/target_tree = controller.blackboard[target_key] - var/mob/living/basic/living_pawn = controller.pawn - if(QDELETED(living_pawn)) // pawn can be null at this point - return - SEND_SIGNAL(living_pawn, COMSIG_LIVING_CLIMB_TREE, target_tree) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/climb_tree/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(succeeded) - controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.json b/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.json new file mode 100644 index 000000000000..54e4af4336a5 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.json @@ -0,0 +1,46 @@ +{ + "dm_type": "/datum/bt_node/subtree/consider_venting", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_in_vent", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/exit_vent" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_ENTRY_VENT_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_VENTING_COOLDOWN", + "cooldown_duration": "BB_VENTCRAWL_COOLDOWN" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ENTRY_VENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enter_vent" + } + ] + } + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm deleted file mode 100644 index 7960301d7044..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm +++ /dev/null @@ -1,28 +0,0 @@ -/datum/ai_behavior/emote_on_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - - -/datum/ai_behavior/emote_on_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/hunt_target = controller.blackboard[target_key] - if (isnull(hunt_target)) - return FALSE - set_movement_target(controller, hunt_target) - - -/datum/ai_behavior/emote_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, list/emote_list) - var/atom/target = controller.blackboard[target_key] - if(!length(emote_list) || isnull(target)) - return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY - run_emote(controller.pawn, target, emote_list) - return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY - - -/datum/ai_behavior/emote_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(succeeded) - controller.clear_blackboard_key(target_key) - - -/datum/ai_behavior/emote_on_target/proc/run_emote(mob/living/living_pawn, atom/target, list/emote_list) - living_pawn.manual_emote("[pick(emote_list)] [target]") diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/find_flee_location.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_flee_location.dm new file mode 100644 index 000000000000..9efe9d6fc873 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_flee_location.dm @@ -0,0 +1,37 @@ +/// Finds the best adjacent turf to flee to away from a threat and stores it in a blackboard key. +/// Tries get_step_away first, then falls back to shuffled directions if blocked. +/// Returns INSTANT SUCCESS if a step was found, INSTANT FAILURE if completely cornered. +/datum/bt_node/ai_behavior/find_flee_location + var/target_key + var/hiding_location_key + var/destination_key + +/datum/bt_node/ai_behavior/find_flee_location/perform(seconds_per_tick, datum/ai_controller/controller) + var/run_distance = controller.blackboard[BB_BASIC_MOB_FLEE_DISTANCE] || DEFAULT_BASIC_FLEE_DISTANCE + var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] + if(QDELETED(target) || !can_see(controller.pawn, target, run_distance)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/turf/flee_turf = get_flee_step(controller, target) + if(!flee_turf) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(destination_key, flee_turf) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/find_flee_location/proc/get_flee_step(datum/ai_controller/controller, atom/target) + var/mob/living/pawn = controller.pawn + var/turf/pawn_turf = get_turf(pawn) + var/datum/can_pass_info/pass_info = new(pawn, controller.get_access()) + var/turf/next_step = get_step_away(pawn, target) + if(!isnull(next_step) && next_step != pawn_turf && !next_step.density && !pawn_turf.LinkBlockedWithAccess(next_step, pass_info)) + return next_step + var/list/all_dirs = GLOB.alldirs.Copy() + all_dirs -= get_dir(pawn, next_step) + all_dirs -= get_dir(pawn, target) + shuffle_inplace(all_dirs) + for(var/dir in all_dirs) + next_step = get_step(pawn, dir) + if(!isnull(next_step) && !next_step.density && !pawn_turf.LinkBlockedWithAccess(next_step, pass_info)) + return next_step + return null diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/find_parent.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_parent.dm index 31735f99231b..7e8a0a4d3913 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/find_parent.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_parent.dm @@ -1,35 +1,51 @@ -/datum/ai_behavior/find_mom - ///range to look for the mom +/// Looks around for a nearby adult of one of BB_FIND_MOM_TYPES (skipping BB_IGNORE_MOM_TYPES) and stores it. +/datum/bt_node/ai_behavior/find_mom + time_between_perform = 2 SECONDS + /// How far to look for our parent. var/look_range = 7 + /// Blackboard key holding the list of typepaths we accept as parents. + var/mom_types_key = BB_FIND_MOM_TYPES + /// Blackboard key holding typepaths to skip even if they match (e.g. other babies). + var/ignore_types_key = BB_IGNORE_MOM_TYPES + /// Blackboard key to store the found parent in. + var/found_mom_key = BB_FOUND_MOM -/datum/ai_behavior/find_mom/perform(seconds_per_tick, datum/ai_controller/controller, mom_key, ignore_mom_key, found_mom) +/datum/bt_node/ai_behavior/find_mom/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living_pawn = controller.pawn - var/list/all_moms = list() - var/list/mom_types = controller.blackboard[mom_key] - var/list/ignore_types = controller.blackboard[ignore_mom_key] - + var/list/mom_types = controller.blackboard[mom_types_key] if(!length(mom_types)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/list/ignore_types = controller.blackboard[ignore_types_key] + var/list/all_moms = list() for(var/mob/mother in oview(look_range, living_pawn)) - if (is_possible_mom(mother, mom_types, ignore_types)) + if(is_possible_mom(mother, mom_types, ignore_types)) all_moms += mother if(length(all_moms)) - controller.set_blackboard_key(found_mom, pick(all_moms)) + controller.set_blackboard_key(found_mom_key, pick(all_moms)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_behavior/find_mom/proc/is_possible_mom(mob/mother, list/mom_types, list/ignore_types) +/datum/bt_node/ai_behavior/find_mom/proc/is_possible_mom(mob/mother, list/mom_types, list/ignore_types) if(!is_type_in_list(mother, mom_types)) return FALSE - if(is_type_in_list(mother, ignore_types)) // so the not permanent baby and the permanent baby subtype dont followed each other + if(is_type_in_list(mother, ignore_types)) return FALSE return TRUE -/datum/ai_behavior/find_mom/raptor/is_possible_mom(mob/mother, list/mom_types, list/ignore_types) - . = ..() - if (!. || !istype(mother, /mob/living/basic/raptor)) - return FALSE - var/mob/living/basic/raptor/raptor = mother - return raptor.growth_stage == RAPTOR_ADULT +/// A baby emotes at its parent: crying if the parent is dead, dancing happily otherwise. +/datum/bt_node/ai_behavior/look_to_parent + /// Blackboard key holding the parent to react to. + var/parent_key = BB_FOUND_MOM + +/datum/bt_node/ai_behavior/look_to_parent/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/parent = controller.blackboard[parent_key] + if(QDELETED(parent)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/baby = controller.pawn + if(parent.stat == DEAD) + baby.manual_emote("cries for their parent!") + else + baby.manual_emote("dances around their parent!") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm deleted file mode 100644 index e3b202c5e164..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm +++ /dev/null @@ -1,29 +0,0 @@ -///behavior for general interactions with any targets -/datum/ai_behavior/interact_with_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - ///should we be clearing the target after the fact? - var/clear_target = TRUE - ///should our combat mode be off during interaction? - var/combat_mode = TRUE - -/datum/ai_behavior/interact_with_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/interact_with_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target) || !pre_interact(controller, target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.ai_interact(target, combat_mode) - return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY - -/datum/ai_behavior/interact_with_target/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(clear_target || !succeeded) - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/interact_with_target/proc/pre_interact(datum/ai_controller/controller, target) - return TRUE diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/nearest_targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/nearest_targeting.dm index 8a570375cba1..9a0d6d041ef2 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/nearest_targeting.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/nearest_targeting.dm @@ -1,13 +1,6 @@ -/// Picks targets based on which one is closest to you, choice between targets at equal distance is arbitrary -/datum/ai_behavior/find_potential_targets/nearest +/// Pick nearest instead of any, we should probably move this into a datum of some kind in the future? +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest -/datum/ai_behavior/find_potential_targets/nearest/pick_final_target(datum/ai_controller/controller, list/filtered_targets) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest/pick_final_target(datum/ai_controller/controller, list/filtered_targets) var/turf/our_position = get_turf(controller.pawn) return get_closest_atom(/atom/, filtered_targets, our_position) - -/// As above but targets have been filtered from the 'retaliate' blackboard list -/datum/ai_behavior/target_from_retaliate_list/nearest - -/datum/ai_behavior/target_from_retaliate_list/nearest/pick_final_target(datum/ai_controller/controller, list/enemies_list) - var/turf/our_position = get_turf(controller.pawn) - return get_closest_atom(/atom/, enemies_list, our_position) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/pick_up_item.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/pick_up_item.dm deleted file mode 100644 index 1b65eaa507c9..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/pick_up_item.dm +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Simple behaviour for picking up an item we are already in range of. - * The blackboard storage key isn't very safe because it doesn't make sense to register signals in here. - * Use the AI held item component to manage this. - */ -/datum/ai_behavior/pick_up_item - -/datum/ai_behavior/pick_up_item/setup(datum/ai_controller/controller, target_key, storage_key) - . = ..() - var/obj/item/target = controller.blackboard[target_key] - return isitem(target) && isturf(target.loc) && !target.anchored - -/datum/ai_behavior/pick_up_item/perform(seconds_per_tick, datum/ai_controller/controller, target_key, storage_key) - var/obj/item/target = controller.blackboard[target_key] - if(QDELETED(target) || !isturf(target.loc)) // Someone picked it up or it got deleted - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(!controller.pawn.Adjacent(target)) // It teleported - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - pickup_item(controller, target, storage_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/pick_up_item/finish_action(datum/ai_controller/controller, success, target_key, storage_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/pick_up_item/proc/pickup_item(datum/ai_controller/controller, obj/item/target, storage_key) - var/atom/pawn = controller.pawn - drop_existing_item(controller, storage_key) - pawn.visible_message(span_notice("[pawn] picks up [target].")) - target.forceMove(pawn) - controller.set_blackboard_key(storage_key, target) - return TRUE - -/datum/ai_behavior/pick_up_item/proc/drop_existing_item(datum/ai_controller/controller, storage_key) - var/obj/item/carried_item = controller.blackboard[storage_key] - if(!carried_item) - return - controller.clear_blackboard_key(storage_key) - var/atom/pawn = controller.pawn - if(carried_item.loc != pawn) - return - pawn.visible_message(span_notice("[pawn] drops [carried_item].")) - carried_item.forceMove(get_turf(pawn)) - return TRUE diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/play_dead.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/play_dead.dm new file mode 100644 index 000000000000..49f27564b972 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/play_dead.dm @@ -0,0 +1,25 @@ +/// Plays dead until a per-tick probability check (default 10%) triggers revival. +/datum/bt_node/ai_behavior/play_dead + var/probability = 10 + +/datum/bt_node/ai_behavior/play_dead/setup(datum/ai_controller/controller) + var/mob/living/basic/pawn = controller.pawn + if(!istype(pawn) || pawn.stat) + return FALSE + INVOKE_ASYNC(pawn, TYPE_PROC_REF(/mob, emote), "deathgasp", intentional = FALSE) + ADD_TRAIT(pawn, TRAIT_FAKEDEATH, BASIC_MOB_DEATH_TRAIT) + pawn.look_dead() + +/datum/bt_node/ai_behavior/play_dead/perform(seconds_per_tick, datum/ai_controller/controller) + if(SPT_PROB(probability, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY + +/datum/bt_node/ai_behavior/play_dead/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + var/mob/living/basic/pawn = controller.pawn + if(QDELETED(pawn) || pawn.stat) + return + pawn.visible_message(span_notice("[pawn] miraculously springs back to life!")) + REMOVE_TRAIT(pawn, TRAIT_FAKEDEATH, BASIC_MOB_DEATH_TRAIT) + pawn.look_alive() diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/pull_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/pull_target.dm deleted file mode 100644 index 900b122dcb3f..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/pull_target.dm +++ /dev/null @@ -1,22 +0,0 @@ -/datum/ai_behavior/pull_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/pull_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/pull_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/movable/target = controller.blackboard[target_key] - if(QDELETED(target) || target.anchored || target.pulledby) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/our_mob = controller.pawn - our_mob.start_pulling(target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/pull_target/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(!succeeded) - controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/run_away_from_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/run_away_from_target.dm deleted file mode 100644 index f23707e72b8c..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/run_away_from_target.dm +++ /dev/null @@ -1,76 +0,0 @@ -/// Move to a position further away from your current target -/datum/ai_behavior/run_away_from_target - required_distance = 0 - action_cooldown = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// How far do we try to run? Further makes for smoother running, but potentially weirder pathfinding - var/run_distance = DEFAULT_BASIC_FLEE_DISTANCE - /// Clear target if we finish the action unsuccessfully - var/clear_failed_targets = TRUE - -/datum/ai_behavior/run_away_from_target/setup(datum/ai_controller/controller, target_key, hiding_location_key) - var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - run_distance = controller.blackboard[BB_BASIC_MOB_FLEE_DISTANCE] || initial(run_distance) - if(!plot_path_away_from(controller, target)) - return FALSE - return ..() - -/datum/ai_behavior/run_away_from_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key) - if (controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return AI_BEHAVIOR_DELAY - var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] - if (QDELETED(target) || !can_see(controller.pawn, target, run_distance)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if (get_dist(controller.pawn, controller.current_movement_target) > required_distance) - return AI_BEHAVIOR_DELAY // Still heading over - if (plot_path_away_from(controller, target)) - return AI_BEHAVIOR_DELAY - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/run_away_from_target/proc/plot_path_away_from(datum/ai_controller/controller, atom/target) - var/turf/target_destination = get_turf(controller.pawn) - var/static/list/offset_angles = list(45, 90, 135, 180, 225, 270) - for(var/angle in offset_angles) - var/turf/test_turf = get_furthest_turf(controller.pawn, angle, target) - if(isnull(test_turf)) - continue - var/distance_from_target = get_dist(target, test_turf) - if(distance_from_target <= get_dist(target, target_destination)) - continue - target_destination = test_turf - if(distance_from_target == run_distance) //we already got the max running distance - break - - if (target_destination == get_turf(controller.pawn)) - return FALSE - set_movement_target(controller, target_destination) - return TRUE - -/datum/ai_behavior/run_away_from_target/proc/get_furthest_turf(atom/source, angle, atom/target) - var/turf/return_turf - var/list/airlocks = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/airlock) - for(var/i in 1 to run_distance) - var/turf/test_destination = get_ranged_target_turf_direct(source, target, range = i, offset = angle) - if(test_destination.is_blocked_turf(source_atom = source, ignore_atoms = airlocks)) - break - return_turf = test_destination - return return_turf - -/datum/ai_behavior/run_away_from_target/finish_action(datum/ai_controller/controller, succeeded, target_key, hiding_location_key) - . = ..() - if (clear_failed_targets) - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/run_away_from_target/run_and_shoot - clear_failed_targets = FALSE - -/datum/ai_behavior/run_away_from_target/run_and_shoot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/living_pawn = controller.pawn - living_pawn.RangedAttack(target) - return ..() - diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/set_travel_destination.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/set_travel_destination.dm deleted file mode 100644 index 6bf6dbb7fdbc..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/set_travel_destination.dm +++ /dev/null @@ -1,10 +0,0 @@ -/datum/ai_behavior/set_travel_destination - -/datum/ai_behavior/set_travel_destination/perform(seconds_per_tick, datum/ai_controller/controller, target_key, location_key) - var/atom/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(location_key, target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/step_towards_turf.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/step_towards_turf.dm deleted file mode 100644 index 7cb3a7b9f140..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/step_towards_turf.dm +++ /dev/null @@ -1,74 +0,0 @@ -/** - * # Step towards turf - * Moves a short distance towards a location repeatedly until you arrive at the destination. - * You'd use this over travel_towards if you're travelling a long distance over a long time, because the AI controller has a maximum range. - */ -/datum/ai_behavior/step_towards_turf - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// How far ahead do we plot movement per action? Further means longer until we return to the decision tree, fewer means jerkier movement - /// This can still result in long moves because this is "a tile x tiles away" not "only move x tiles", you might path around some walls - var/step_distance = 3 - -/datum/ai_behavior/step_towards_turf/setup(datum/ai_controller/controller, turf_key) - var/turf/target_turf = controller.blackboard[turf_key] - if (QDELETED(target_turf) || target_turf.is_blocked_turf(exclude_mobs = TRUE)) - target_turf = find_destination_turf(args) - if (!target_turf) - return FALSE - controller.set_blackboard_key(turf_key, target_turf) - - if (target_turf.z != controller.pawn.z) - return FALSE - - var/turf/destination = plot_movement(controller, target_turf) - if (!destination) - return FALSE - set_movement_target(controller, destination) - return ..() - -/** - * Get a turf to aim towards if we don't already have one, the default behaviour is actually to not do this but we want to extend it - * Gets passed all of the arguments from `setup` - */ -/datum/ai_behavior/step_towards_turf/proc/find_destination_turf() - return null - -/** - * Figure out where we're going to move to, which isn't all the way to the destination in one go - */ -/datum/ai_behavior/step_towards_turf/proc/plot_movement(datum/ai_controller/controller, turf/target_turf) - var/distance_to_destination = get_dist(controller.pawn, target_turf) - if (distance_to_destination <= step_distance) - return target_turf - - var/direction_to_destination = get_dir(controller.pawn, target_turf) - return get_ranged_target_turf(controller.pawn, direction_to_destination, step_distance) - -// We actually only wanted the movement so if we've arrived we're done -/datum/ai_behavior/step_towards_turf/perform(seconds_per_tick, datum/ai_controller/controller, area_key, turf_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/** - * # Step towards turf in area - * Moves a short distance towards a location in an area - * Unlike step_towards_turf it will reacquire a new turf from the area if it loses its target - */ -/datum/ai_behavior/step_towards_turf/in_area - -/datum/ai_behavior/step_towards_turf/in_area/setup(datum/ai_controller/controller, turf_key, area_key) - var/area/target_area = controller.blackboard[area_key] - if (!target_area) - return FALSE - - return ..() - -// Return the first valid turf in the area to replace a lost target -/datum/ai_behavior/step_towards_turf/in_area/find_destination_turf(datum/ai_controller/controller, turf_key, area_key) - var/area/target_area = controller.blackboard[area_key] - var/list/target_area_turfs = get_area_turfs(target_area.type) - for (var/turf/potential_target as anything in target_area_turfs) - if (potential_target.is_blocked_turf(exclude_mobs = TRUE)) - continue - return potential_target - return null diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm index 4ce1f877b18a..a5a60d726383 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm @@ -1,27 +1,29 @@ -/// Makes a mob simply stop and stare at a movable... yea... -/datum/ai_behavior/stop_and_stare - behavior_flags = AI_BEHAVIOR_MOVE_AND_PERFORM +/// Faces a nearby scary atom and holds still for a while. +/datum/bt_node/ai_behavior/stop_and_stare + /// Blackboard key holding the atom we're staring at. + var/target_key = BB_STATIONARY_CAUSE + /// Blackboard key holding how long (in deciseconds) to stay frozen for. + var/stare_duration_key = BB_STATIONARY_SECONDS -/datum/ai_behavior/stop_and_stare/setup(datum/ai_controller/controller, target_key) - . = ..() +/datum/bt_node/ai_behavior/stop_and_stare/setup(datum/ai_controller/controller) var/atom/movable/target = controller.blackboard[target_key] return ismovable(target) && isturf(target.loc) && ismob(controller.pawn) -/datum/ai_behavior/stop_and_stare/get_cooldown(datum/ai_controller/cooldown_for) - return cooldown_for.blackboard[BB_STATIONARY_COOLDOWN] +/datum/bt_node/ai_behavior/stop_and_stare/get_cooldown(datum/ai_controller/cooldown_for) + return cooldown_for.blackboard[stare_duration_key] || ..() -/datum/ai_behavior/stop_and_stare/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/stop_and_stare/perform(seconds_per_tick, datum/ai_controller/controller) var/atom/movable/target = controller.blackboard[target_key] - if(!ismovable(target) || !isturf(target.loc)) // just to make sure that nothing funky happened between setup and perform - return AI_BEHAVIOR_DELAY + if(!ismovable(target) || !isturf(target.loc)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/mob/pawn_mob = controller.pawn - var/turf/pawn_turf = get_turf(pawn_mob) - pawn_mob.face_atom(target) pawn_mob.balloon_alert_to_viewers("stops and stares...") - set_movement_target(controller, pawn_turf, /datum/ai_movement/complete_stop) + // Returning a long cooldown keeps this leaf RUNNING (and thus the mob standing still) for the stare. + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(controller.blackboard[BB_STATIONARY_MOVE_TO_TARGET]) - addtimer(CALLBACK(src, PROC_REF(set_movement_target), controller, target, initial(controller.ai_movement)), (controller.blackboard[BB_STATIONARY_SECONDS] + 1 SECONDS)) - return AI_BEHAVIOR_DELAY +/datum/bt_node/ai_behavior/stop_and_stare/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + // Forget the cause so we can be spooked fresh next time it wanders into view. + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm index 1e438ea09799..6b1f18e2441c 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm @@ -1,55 +1,55 @@ -/** - * # Targeted Mob Ability - * Attempts to use a mob's cooldown ability on a target - */ -/datum/ai_behavior/targeted_mob_ability +/// Tries to use a specified ability on the current target +/datum/bt_node/ai_behavior/targeted_mob_ability + var/ability_key = BB_GENERIC_ACTION + var/target_key + /// Maximum distance at which the ability can fire (inclusive cuz this is tg :) ) + var/maximum_distance = 0 + ///Does this require adjacency? + var/require_adjacency = FALSE + +/datum/bt_node/ai_behavior/targeted_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags -/datum/ai_behavior/targeted_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) var/datum/action/cooldown/ability = controller.blackboard[ability_key] var/mob/living/target = controller.blackboard[target_key] if(QDELETED(ability) || QDELETED(target)) return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - var/mob/pawn = controller.pawn - pawn.face_atom(target) + if(maximum_distance && get_dist(controller.pawn, target) > maximum_distance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(require_adjacency && !controller.pawn.Adjacent(target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!ability.IsAvailable()) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/mob/pawn_mob = controller.pawn + pawn_mob.face_atom(target) + return start_async() + +/datum/bt_node/ai_behavior/targeted_mob_ability/perform_async(datum/ai_controller/controller) + var/datum/action/cooldown/ability = controller.blackboard[ability_key] + var/atom/target = controller.blackboard[target_key] var/result = ability.Trigger(target = target) - if(result) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - -/** - * # Try Mob Ability and plan execute - * Attempts to use a mob's cooldown ability on a target and then move the target into a special target blackboard datum - * Doesn't need another subtype to clear BB_BASIC_MOB_EXECUTION_TARGET because it will be the target key for the normal action - */ -/datum/ai_behavior/targeted_mob_ability/and_plan_execute - -/datum/ai_behavior/targeted_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) + if(!async_still_valid()) + return + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/// Variant for abilities that require adjacency (distance ≤ 1). +/datum/bt_node/ai_behavior/targeted_mob_ability/melee + require_adjacency = TRUE + + +/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute + +/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded) controller.set_blackboard_key(BB_BASIC_MOB_EXECUTION_TARGET, controller.blackboard[target_key]) return ..() -/** - * # Try Mob Ability and clear target - * Attempts to use a mob's cooldown ability on a target and releases the target when the action completes - */ -/datum/ai_behavior/targeted_mob_ability/and_clear_target +/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target -/datum/ai_behavior/targeted_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) +/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) -/** - * Attempts to move into the provided range and then use a mob's cooldown ability on a target - */ -/datum/ai_behavior/targeted_mob_ability/min_range - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - required_distance = 6 - -/datum/ai_behavior/targeted_mob_ability/min_range/setup(datum/ai_controller/controller, ability_key, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) -/datum/ai_behavior/targeted_mob_ability/min_range/short - required_distance = 3 diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm index ce46d4c1ab98..2fd2e494ef16 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm @@ -5,101 +5,74 @@ GLOBAL_ALIST_EMPTY(hostile_machines_by_z) /// Must be kept up to date with the contents of hostile_machines GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/porta_turret, /obj/vehicle/sealed/mecha))) -/datum/ai_behavior/find_potential_targets - action_cooldown = 2 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// How far can we see stuff? - var/vision_range = 9 + +///Used to find combat targets; Allow finding things hidden in things such as lockers too. +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets + target_source = /datum/target_source/hearers + targeting_strategy = BB_TARGETING_STRATEGY + vision_range = 9 + target_loss_distance = 16 /// Blackboard key for aggro range, uses vision range if not specified var/aggro_range_key = BB_AGGRO_RANGE - /// Range in which we can acquire a new target - var/aggro_grab_range_key = BB_AGGRO_GRAB_RANGE - /// Blackboard key for the target priority strategy + /// Blackboard key holding the hiding-location atom (e.g. closet the target ducked into) + var/hiding_location_key + /// Blackboard key holding the /datum/target_priority_strategy typepath for selection var/priority_strategy_key = BB_TARGET_PRIORITY_STRATEGY /// If we have a priority strategy set, how often do we refresh our target search? var/priority_refresh_cooldown = 6 SECONDS -/datum/ai_behavior/find_potential_targets/get_cooldown(datum/ai_controller/cooldown_for) - if(cooldown_for.blackboard[BB_FIND_TARGETS_FIELD(type)]) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/get_cooldown(datum/ai_controller/controller) + if(controller.blackboard[BB_FIND_TARGETS_FIELD(type)]) return 60 SECONDS return ..() -/datum/ai_behavior/find_potential_targets/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/mob/living/living_mob = controller.pawn - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - - if(!targeting_strategy) - CRASH("No target datum was supplied in the blackboard for [controller.pawn]") +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/can_search(datum/ai_controller/controller) + return !(controller.blackboard[BB_FIND_TARGETS_FIELD(type)]) - var/atom/current_target = controller.blackboard[target_key] +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/should_keep_target(datum/ai_controller/controller, datum/targeting_strategy/strategy, atom/current_target) + if(!current_target) + return FALSE + if(!strategy.is_valid_target(controller.pawn, current_target, vision_range)) + return FALSE var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[priority_strategy_key]) - if((!priority_strategy || controller.blackboard[BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN] > world.time) && current_target && targeting_strategy.can_attack(living_mob, current_target, vision_range)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/aggro_range = vision_range - if(isnull(current_target) && !isnull(controller.blackboard[aggro_grab_range_key])) - aggro_range = controller.blackboard[aggro_grab_range_key] - else if(!isnull(controller.blackboard[aggro_range_key])) - aggro_range = controller.blackboard[aggro_range_key] - - controller.clear_blackboard_key(target_key) - - // If we're using a field rn, just don't do anything yeah? - if(controller.blackboard[BB_FIND_TARGETS_FIELD(type)]) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/list/potential_targets = hearers(aggro_range, get_turf(controller.pawn)) - living_mob //Remove self, so we don't suicide - - var/turf/mob_turf = get_turf(living_mob) - if(mob_turf?.z) - for (var/atom/hostile_machine as anything in GLOB.hostile_machines_by_z[mob_turf.z]) - if (can_see(living_mob, hostile_machine, aggro_range)) - potential_targets += hostile_machine - - if(!potential_targets.len) - if(!current_target) - failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/list/filtered_targets = list() - var/current_priority = 0 - if(priority_strategy) - current_priority = priority_strategy.get_target_priority(controller, current_target) - - for(var/atom/pot_target in potential_targets) - if(!targeting_strategy.can_attack(living_mob, pot_target)) + if(!priority_strategy) + return TRUE + return controller.blackboard[BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN] > world.time + +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/on_no_candidates(datum/ai_controller/controller, atom/current_target, datum/targeting_strategy/strategy, range) + if(current_target && strategy.can_keep_target(controller.pawn, current_target, target_loss_distance)) + return list(current_target) + if(!current_target) + failed_to_find_anyone(controller, target_key, targeting_strategy, hiding_location_key) + return list() + +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/on_no_valid_candidates(datum/ai_controller/controller, atom/current_target) + if(!current_target) + failed_to_find_anyone(controller, target_key, targeting_strategy, hiding_location_key) + +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/filter_candidates(datum/ai_controller/controller, list/candidates, datum/targeting_strategy/strategy, atom/current_target) + var/mob/living/pawn = controller.pawn + var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[priority_strategy_key]) + var/current_priority = priority_strategy ? priority_strategy.get_target_priority(controller, current_target) : 0 + var/list/filtered = list() + for(var/atom/candidate as anything in candidates) + if(!strategy.is_valid_target(pawn, candidate, vision_range, controller)) continue - if (priority_strategy && priority_strategy.get_target_priority(controller, pot_target) < current_priority) + if(priority_strategy && priority_strategy.get_target_priority(controller, candidate) < current_priority) continue - filtered_targets += pot_target - - if(!filtered_targets.len) - if(!current_target) - failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + filtered += candidate + return filtered - var/atom/target = pick_final_target(controller, filtered_targets) - - EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [target] as a target for blackboard key [target_key]! Behavior: [src]", get_turf(target), "Target: [target]") - EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(target)) - - controller.set_blackboard_key(target_key, target) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) controller.set_blackboard_key(BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN, world.time + priority_refresh_cooldown) - - var/atom/potential_hiding_location = targeting_strategy.find_hidden_mobs(living_mob, target) - - if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially. + var/atom/potential_hiding_location = strategy.find_hidden_mobs(controller.pawn, target) + if(potential_hiding_location) controller.set_blackboard_key(hiding_location_key, potential_hiding_location) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_potential_targets/proc/failed_to_find_anyone(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/aggro_range = vision_range - if(!isnull(controller.blackboard[aggro_grab_range_key])) - aggro_range = controller.blackboard[aggro_grab_range_key] - else if(!isnull(controller.blackboard[aggro_range_key])) - aggro_range = controller.blackboard[aggro_range_key] - +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/proc/failed_to_find_anyone(datum/ai_controller/controller, target_key, targeting_strategy, hiding_location_key) + // TEMP DISABLED nuke this if performance improves + /* + var/aggro_range = controller.blackboard[aggro_range_key] || vision_range // takes the larger between our range() input and our implicit hearers() input (world.view) aggro_range = max(aggro_range, ROUND_UP(max(getviewsize(world.view)) / 2)) // Alright, here's the interesting bit @@ -112,13 +85,16 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ src, controller, target_key, - targeting_strategy_key, + targeting_strategy, hiding_location_key, ) // We're gonna store this field in our blackboard, so we can clear it away if we end up finishing successsfully controller.set_blackboard_key(BB_FIND_TARGETS_FIELD(type), detection_field) + */ + controller.clear_blackboard_key(target_key) + -/datum/ai_behavior/find_potential_targets/proc/new_turf_found(turf/found, datum/ai_controller/controller, datum/targeting_strategy/strategy) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/proc/new_turf_found(turf/found, datum/ai_controller/controller, datum/targeting_strategy/strategy) var/valid_found = FALSE var/mob/pawn = controller.pawn for(var/maybe_target in found) @@ -126,7 +102,7 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ continue if(!is_type_in_typecache(maybe_target, GLOB.target_interested_atoms)) continue - if(!strategy.can_attack(pawn, maybe_target)) + if(!strategy.is_valid_target(pawn, maybe_target)) continue valid_found = TRUE break @@ -136,18 +112,18 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ var/datum/proximity_monitor/field = controller.blackboard[BB_FIND_TARGETS_FIELD(type)] qdel(field) // autoclears so it's fine // Fire instantly, you should find something I hope - controller.modify_cooldown(src, world.time) + modify_cooldown(world.time) -/datum/ai_behavior/find_potential_targets/proc/atom_allowed(atom/movable/checking, datum/targeting_strategy/strategy, mob/pawn) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/proc/atom_allowed(atom/movable/checking, datum/targeting_strategy/strategy, mob/pawn) if(checking == pawn) return FALSE if(!ismob(checking) && !is_type_in_typecache(checking, GLOB.target_interested_atoms)) return FALSE - if(!strategy.can_attack(pawn, checking)) + if(!strategy.is_valid_target(pawn, checking)) return FALSE return TRUE -/datum/ai_behavior/find_potential_targets/proc/new_atoms_found(list/atom/movable/found, datum/ai_controller/controller, target_key, datum/targeting_strategy/strategy, hiding_location_key) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/proc/new_atoms_found(list/atom/movable/found, datum/ai_controller/controller, target_key, datum/targeting_strategy/strategy, hiding_location_key) var/mob/pawn = controller.pawn var/list/accepted_targets = list() for(var/maybe_target in found) @@ -156,7 +132,7 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ // Need to better handle viewers here if(!ismob(maybe_target) && !is_type_in_typecache(maybe_target, GLOB.target_interested_atoms)) continue - if(!strategy.can_attack(pawn, maybe_target)) + if(!strategy.is_valid_target(pawn, maybe_target)) continue accepted_targets += maybe_target @@ -165,36 +141,46 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [target] as a target for blackboard key [target_key]! Behavior: [src]", get_turf(target), "Target: [target]") EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(target)) controller.set_blackboard_key(target_key, target) - - var/atom/potential_hiding_location = strategy.find_hidden_mobs(pawn, target) - - if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially. - controller.set_blackboard_key(hiding_location_key, potential_hiding_location) + on_target_found(controller, target, strategy) finish_action(controller, succeeded = TRUE) -/datum/ai_behavior/find_potential_targets/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/finish_action(datum/ai_controller/controller, succeeded) . = ..() if (succeeded) var/datum/proximity_monitor/field = controller.blackboard[BB_FIND_TARGETS_FIELD(type)] qdel(field) // autoclears so it's fine - controller.CancelActions() // On retarget cancel any further queued actions so that they will setup again with new target - controller.modify_cooldown(src, get_cooldown(controller)) + modify_cooldown(get_cooldown(controller)) -/// Returns the desired final target from the filtered list of targets -/datum/ai_behavior/find_potential_targets/proc/pick_final_target(datum/ai_controller/controller, list/filtered_targets) +/// Picks the final target, preferring higher-priority candidates when a priority strategy is set. +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/pick_final_target(datum/ai_controller/controller, list/filtered_targets) var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[priority_strategy_key]) - if (!priority_strategy) - return pick(filtered_targets) + if(!priority_strategy) + return filtered_targets[1] return priority_strategy.select_target(controller, filtered_targets) -/// Targets with the trait specified by the BB_TARGET_PRIORITY_TRAIT blackboard key will be prioritized over the rest. -/datum/ai_behavior/find_potential_targets/prioritize_trait +/// Picks targets based on which one has the lowest health. +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded + +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/list/living_targets = list() + for(var/mob/living/living_target in filtered_targets) + living_targets += living_target + if(living_targets.len) + sortTim(living_targets, GLOBAL_PROC_REF(cmp_mob_health)) + return living_targets[living_targets.len] + return ..() + +/// Prioritizes targets carrying the trait named by our trait_key blackboard key over the rest. +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait + /// Blackboard key holding the trait that marks a target as high-priority. + var/trait_key = BB_TARGET_PRIORITY_TRAIT -/datum/ai_behavior/find_potential_targets/prioritize_trait/pick_final_target(datum/ai_controller/controller, list/filtered_targets) - var/priority_targets = list() +/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/list/priority_targets = list() + var/priority_trait = controller.blackboard[trait_key] for(var/atom/target as anything in filtered_targets) - if(HAS_TRAIT(target, controller.blackboard[BB_TARGET_PRIORITY_TRAIT])) + if(HAS_TRAIT(target, priority_trait)) priority_targets += target if(length(priority_targets)) return ..(controller, priority_targets) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm index c734d961b5ef..288276b358c3 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm @@ -1,15 +1,13 @@ +///A tipped-over mob looks to a nearby person for help, then resigns itself to its fate. +/datum/bt_node/ai_behavior/tipped_reaction + /// Blackboard key holding the mob that tipped us over. + var/tipper_key = BB_BASIC_MOB_TIPPER + /// Blackboard key holding whether we are still reacting to being tipped. + var/reacting_key = BB_BASIC_MOB_TIP_REACTING -///type of tipped reaction that is akin to puppy dog eyes -/datum/ai_behavior/tipped_reaction - -/datum/ai_behavior/tipped_reaction/perform(seconds_per_tick, datum/ai_controller/controller, tipper_key, reacting_key) +/datum/bt_node/ai_behavior/tipped_reaction/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/carbon/tipper = controller.blackboard[tipper_key] - // visible part of the visible message - var/seen_message = "" - // self part of the visible message - var/self_message = "" - // the mob we're looking to for aid var/mob/living/carbon/savior // look for someone in a radius around us for help. If our original tipper is in range, prioritize them for(var/mob/living/carbon/potential_aid in oview(3, get_turf(controller.pawn))) @@ -18,6 +16,8 @@ break savior = potential_aid + var/seen_message + var/self_message if(prob(75) && savior) var/text = pick("imploringly", "pleadingly", "with a resigned expression") seen_message = "[controller.pawn] looks at [savior] [text]." @@ -28,7 +28,7 @@ controller.pawn.visible_message(span_notice("[seen_message]"), span_notice("[self_message]")) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/tipped_reaction/finish_action(datum/ai_controller/controller, succeeded, tipper_key, reacting_key) +/datum/bt_node/ai_behavior/tipped_reaction/finish_action(datum/ai_controller/controller, succeeded) . = ..() //I'VE SAID MY PEACE... controller.set_blackboard_key(reacting_key, FALSE) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm deleted file mode 100644 index f78eb3db4ec0..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/travel_towards.dm +++ /dev/null @@ -1,53 +0,0 @@ -/** - * # Travel Towards - * Moves towards the atom in the passed blackboard key. - * Planning continues during this action so it can be interrupted by higher priority actions. - */ -/datum/ai_behavior/travel_towards - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// If true we will get rid of our target on completion - var/clear_target = FALSE - ///should we use a different movement type? - var/new_movement_type - -/datum/ai_behavior/travel_towards/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target, new_movement_type) - -/datum/ai_behavior/travel_towards/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/travel_towards/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if (clear_target) - controller.clear_blackboard_key(target_key) - if(new_movement_type) - controller.change_ai_movement_type(initial(controller.ai_movement)) - -/datum/ai_behavior/travel_towards/stop_on_arrival - clear_target = TRUE - -/datum/ai_behavior/travel_towards/adjacent - required_distance = 1 - -/** - * # Travel Towards Atom - * Travel towards an atom you pass directly from the controller rather than a blackboard key. - * You might need to do this to avoid repeating some checks in both a controller and an action. - */ -/datum/ai_behavior/travel_towards_atom - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - -/datum/ai_behavior/travel_towards_atom/setup(datum/ai_controller/controller, atom/target_atom) - . = ..() - if(isnull(target_atom)) - return FALSE - set_movement_target(controller, target_atom) - -/datum/ai_behavior/travel_towards_atom/perform(seconds_per_tick, datum/ai_controller/controller, atom/target_atom) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm deleted file mode 100644 index 34e651f8e528..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm +++ /dev/null @@ -1,11 +0,0 @@ -/datum/ai_behavior/unbuckle_mob - -/datum/ai_behavior/unbuckle_mob/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - var/atom/movable/buckled_to = living_pawn.buckled - - if(isnull(buckled_to)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - buckled_to.unbuckle_mob(living_pawn) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/use_mob_ability.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/use_mob_ability.dm new file mode 100644 index 000000000000..847a54758dfd --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/use_mob_ability.dm @@ -0,0 +1,39 @@ +/// Triggers a mob ability stored in a blackboard key. Returns INSTANT SUCCESS if triggered, INSTANT FAILURE if unavailable or trigger fails. +/datum/bt_node/ai_behavior/use_mob_ability + var/ability_key = BB_GENERIC_ACTION + +/datum/bt_node/ai_behavior/use_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/datum/action/using_action = get_valid_ability(controller) + if(QDELETED(using_action)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return start_async() + +/// Returns the action to trigger, or null if it isn't available. Override to prep the action before it fires. +/datum/bt_node/ai_behavior/use_mob_ability/proc/get_valid_ability(datum/ai_controller/controller) + var/datum/action/using_action = controller.blackboard[ability_key] + if(QDELETED(using_action) || !using_action.IsAvailable()) + return null + return using_action + +/datum/bt_node/ai_behavior/use_mob_ability/perform_async(datum/ai_controller/controller) + var/datum/action/using_action = controller.blackboard[ability_key] + var/result = using_action.Trigger() + if(!async_still_valid()) + return + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/// Triggers a shapeshift ability, picking a random shape if none has been selected yet (AI can't use context wheels). +/datum/bt_node/ai_behavior/use_mob_ability/shapeshift + ability_key = BB_SHAPESHIFT_ACTION + +/datum/bt_node/ai_behavior/use_mob_ability/shapeshift/get_valid_ability(datum/ai_controller/controller) + var/datum/action/cooldown/spell/shapeshift/using_action = ..() + if(QDELETED(using_action)) + return null + if(isnull(using_action.shapeshift_type)) + using_action.shapeshift_type = pick(using_action.possible_shapes) + return using_action diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm index 06fcfa147262..7e1f3a763724 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm @@ -1,143 +1,190 @@ -/// We hop into the vents through a vent outlet, and then crawl around a bit. Jolly good times. -/// This also assumes that we are on the turf that the vent outlet is on. If it isn't, shit. +///uhm...sus? +/datum/bt_node/subtree/consider_venting + behavior_tree_json = "code/datums/ai/basic_mobs/basic_ai_behaviors/consider_venting.bt.json" -/// Warning: this was really snowflake code lifted from an obscure feature that likely has not been touched for over five years years. -/// Something that isn't implemented is the ability to actually crawl through vents ourselves because I think that's just a waste of time for the same effect (instead of psuedo-teleportation, do REAL forceMoving) -/// If you are seriously considering using this component, it would be a great idea to extend this proc to be more versatile/less overpowered - the mobs that currently implement this benefit the most -/// since they are weak as shit with only five health. Up to you though, don't take what's written here as gospel. -/datum/ai_behavior/crawl_through_vents - action_cooldown = 10 SECONDS -/datum/ai_behavior/crawl_through_vents/get_cooldown(datum/ai_controller/cooldown_for) - return cooldown_for.blackboard[BB_VENTCRAWL_COOLDOWN] || initial(action_cooldown) +/// Enters a vent stored in entry_vent_key. Sets BB_EXIT_VENT_TARGET and BB_VENT_ENTRY_TIME on success. +/datum/bt_node/ai_behavior/enter_vent + var/entry_vent_key = BB_ENTRY_VENT_TARGET + /// TRUE while the async crawl-in is running. perform() holds at DELAY until it resolves. + var/is_starting_crawl = FALSE + /// Set by the async action when the crawl finished but we did not end up in the vent. + var/failed_ventcrawl = FALSE -/datum/ai_behavior/crawl_through_vents/setup(datum/ai_controller/controller, target_key) - . = ..() - var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET] - return istype(target) && isliving(controller.pawn) // only mobs can vent crawl in the current framework - -/datum/ai_behavior/crawl_through_vents/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET] +/datum/bt_node/ai_behavior/enter_vent/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/cached_pawn = controller.pawn - if(HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING) || !controller.blackboard[BB_CURRENTLY_TARGETING_VENT] || !is_vent_valid(entry_vent)) - return AI_BEHAVIOR_DELAY - - if(!cached_pawn.can_enter_vent(entry_vent, provide_feedback = FALSE)) // we're an AI we scoff at feedback - // "never enter a hole you can't get out of" - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/vent_we_exit_out_of = calculate_exit_vent(controller, target_key) - if(isnull(vent_we_exit_out_of)) // don't get into the vents if we can't get out of them, that's SILLY. - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + // We kicked off the crawl on a previous tick; report its result once it resolves. Flags reset in finish_action. + if(failed_ventcrawl) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(is_starting_crawl) + if(!HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) + return AI_BEHAVIOR_DELAY // still climbing in + controller.set_blackboard_key(BB_VENT_ENTRY_TIME, world.time) + if(prob(50)) + cached_pawn.visible_message( + span_warning("[cached_pawn] scrambles into the ventilation ducts!"), + span_hear("You hear something scampering through the ventilation ducts."), + ) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + if(HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent = controller.blackboard[entry_vent_key] + if(!is_vent_valid(entry_vent) || !isliving(cached_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(!cached_pawn.can_enter_vent(entry_vent, provide_feedback = FALSE)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent = calculate_exit_vent(controller) + if(isnull(exit_vent)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(BB_EXIT_VENT_TARGET, exit_vent) + is_starting_crawl = TRUE + INVOKE_ASYNC(src, PROC_REF(perform_ventcrawl_action), controller, entry_vent) + return AI_BEHAVIOR_DELAY - controller.set_blackboard_key(BB_CURRENTLY_TARGETING_VENT, FALSE) // must be done here because we have a do_after sleep in handle_ventcrawl unfortunately and double dipping could lead to erroneous suicide pill calls. +/// Runs the sleeping ventcrawl off the tick. Flags failure if we didn't end up in the vent, so perform() never has to sleep. +/datum/bt_node/ai_behavior/enter_vent/proc/perform_ventcrawl_action(datum/ai_controller/controller, obj/machinery/atmospherics/components/unary/vent_pump/entry_vent) + var/mob/living/cached_pawn = controller.pawn cached_pawn.handle_ventcrawl(entry_vent) if(!HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) //something failed and we ARE NOT IN THE VENT even though the earlier check said we were good to go! odd. - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + failed_ventcrawl = TRUE - controller.set_blackboard_key(BB_EXIT_VENT_TARGET, vent_we_exit_out_of) +/datum/bt_node/ai_behavior/enter_vent/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + is_starting_crawl = FALSE + failed_ventcrawl = FALSE + if(!succeeded) + controller.clear_blackboard_key(entry_vent_key) + +/// Returns TRUE if the vent exists and isn't welded shut. +/datum/bt_node/ai_behavior/enter_vent/proc/is_vent_valid(obj/machinery/atmospherics/components/unary/vent_pump/vent) + return !QDELETED(vent) && !vent.welded + +/// Picks a random valid vent on the same pipeline as the entry vent. Falls back to the entry vent itself; returns null if nothing is usable. +/datum/bt_node/ai_behavior/enter_vent/proc/calculate_exit_vent(datum/ai_controller/controller) + var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent = controller.blackboard[entry_vent_key] + if(QDELETED(entry_vent)) + return null - if(prob(50)) - cached_pawn.visible_message( - span_warning("[src] scrambles into the ventilation ducts!"), - span_hear("You hear something scampering through the ventilation ducts."), - ) + var/datum/pipeline/parent_pipe = entry_vent.parents[1] + var/list/candidates = list() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent in parent_pipe.other_atmos_machines) + if(is_vent_valid(vent)) + candidates += vent - var/lower_vent_time_limit = controller.blackboard[BB_LOWER_VENT_TIME_LIMIT] // the least amount of time we spend in the vents - var/upper_vent_time_limit = controller.blackboard[BB_UPPER_VENT_TIME_LIMIT] // the most amount of time we spend in the vents + if(length(candidates)) + return pick(candidates) - addtimer(CALLBACK(src, PROC_REF(exit_the_vents), controller), rand(lower_vent_time_limit, upper_vent_time_limit)) - controller.set_blackboard_key(BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID, addtimer(CALLBACK(src, PROC_REF(delayed_suicide_pill), controller, target_key), controller.blackboard[BB_TIME_TO_GIVE_UP_ON_VENT_PATHING], TIMER_STOPPABLE)) - return AI_BEHAVIOR_DELAY + if(is_vent_valid(entry_vent)) + return null // in the pipeline already; let the caller handle it -/// Figure out an exit vent that we should head towards. If we don't have one, default to the entry vent. If they're all kaput, we die. -/datum/ai_behavior/crawl_through_vents/proc/calculate_exit_vent(datum/ai_controller/controller, target_key) - var/obj/machinery/atmospherics/components/unary/vent_pump/returnable_vent - var/obj/machinery/atmospherics/components/unary/vent_pump/vent_we_entered_through = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET] + return entry_vent - var/datum/pipeline/entry_vent_parent = vent_we_entered_through.parents[1] - var/list/potential_exits = list() - for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent in entry_vent_parent.other_atmos_machines) - if(is_vent_valid(vent)) - potential_exits.Add(vent) +/// Waits inside a vent for a randomised duration, then exits. Handles the give-up timeout. +/datum/bt_node/ai_behavior/exit_vent + time_between_perform = 1 SECONDS + var/target_exit_time = 0 + /// TRUE while the async crawl-out is running. perform() holds at DELAY until it resolves. + var/is_exiting_crawl = FALSE + /// Set by the async action when the crawl finished but we are somehow still in the vent. + var/failed_ventcrawl = FALSE - if(length(potential_exits)) - returnable_vent = pick(potential_exits) - return returnable_vent +/datum/bt_node/ai_behavior/exit_vent/setup(datum/ai_controller/controller) + . = ..() + var/lower = controller.blackboard[BB_LOWER_VENT_TIME_LIMIT] + var/upper = controller.blackboard[BB_UPPER_VENT_TIME_LIMIT] + var/entry_time = controller.blackboard[BB_VENT_ENTRY_TIME] || world.time + target_exit_time = entry_time + rand(lower, upper) + return TRUE - // if we're here, we're in "what the flarp" mode... okay maybe we can default to the vent we entered in. - returnable_vent = vent_we_entered_through - if(is_vent_valid(vent_we_entered_through)) - // AH WHAT THE FUCK. okay, maybe we're not inside the vents yet? let's return null and we can pick up on that based on the wider context of the proc that invokes it. - return null +/datum/bt_node/ai_behavior/exit_vent/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/cached_pawn = controller.pawn - return returnable_vent // we return null in case something yonked between then and now so it's all good man + // We kicked off the crawl-out on a previous tick; report its result once it resolves. Flags reset in finish_action. + if(failed_ventcrawl) + return suicide_pill(cached_pawn) + if(is_exiting_crawl) + if(HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) + return AI_BEHAVIOR_DELAY // still climbing out + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + if(world.time < target_exit_time) + var/give_up = controller.blackboard[BB_TIME_TO_GIVE_UP_ON_VENT_PATHING] + var/entry_time = controller.blackboard[BB_VENT_ENTRY_TIME] + if(give_up && entry_time && world.time > entry_time + give_up) + return suicide_pill(cached_pawn) + return AI_BEHAVIOR_DELAY -/// We've had enough horsing around in the vents, it's time to get out. -/datum/ai_behavior/crawl_through_vents/proc/exit_the_vents(datum/ai_controller/controller, target_key) - var/obj/machinery/atmospherics/components/unary/vent_pump/emergency_vent // vent we will scramble to search for in case plan A is a bust (exit vent) var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent = controller.blackboard[BB_EXIT_VENT_TARGET] - var/mob/living/living_pawn = controller.pawn - - if(!HAS_TRAIT(living_pawn, TRAIT_MOVE_VENTCRAWLING) && isturf(get_turf(living_pawn))) // we're out of the vents, so no need to do an exit - // assume that we got yeeted out somehow and return this so we can halt the suicide pill timer. - finish_action(controller, TRUE, target_key) - return - - living_pawn.forceMove(exit_vent) - if(!living_pawn.can_enter_vent(exit_vent, provide_feedback = FALSE)) - // oh shit, something happened while we were waiting on that timer. let's figure out a different way to get out of here. - emergency_vent = calculate_exit_vent(controller) - if(isnull(emergency_vent)) - // it's joever. we cooked too hard. - suicide_pill(controller) - return - - controller.set_blackboard_key(BB_EXIT_VENT_TARGET, emergency_vent) // assign and go again - addtimer(CALLBACK(src, PROC_REF(exit_the_vents), controller), (rand(controller.blackboard[BB_LOWER_VENT_TIME_LIMIT], controller.blackboard[BB_UPPER_VENT_TIME_LIMIT]) / 2)) // we're in danger mode, so scurry out at half the time it would normally take. - return - - living_pawn.handle_ventcrawl(exit_vent) - if(HAS_TRAIT(living_pawn, TRAIT_MOVE_VENTCRAWLING)) // how'd we fail? what the fuck - stack_trace("We failed to exit the vents, even though we should have been fine? This is very weird.") - suicide_pill(controller) - return + if(!is_vent_valid(exit_vent)) + exit_vent = calculate_exit_vent(controller) + if(isnull(exit_vent)) + return suicide_pill(cached_pawn) + controller.set_blackboard_key(BB_EXIT_VENT_TARGET, exit_vent) + + cached_pawn.forceMove(exit_vent) + if(!cached_pawn.can_enter_vent(exit_vent, provide_feedback = FALSE)) + // vent became unusable while we waited; try an emergency exit next tick + var/emergency = calculate_exit_vent(controller) + if(isnull(emergency)) + return suicide_pill(cached_pawn) + controller.set_blackboard_key(BB_EXIT_VENT_TARGET, emergency) + target_exit_time = world.time // retry immediately next tick + return AI_BEHAVIOR_DELAY - finish_action(controller, TRUE, target_key) - return + is_exiting_crawl = TRUE + INVOKE_ASYNC(src, PROC_REF(perform_ventcrawl_action), controller, exit_vent) + return AI_BEHAVIOR_DELAY -/// Incredibly stripped down version of the overarching `can_enter_vent` proc on `/mob, just meant for rapid rechecking of a vent. Will be TRUE if not blocked, FALSE otherwise. -/datum/ai_behavior/crawl_through_vents/proc/is_vent_valid(obj/machinery/atmospherics/components/unary/vent_pump/checkable) - return !QDELETED(checkable) && !checkable.welded +/// Runs the sleeping ventcrawl off the tick. Flags failure if we're somehow still in the vent, so perform() never has to sleep. +/datum/bt_node/ai_behavior/exit_vent/proc/perform_ventcrawl_action(datum/ai_controller/controller, obj/machinery/atmospherics/components/unary/vent_pump/exit_vent) + var/mob/living/cached_pawn = controller.pawn + cached_pawn.handle_ventcrawl(exit_vent) + if(HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) + stack_trace("[cached_pawn] [type]: exited vent but still has TRAIT_MOVE_VENTCRAWLING") + failed_ventcrawl = TRUE -/// Wraps a delayed defeat, so we gotta handle the return value properly ya feel? -/datum/ai_behavior/crawl_through_vents/proc/delayed_suicide_pill(datum/ai_controller/controller, target_key) - if(suicide_pill(controller) & AI_BEHAVIOR_FAILED) - finish_action(controller, FALSE, target_key) +/datum/bt_node/ai_behavior/exit_vent/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + is_exiting_crawl = FALSE + failed_ventcrawl = FALSE + controller.clear_blackboard_key(BB_VENT_ENTRY_TIME) + controller.clear_blackboard_key(BB_EXIT_VENT_TARGET) + controller.clear_blackboard_key(BB_ENTRY_VENT_TARGET) -/// Aw fuck, we may have been bested somehow. Regardless of what we do, we can't exit through a vent! Let's end our misery and prevent useless endless calculations. -/datum/ai_behavior/crawl_through_vents/proc/suicide_pill(datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn +/// Kills the pawn if it has no client, then returns INSTANT FAILED. +/datum/bt_node/ai_behavior/exit_vent/proc/suicide_pill(mob/living/pawn) + if(istype(pawn) && isnull(pawn.client)) + pawn.death(TRUE) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - if(istype(living_pawn)) - if(isnull(living_pawn.client)) // only call death if we don't have a client because maybe their natural intelligence can pick up where our AI calculations have failed - living_pawn.death(TRUE) // call gibbed as true because we are never coming back it is so fucking joever +/// Returns TRUE if the vent exists and isn't welded shut. +/datum/bt_node/ai_behavior/exit_vent/proc/is_vent_valid(obj/machinery/atmospherics/components/unary/vent_pump/vent) + return !QDELETED(vent) && !vent.welded - return AI_BEHAVIOR_FAILED +/// Picks a random valid vent on the same pipeline as BB_ENTRY_VENT_TARGET. Returns null if nothing is usable. +/datum/bt_node/ai_behavior/exit_vent/proc/calculate_exit_vent(datum/ai_controller/controller) + var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent = controller.blackboard[BB_ENTRY_VENT_TARGET] + if(QDELETED(entry_vent)) + return null - if(QDELETED(living_pawn)) // we got deleted by some other means, just presume the action is a wash and get outta here - return NONE + var/datum/pipeline/parent_pipe = entry_vent.parents[1] + var/list/candidates = list() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent in parent_pipe.other_atmos_machines) + if(is_vent_valid(vent)) + candidates += vent - qdel(living_pawn) // failover, we really should've been caught in the istype() but lets just bow out of existing at this point - return NONE + if(length(candidates)) + return pick(candidates) -/datum/ai_behavior/crawl_through_vents/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() + if(is_vent_valid(entry_vent)) + return null - deltimer(controller.blackboard[BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID]) - controller.clear_blackboard_key(target_key) - controller.clear_blackboard_key(BB_ENTRY_VENT_TARGET) - controller.clear_blackboard_key(BB_EXIT_VENT_TARGET) - controller.set_blackboard_key(BB_CURRENTLY_TARGETING_VENT, FALSE) // just in case + return entry_vent diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targeting.dm deleted file mode 100644 index 46037fdc076e..000000000000 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targeting.dm +++ /dev/null @@ -1,11 +0,0 @@ -/// Picks targets based on which one has the lowest health -/datum/ai_behavior/find_potential_targets/most_wounded - -/datum/ai_behavior/find_potential_targets/most_wounded/pick_final_target(datum/ai_controller/controller, list/filtered_targets) - var/list/living_targets = list() - for(var/mob/living/living_target in filtered_targets) - living_targets += filtered_targets - if(living_targets.len) - sortTim(living_targets, GLOBAL_PROC_REF(cmp_mob_health)) - return pop(living_targets) - return ..() diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/write_on_paper.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/write_on_paper.dm index 51826676e9e8..e5dab148bd56 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/write_on_paper.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/write_on_paper.dm @@ -1,15 +1,23 @@ -/datum/ai_behavior/write_on_paper +/// Scrawls a random line from the writing list onto the carried paper, then drops it. Clears the carry key on finish. +/datum/bt_node/ai_behavior/write_on_paper + /// Blackboard key holding the paper to write on (also the virtual carry slot). + var/paper_key + /// Blackboard key holding the list of phrases to choose from. + var/writing_list_key -/datum/ai_behavior/write_on_paper/perform(seconds_per_tick, datum/ai_controller/controller, found_paper, list_of_writings) +/datum/bt_node/ai_behavior/write_on_paper/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/wizard = controller.pawn - var/list/writing_list = controller.blackboard[list_of_writings] - var/obj/item/paper/target = controller.blackboard[found_paper] + var/obj/item/paper/target = controller.blackboard[paper_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/list/writing_list = controller.blackboard[writing_list_key] if(length(writing_list)) target.add_raw_text(pick(writing_list)) target.update_appearance() - wizard.dropItemToGround(target) + if(target.loc == wizard) + target.forceMove(get_turf(wizard)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/write_on_paper/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/write_on_paper/finish_action(datum/ai_controller/controller, succeeded) . = ..() - controller.clear_blackboard_key(target_key) + controller.clear_blackboard_key(paper_key) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm deleted file mode 100644 index 98cf1654f713..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm +++ /dev/null @@ -1,35 +0,0 @@ -/// Attack something which is already adjacent to us, without ending planning -/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/opportunistic - end_planning = FALSE - -/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target) || !controller.pawn.Adjacent(target)) - return - if (isliving(controller.pawn)) - var/mob/living/pawn = controller.pawn - if (LAZYLEN(pawn.do_afters)) - return - controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - -/// Attack something which is already adjacent to us without moving -/datum/ai_behavior/basic_melee_attack/opportunistic - action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/basic_melee_attack/opportunistic/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - if (!controller.blackboard_key_exists(targeting_strategy_key)) - CRASH("No target datum was supplied in the blackboard for [controller.pawn]") - return controller.blackboard_key_exists(target_key) - -/datum/ai_behavior/basic_melee_attack/opportunistic/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/atom/movable/atom_pawn = controller.pawn - var/atom/atom_target = controller.blackboard[target_key] - if (QDELETED(atom_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(!atom_target.IsReachableBy(atom_pawn)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - . = ..() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm deleted file mode 100644 index bc8efdeb4ff5..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm +++ /dev/null @@ -1,85 +0,0 @@ -/// If there's something between us and our target then we need to queue a behaviour to make it not be there -/datum/ai_planning_subtree/attack_obstacle_in_path - /// Blackboard key containing current target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// The action to execute, extend to add a different cooldown or something - var/attack_behaviour = /datum/ai_behavior/attack_obstructions - -/datum/ai_planning_subtree/attack_obstacle_in_path/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return - - var/turf/next_step = get_step_towards(controller.pawn, target) - if (!next_step.is_blocked_turf(exclude_mobs = TRUE, source_atom = controller.pawn)) - return - - controller.queue_behavior(attack_behaviour, target_key) - // Don't cancel future planning, maybe we can move now - -/// Something is in our way, get it outta here -/datum/ai_behavior/attack_obstructions - action_cooldown = 2 SECONDS - /// If we should attack walls, be prepared for complaints about breaches - var/can_attack_turfs = FALSE - /// For if you want your mob to be able to attack dense objects - var/can_attack_dense_objects = FALSE - -/datum/ai_behavior/attack_obstructions/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/basic/basic_mob = controller.pawn - var/atom/target = controller.blackboard[target_key] - - if (QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/turf/next_step = get_step_towards(basic_mob, target) - var/dir_to_next_step = get_dir(basic_mob, next_step) - // If moving diagonally we need to punch both ways, or more accurately the one we are blocked in - var/list/dirs_to_move = list() - if (ISDIAGONALDIR(dir_to_next_step)) - for(var/direction in GLOB.cardinals) - if(direction & dir_to_next_step) - dirs_to_move += direction - else - dirs_to_move += dir_to_next_step - - for (var/direction in dirs_to_move) - if (attack_in_direction(controller, basic_mob, direction)) - return AI_BEHAVIOR_DELAY - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/attack_obstructions/proc/attack_in_direction(datum/ai_controller/controller, mob/living/basic/basic_mob, direction) - var/turf/next_step = get_step(basic_mob, direction) - if (!next_step.is_blocked_turf(exclude_mobs = TRUE, source_atom = controller.pawn)) - return FALSE - - for (var/obj/object as anything in next_step.contents) - if (!can_smash_object(basic_mob, object)) - continue - basic_mob.melee_attack(object) - return TRUE - - if (can_attack_turfs) - basic_mob.melee_attack(next_step) - return TRUE - return FALSE - -/datum/ai_behavior/attack_obstructions/proc/can_smash_object(mob/living/basic/basic_mob, obj/object) - if (!object.density && !can_attack_dense_objects) - return FALSE - if (object.IsObscured()) - return FALSE - if (basic_mob.see_invisible < object.invisibility) - return FALSE - var/list/whitelist = basic_mob.ai_controller.blackboard[BB_OBSTACLE_TARGETING_WHITELIST] - if(whitelist && !is_type_in_typecache(object, whitelist)) - return FALSE - - return TRUE // It's in our way, let's get it out of our way - -/datum/ai_planning_subtree/attack_obstacle_in_path/low_priority_target - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - -/datum/ai_planning_subtree/attack_obstacle_in_path/pet_target - target_key = BB_CURRENT_PET_TARGET diff --git a/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm b/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm deleted file mode 100644 index 46886680d528..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/call_reinforcements.dm +++ /dev/null @@ -1,68 +0,0 @@ -/// Calls all nearby mobs that share a faction to give backup in combat -/datum/ai_planning_subtree/call_reinforcements - /// Blackboard key containing something to say when calling reinforcements (takes precedence over emotes) - var/say_key = BB_REINFORCEMENTS_SAY - /// Blackboard key containing an emote to perform when calling reinforcements - var/emote_key = BB_REINFORCEMENTS_EMOTE - /// Reinforcement-calling behavior to use - var/call_type = /datum/ai_behavior/call_reinforcements - -/datum/ai_planning_subtree/call_reinforcements/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if (!decide_to_call(controller) || controller.blackboard[BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN] > world.time) - return - - var/call_say = controller.blackboard[BB_REINFORCEMENTS_SAY] - var/call_emote = controller.blackboard[BB_REINFORCEMENTS_EMOTE] - - if(!isnull(call_say)) - controller.queue_behavior(/datum/ai_behavior/perform_speech, call_say) - else if(!isnull(call_emote)) - controller.queue_behavior(/datum/ai_behavior/perform_emote, call_emote) - - controller.queue_behavior(call_type) - -/// Decides when to call reinforcements, can be overridden for alternate behavior -/datum/ai_planning_subtree/call_reinforcements/proc/decide_to_call(datum/ai_controller/controller) - return controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && istype(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET], /mob) - -/datum/ai_planning_subtree/call_reinforcements/mining - call_type = /datum/ai_behavior/call_reinforcements/mining - -/// Call out to all mobs in the specified range for help -/datum/ai_behavior/call_reinforcements - /// How frequently can we call for reinforcements? - var/cooldown = 30 SECONDS - /// Range to call reinforcements from - var/reinforcements_range = 15 - -/datum/ai_behavior/call_reinforcements/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/pawn_mob = controller.pawn - for(var/mob/other_mob in oview(reinforcements_range, pawn_mob)) - if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller)) - continue - // Add our current target to their retaliate list so that they'll attack our aggressor - other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET], world.time) - other_mob.ai_controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENT_TARGET, pawn_mob) - - controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN, world.time + cooldown) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/// Does not force retaliation, but increases targeting priority instead -/datum/ai_behavior/call_reinforcements/mining - cooldown = 1 SECONDS - reinforcements_range = 7 - -/datum/ai_behavior/call_reinforcements/mining/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/pawn_mob = controller.pawn - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - for(var/mob/other_mob in oview(reinforcements_range, pawn_mob)) - if(!pawn_mob.faction_check_atom(other_mob) || isnull(other_mob.ai_controller)) - continue - var/list/existing_requests = other_mob.ai_controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS] - if (!existing_requests || !existing_requests[target]) - other_mob.ai_controller.set_blackboard_key_assoc_lazylist(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, list()) - other_mob.ai_controller.add_blackboard_key_assoc(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, target, world.time) - - controller.set_blackboard_key(BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN, world.time + cooldown) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm b/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm index 211e01be1f28..54578a21d385 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/capricious_retaliate.dm @@ -1,65 +1,59 @@ -/// Add or remove people to our retaliation shitlist just on an arbitrary whim -/datum/ai_planning_subtree/capricious_retaliate - /// Blackboard key which tells us how to select valid targets - var/targeting_strategy_key = BB_TARGETING_STRATEGY - /// Whether we should skip checking faction for our decision - var/ignore_faction = TRUE +///Random chance to add things to our retaliate list +/datum/bt_node/ai_behavior/capricious_retaliate + var/targeting_strategy = BB_TARGETING_STRATEGY + var/ignore_faction + time_between_perform = 1 SECONDS -/datum/ai_planning_subtree/capricious_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(/datum/ai_behavior/capricious_retaliate, targeting_strategy_key, ignore_faction) - -/// Add or remove people to our retaliation shitlist just on an arbitrary whim -/datum/ai_behavior/capricious_retaliate - action_cooldown = 1 SECONDS - -/datum/ai_behavior/capricious_retaliate/perform(seconds_per_tick, datum/ai_controller/controller, targeting_strategy_key, ignore_faction) +/datum/bt_node/ai_behavior/capricious_retaliate/perform(seconds_per_tick, datum/ai_controller/controller) var/atom/pawn = controller.pawn - if (controller.blackboard_key_exists(BB_BASIC_MOB_RETALIATE_LIST)) + + if(controller.blackboard_key_exists(BB_BASIC_MOB_RETALIATE_LIST)) var/deaggro_chance = controller.blackboard[BB_RANDOM_DEAGGRO_CHANCE] || 10 - if (!SPT_PROB(deaggro_chance, seconds_per_tick)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - pawn.visible_message(span_notice("[pawn] calms down.")) // We can blackboard key this if anyone else actually wants to customise it - controller.clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - controller.CancelActions() // Otherwise they will try and get one last kick in - return AI_BEHAVIOR_DELAY + if(prob(deaggro_chance)) //Chance to chill the fuck out. This prob() should be matched with the frequency of calling. + pawn.visible_message(span_notice("[pawn] calms down.")) + controller.clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST) + controller.clear_blackboard_key(BB_CURRENT_TARGET) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED // De-aggroed var/aggro_chance = controller.blackboard[BB_RANDOM_AGGRO_CHANCE] || 0.5 - if (!SPT_PROB(aggro_chance, seconds_per_tick)) + if(!prob(aggro_chance)) //Check if we should get pissed at someone REEE return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/aggro_range = controller.blackboard[BB_AGGRO_RANGE] || 9 var/list/potential_targets = hearers(aggro_range, get_turf(pawn)) - pawn - if (!length(potential_targets)) + if(!length(potential_targets)) failed_targeting(pawn) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/datum/targeting_strategy/target_helper = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) + if(!ispath(targeting_strategy)) + targeting_strategy = controller.blackboard[targeting_strategy] - var/mob/living/final_target = null - if (ignore_faction) + var/datum/targeting_strategy/target_helper = GET_TARGETING_STRATEGY(targeting_strategy) + + if(ignore_faction) controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE) - while (isnull(final_target) && length(potential_targets)) + + var/mob/living/final_target = null + while(isnull(final_target) && length(potential_targets)) var/mob/living/test_target = pick_n_take(potential_targets) - if (target_helper.can_attack(pawn, test_target, vision_range = aggro_range)) + if(target_helper.is_valid_target(pawn, test_target, vision_range = aggro_range)) final_target = test_target - if (isnull(final_target)) + if(isnull(final_target)) failed_targeting(pawn) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + // Add to shitlist set_blackboard_key_assoc_lazylist calls post_blackboard_key_set, waking the combat branch controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, final_target, world.time) pawn.visible_message(span_warning("[pawn] glares grumpily at [final_target]!")) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/// Called if we try but fail to target something -/datum/ai_behavior/capricious_retaliate/proc/failed_targeting(atom/pawn) - pawn.visible_message(span_notice("[pawn] grumbles.")) // We're pissed off but with no outlet to vent our frustration upon +/datum/bt_node/ai_behavior/capricious_retaliate/proc/failed_targeting(atom/pawn) + pawn.visible_message(span_notice("[pawn] grumbles.")) -/datum/ai_behavior/capricious_retaliate/finish_action(datum/ai_controller/controller, succeeded, ignore_faction) +/datum/bt_node/ai_behavior/capricious_retaliate/finish_action(datum/ai_controller/controller, succeeded) . = ..() - if (succeeded || !ignore_faction) + if(succeeded || !ignore_faction) return var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm b/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm deleted file mode 100644 index bad349030f1b..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/ai_planning_subtree/climb_trees - operational_datums = list(/datum/component/tree_climber) - ///chance to climb a tree - var/climb_chance = 35 - -/datum/ai_planning_subtree/climb_trees/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - if(!SPT_PROB(climb_chance, seconds_per_tick)) - return - - if(controller.blackboard_key_exists(BB_CLIMBED_TREE)) - controller.queue_behavior(/datum/ai_behavior/climb_tree, BB_CLIMBED_TREE) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_tree, BB_CLIMBED_TREE, /obj/structure/flora/tree) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/drag_items.dm b/code/datums/ai/basic_mobs/basic_subtrees/drag_items.dm deleted file mode 100644 index 3bdb1ab8f588..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/drag_items.dm +++ /dev/null @@ -1,60 +0,0 @@ - -///simple behavior to make mobs randomly drag things around -/datum/ai_planning_subtree/steal_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.pulling) - if(prob(controller.blackboard[BB_GUILTY_CONSCIOUS_CHANCE])) - controller.queue_behavior(/datum/ai_behavior/stop_dragging) - return - if(!prob(controller.blackboard[BB_STEAL_CHANCE])) - return - if(!controller.blackboard_key_exists(BB_ITEM_TO_STEAL)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/find_stealable, BB_ITEM_TO_STEAL, /obj/item) - return - controller.queue_behavior(/datum/ai_behavior/drag_target, BB_ITEM_TO_STEAL) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/find_and_set/find_stealable - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 2 MINUTES - -/datum/ai_behavior/find_and_set/find_stealable/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - - var/list/possible_items = shuffle_inplace(oview(search_range, controller.pawn)) - for(var/obj/item/possible_item in possible_items) - if(possible_item.pulledby || possible_item.anchored) - continue - if(can_see(living_pawn, possible_item)) - return possible_item - - -/datum/ai_behavior/stop_dragging - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/stop_dragging/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - living_pawn.stop_pulling() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/drag_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/drag_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/drag_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/movable/target = controller.blackboard[target_key] - if(QDELETED(target) || target.anchored || target.pulledby) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/our_mob = controller.pawn - our_mob.start_pulling(target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/drag_target/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm b/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm index cf3a922a5a44..8523adb9ef5f 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm @@ -1,44 +1,33 @@ -// Performs the enrage behavior when health is below given threshold, and calm down behavior if above that value afterwards -/datum/ai_planning_subtree/enrage +/// Halves the basic mob's melee attack cooldown while its health is at or below a threshold, and restores it once recovered. +/datum/bt_node/ai_behavior/enrage + /// Fraction of max health at or below which the mob becomes enraged. var/health_threshold = 0.5 - var/enrage_behavior = /datum/ai_behavior/enrage -/datum/ai_planning_subtree/enrage/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) +/datum/bt_node/ai_behavior/enrage/perform(seconds_per_tick, datum/ai_controller/controller) if(!isbasicmob(controller.pawn)) - return + return AI_BEHAVIOR_FAILED + var/mob/living/basic/basic_pawn = controller.pawn var/low_health = (basic_pawn.health / basic_pawn.maxHealth) <= health_threshold - var/is_enraged = controller.blackboard_key_exists(BB_BASIC_MOB_ENRAGE) - if(low_health && !is_enraged) - controller.queue_behavior(enrage_behavior, FALSE) - else if(!low_health && is_enraged) - controller.queue_behavior(enrage_behavior, TRUE) + if(low_health && !is_enraged) + var/current_cooldown = basic_pawn.melee_attack_cooldown + controller.set_blackboard_key(BB_BASIC_MOB_ENRAGE, TRUE) + controller.set_blackboard_key(BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN, current_cooldown) + basic_pawn.melee_attack_cooldown = current_cooldown / 2 + + if(controller.blackboard_key_exists(BB_CURRENT_TARGET)) + basic_pawn.visible_message(span_danger("\The [basic_pawn] gets an enraged look at [controller.blackboard[BB_CURRENT_TARGET]]!")) + else + basic_pawn.visible_message(span_danger("\The [basic_pawn] gets an enraged look!")) + return AI_BEHAVIOR_SUCCEEDED -/// Cuts down basic mob's melee attack cooldown in half -/datum/ai_behavior/enrage - -/datum/ai_behavior/enrage/perform(seconds_per_tick, datum/ai_controller/controller, calm_down) - var/mob/living/basic/basic_pawn = controller.pawn - if(calm_down) - var/previous_delay = controller.blackboard[BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN] + if(!low_health && is_enraged) // Technically something else could have modified the cooldown before/after but that requires further consideration so don't use this behavior in these scenarios - basic_pawn.melee_attack_cooldown = previous_delay + basic_pawn.melee_attack_cooldown = controller.blackboard[BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN] controller.clear_blackboard_key(BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN) controller.clear_blackboard_key(BB_BASIC_MOB_ENRAGE) return AI_BEHAVIOR_SUCCEEDED - var/current_cooldown = basic_pawn.melee_attack_cooldown - var/new_attack_cooldown = current_cooldown / 2 - - controller.set_blackboard_key(BB_BASIC_MOB_ENRAGE, TRUE) - controller.set_blackboard_key(BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN, current_cooldown) - basic_pawn.melee_attack_cooldown = new_attack_cooldown - - if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - var/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - controller.pawn.visible_message(span_danger("\The [controller.pawn] gets an enraged look at [current_target]!")) - else - controller.pawn.visible_message(span_danger("\The [controller.pawn] gets an enraged look!")) - return AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_FAILED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.json new file mode 100644 index 000000000000..9bdb05c4b056 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.json @@ -0,0 +1,82 @@ +{ + "dm_type": "/datum/bt_node/subtree/escape_captivity", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/buckle_target_dangerous", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_out_of_object/from_bb", + "vars": { + "target_key": "BB_BASIC_MOB_ESCAPE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_contained_in_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/container_attackable", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_out_of_object/from_bb", + "vars": { + "target_key": "BB_BASIC_MOB_ESCAPE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.dm b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.dm index 759a5c69eb85..eeb05455c1ba 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.dm @@ -1,70 +1,7 @@ -/// Generically try to escape from being trapped -/datum/ai_planning_subtree/escape_captivity - /// Targeting strategy for use deciding if we can attack a mob grabbing us - var/targeting_strategy_key = BB_TARGETING_STRATEGY - /// If true we will never attack objects - var/pacifist = FALSE +///Tries to escape activity, has observers to cancel if needed +/datum/bt_node/subtree/escape_captivity + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/escape_captivity.bt.json" -/datum/ai_planning_subtree/escape_captivity/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - if (isobj(living_pawn.buckled)) - // we can just stand up we don't need to freak out - if (pacifist || !HAS_TRAIT(living_pawn.buckled, TRAIT_DANGEROUS_BUCKLE)) - controller.queue_behavior(/datum/ai_behavior/resist) - // otherwise beat the shit out of we we gotta get out NOW - else - controller.queue_behavior(/datum/ai_behavior/break_out_of_object, living_pawn.buckled) - return SUBTREE_RETURN_FINISH_PLANNING - - if (!isturf(living_pawn.loc) && !ismob(living_pawn.loc) && !istype(living_pawn.loc, /obj/item/mob_holder)) - var/atom/contained_in = living_pawn.loc - var/attack_effective = FALSE - if (!pacifist) - if (isbasicmob(living_pawn)) // Currently this literally only works for basic mobs because it's hard to check for anyone else but it's ok because only they use this subtree - var/mob/living/basic/basic_pawn = living_pawn - attack_effective = basic_pawn.obj_damage > contained_in.damage_deflection - if (attack_effective) - controller.queue_behavior(/datum/ai_behavior/break_out_of_object, contained_in) - else - controller.queue_behavior(/datum/ai_behavior/resist) - return SUBTREE_RETURN_FINISH_PLANNING - - var/mob/puller = living_pawn.pulledby - if (puller && puller.grab_state > GRAB_PASSIVE) - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - var/friends_list = controller.blackboard[BB_FRIENDS_LIST] || list() - // Only resist grabs from mobs that aren't in our faction - if (targeting_strategy?.can_attack(living_pawn, puller) && !(puller in friends_list)) - controller.queue_behavior(/datum/ai_behavior/resist) - return SUBTREE_RETURN_FINISH_PLANNING - - if (HAS_TRAIT(living_pawn, TRAIT_RESTRAINED)) - controller.queue_behavior(/datum/ai_behavior/resist) - return SUBTREE_RETURN_FINISH_PLANNING - -/// Keep attacking an object while it is our loc or while we are buckled to it -/datum/ai_behavior/break_out_of_object - action_cooldown = 0.2 SECONDS - -/datum/ai_behavior/break_out_of_object/setup(datum/ai_controller/controller, atom/target) - if (!should_attack_target(controller, target)) - return FALSE - return TRUE - -/datum/ai_behavior/break_out_of_object/perform(seconds_per_tick, datum/ai_controller/controller, atom/target_atom) - if (!should_attack_target(controller, target_atom)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - controller.ai_interact(target = target_atom, combat_mode = TRUE) - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/break_out_of_object/proc/should_attack_target(datum/ai_controller/controller, atom/target) - if (QDELETED(target)) - return FALSE - var/mob/living/pawn = controller.pawn - if (!target.IsReachableBy(pawn)) - return FALSE - return pawn.loc == target || pawn.buckled == target - -/datum/ai_planning_subtree/escape_captivity/pacifist - pacifist = TRUE +/// Pacifist variant: never attacks objects, only resists. +/datum/bt_node/subtree/escape_captivity/pacifist + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.json new file mode 100644 index 000000000000..59684c19d86d --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/escape_captivity_pacifist.bt.json @@ -0,0 +1,50 @@ +{ + "dm_type": "/datum/bt_node/subtree/escape_captivity/pacifist", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_contained_in_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/express_happiness.dm b/code/datums/ai/basic_mobs/basic_subtrees/express_happiness.dm deleted file mode 100644 index 4d7a3e7ad306..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/express_happiness.dm +++ /dev/null @@ -1,44 +0,0 @@ -#define HIGH_HAPPINESS_THRESHOLD 0.7 -#define MODERATE_HAPPINESS_THRESHOLD 0.5 - -/datum/ai_planning_subtree/express_happiness - operational_datums = list(/datum/component/happiness) - ///the key storing our happiness value - var/happiness_key = BB_BASIC_HAPPINESS - ///list of emotions we relay when happy - var/static/list/happy_emotions = list( - "celebrates happily!", - "dances around in excitement!", - ) - ///our moderate emotions - var/static/list/moderate_emotions = list( - "looks satisfied.", - "trots around.", - ) - ///emotions we display when we are sad - var/static/list/depressed_emotions = list( - "looks depressed...", - "turns its back and sulks...", - "looks towards the floor in dissapointment...", - ) - -/datum/ai_planning_subtree/express_happiness/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(5, seconds_per_tick)) - return - var/happiness_value = controller.blackboard[happiness_key] - if(isnull(happiness_value)) - return - var/list/final_list - switch(happiness_value) - if(HIGH_HAPPINESS_THRESHOLD to INFINITY) - final_list = controller.blackboard[BB_HAPPY_EMOTIONS] || happy_emotions - if(MODERATE_HAPPINESS_THRESHOLD to HIGH_HAPPINESS_THRESHOLD) - final_list = controller.blackboard[BB_MODERATE_EMOTIONS] || moderate_emotions - else - final_list = controller.blackboard[BB_SAD_EMOTIONS] || depressed_emotions - if(!length(final_list)) - return - controller.queue_behavior(/datum/ai_behavior/perform_emote, pick(final_list)) - -#undef HIGH_HAPPINESS_THRESHOLD -#undef MODERATE_HAPPINESS_THRESHOLD diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm deleted file mode 100644 index 9b3e2883290e..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm +++ /dev/null @@ -1,43 +0,0 @@ -/// similar to finding a target but looks for food types in the // the what? -/datum/ai_planning_subtree/find_food - ///behavior we use to find the food - var/datum/ai_behavior/finding_behavior = /datum/ai_behavior/find_and_set/in_list - ///key of foods list - var/food_list_key = BB_BASIC_FOODS - ///key where we store our food - var/found_food_key = BB_TARGET_FOOD - ///key holding any emotes we play after eating food - var/emotes_blackboard_list = BB_EAT_EMOTES - ///key where we store our search range - var/search_range = BB_SEARCH_RANGE - -/datum/ai_planning_subtree/find_food/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/list/foods_list = controller.blackboard[food_list_key] - if(!length(foods_list)) - CRASH("the types of food has not been supplied in the [food_list_key] key!") - if(controller.blackboard[BB_NEXT_FOOD_EAT] > world.time) - return - - if(!controller.blackboard_key_exists(found_food_key)) - controller.queue_behavior(finding_behavior, found_food_key, foods_list, controller.blackboard[BB_SEARCH_RANGE]) - return - - controller.queue_behavior(/datum/ai_behavior/interact_with_target/eat_food, found_food_key, emotes_blackboard_list) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/interact_with_target/eat_food - ///default list of actions we take after eating - var/list/food_actions = list( - "eats up happily!", - "chomps with glee!", - ) - -/datum/ai_behavior/interact_with_target/eat_food/perform(seconds_per_tick, datum/ai_controller/controller, target_key, emotes_blackboard_list) - . = ..() - if(. & AI_BEHAVIOR_FAILED) - return - var/list/emotes_to_pick = controller.blackboard[emotes_blackboard_list] || food_actions - if(!length(emotes_to_pick)) - return - var/mob/living/living_pawn = controller.pawn - living_pawn.manual_emote(pick(emotes_to_pick)) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.json new file mode 100644 index 000000000000..d6b902983f93 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.json @@ -0,0 +1,73 @@ +{ + "dm_type": "/datum/bt_node/subtree/find_paper_and_write", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/write_on_paper", + "vars": { + "paper_key": "BB_SIMPLE_CARRY_ITEM", + "writing_list_key": "BB_WRITING_LIST" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_FOUND_PAPER" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "time_between_perform": "0.4 SECONDS", + "target_key": "BB_FOUND_PAPER", + "can_attack_turfs": true, + "can_attack_dense_objects": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_PAPER", + "required_dist": 0 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up_item_virtual", + "vars": { + "target_key": "BB_FOUND_PAPER", + "storage_key": "BB_SIMPLE_CARRY_ITEM" + } + } + ] + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm index 0d2d0c3e3b44..947fbec7b8d9 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm @@ -1,22 +1,3 @@ -/datum/ai_planning_subtree/find_paper_and_write - -/datum/ai_planning_subtree/find_paper_and_write/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/wizard = controller.pawn - - if(controller.blackboard_key_exists(BB_SIMPLE_CARRY_ITEM)) - controller.queue_behavior(/datum/ai_behavior/write_on_paper, BB_SIMPLE_CARRY_ITEM, BB_WRITING_LIST) - return SUBTREE_RETURN_FINISH_PLANNING - - var/obj/item/paper/target = controller.blackboard[BB_FOUND_PAPER] - - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/empty_paper, BB_FOUND_PAPER, /obj/item/paper) - return - - if(get_turf(wizard) != get_turf(target)) - controller.queue_behavior(/datum/ai_behavior/travel_towards, BB_FOUND_PAPER) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!(target in wizard.contents)) - controller.queue_behavior(/datum/ai_behavior/pick_up_item, BB_FOUND_PAPER, BB_SIMPLE_CARRY_ITEM) - return SUBTREE_RETURN_FINISH_PLANNING +/// Idle behaviour: hunt down a nearby blank paper, fetch it, scrawl a threat on it and drop it. +/datum/bt_node/subtree/find_paper_and_write + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm deleted file mode 100644 index 7b35053ff386..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm +++ /dev/null @@ -1,31 +0,0 @@ -/datum/ai_planning_subtree/look_for_adult - ///how far we must be from the mom - var/minimum_distance = 1 - -/datum/ai_planning_subtree/look_for_adult/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/target = controller.blackboard[BB_FOUND_MOM] - var/mob/baby = controller.pawn - - if(QDELETED(target)) - find_mom(controller) - return - - if(get_dist(target, baby) > minimum_distance) - controller.queue_behavior(/datum/ai_behavior/travel_towards/stop_on_arrival, BB_FOUND_MOM) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!SPT_PROB(15, seconds_per_tick)) - return - - if(target.stat == DEAD) - controller.queue_behavior(/datum/ai_behavior/perform_emote, "cries for their parent!") - else - controller.queue_behavior(/datum/ai_behavior/perform_emote, "dances around their parent!") - - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_planning_subtree/look_for_adult/proc/find_mom(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/find_mom, BB_FIND_MOM_TYPES, BB_IGNORE_MOM_TYPES, BB_FOUND_MOM) - -/datum/ai_planning_subtree/look_for_adult/raptor/find_mom(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/find_mom/raptor, BB_FIND_MOM_TYPES, BB_FOUND_MOM) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_targets_prioritize_traits.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_targets_prioritize_traits.dm deleted file mode 100644 index 6c83469960ad..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_targets_prioritize_traits.dm +++ /dev/null @@ -1,6 +0,0 @@ -/// Find something with a specific trait to run from -/datum/ai_planning_subtree/find_target_prioritize_traits - -/datum/ai_planning_subtree/find_target_prioritize_traits/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(/datum/ai_behavior/find_potential_targets/prioritize_trait, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION, BB_TARGET_PRIORITY_TRAIT) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm b/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm deleted file mode 100644 index e17f50740fa6..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm +++ /dev/null @@ -1,44 +0,0 @@ -#define FISHING_COOLDOWN 45 SECONDS - -///subtree for fishing and eating food! -/datum/ai_planning_subtree/fish - ///behavior we use to find fishable objects - var/datum/ai_behavior/find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list - ///behavior we use to fish! - var/datum/ai_behavior/fishing_behavior = /datum/ai_behavior/interact_with_target/fishing - ///blackboard key storing things we can fish from - var/fishable_list_key = BB_FISHABLE_LIST - ///key where we store found fishable items - var/fishing_target_key = BB_FISHING_TARGET - ///key where we store our fishing cooldown - var/fishing_cooldown_key = BB_FISHING_COOLDOWN - ///our fishing range - var/fishing_range = 5 - -/datum/ai_planning_subtree/fish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_ONLY_FISH_WHILE_HUNGRY] && controller.blackboard[BB_NEXT_FOOD_EAT] > world.time) - return - if(controller.blackboard[BB_FISHING_TIMER] > world.time) - return - if(!controller.blackboard_key_exists(fishing_target_key)) - controller.queue_behavior(find_fishable_behavior, fishing_target_key, controller.blackboard[fishable_list_key], fishing_range) - return - controller.queue_behavior(/datum/ai_behavior/interact_with_target/fishing, fishing_target_key, fishing_cooldown_key) - return SUBTREE_RETURN_FINISH_PLANNING - -///less expensive fishing behavior! -/datum/ai_planning_subtree/fish/fish_from_turfs - find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list/closest_turf - -/datum/ai_behavior/interact_with_target/fishing - clear_target = FALSE - combat_mode = FALSE - -/datum/ai_behavior/interact_with_target/fishing/finish_action(datum/ai_controller/controller, succeeded, fishing_target_key, fishing_cooldown_key) - . = ..() - if(!succeeded) - return - var/cooldown = controller.blackboard[fishing_cooldown_key] || FISHING_COOLDOWN - controller.set_blackboard_key(BB_FISHING_TIMER, world.time + cooldown) - -#undef FISHING_COOLDOWN diff --git a/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm deleted file mode 100644 index 3ed8b2df2b26..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm +++ /dev/null @@ -1,39 +0,0 @@ -/// Try to escape from your current target, without performing any other actions. -/datum/ai_planning_subtree/flee_target - /// Behaviour to execute in order to flee - var/flee_behaviour = /datum/ai_behavior/run_away_from_target - /// Blackboard key in which to store selected target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// Blackboard key in which to store selected target's hiding place - var/hiding_place_key = BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION - -/datum/ai_planning_subtree/flee_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/flee_from = controller.blackboard[target_key] - if(!should_flee(controller, flee_from)) - return - var/flee_distance = controller.blackboard[BB_BASIC_MOB_FLEE_DISTANCE] || DEFAULT_BASIC_FLEE_DISTANCE - if (get_dist(controller.pawn, flee_from) >= flee_distance) - return - - controller.queue_behavior(flee_behaviour, target_key, hiding_place_key) - return SUBTREE_RETURN_FINISH_PLANNING //we gotta get out of here. - -/datum/ai_planning_subtree/flee_target/proc/should_flee(datum/ai_controller/controller, atom/flee_from) - if (controller.blackboard[BB_BASIC_MOB_STOP_FLEEING] || QDELETED(flee_from)) - return FALSE - return TRUE - -/// Try to escape from your current target, without performing any other actions. -/// Reads from some fleeing-specific targeting keys rather than the current mob target. -/datum/ai_planning_subtree/flee_target/from_flee_key - target_key = BB_BASIC_MOB_FLEE_TARGET - hiding_place_key = BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION - -/// A subtype that forces the mob to flee from targets with the scary fisherman trait anyway. -/datum/ai_planning_subtree/flee_target/from_fisherman - -/datum/ai_planning_subtree/flee_target/from_fisherman/should_flee(datum/ai_controller/controller, atom/flee_from) - if (!QDELETED(flee_from) && HAS_TRAIT(flee_from, TRAIT_SCARY_FISHERMAN)) - return TRUE - return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/generic_hunger.dm b/code/datums/ai/basic_mobs/basic_subtrees/generic_hunger.dm new file mode 100644 index 000000000000..38ceaf78659a --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/generic_hunger.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/generic_hunger + behavior_tree_json = "code/datums/ai/generic_hunger.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/generic_play_instrument.dm b/code/datums/ai/basic_mobs/basic_subtrees/generic_play_instrument.dm new file mode 100644 index 000000000000..7b7e3d7a8ee6 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/generic_play_instrument.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/generic_play_instrument + behavior_tree_json = "code/datums/ai/generic_play_instrument.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.json new file mode 100644 index 000000000000..1524aad1a2d5 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.json @@ -0,0 +1,70 @@ +{ + "dm_type": "/datum/bt_node/subtree/go_for_swim", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SWIM_ALTERNATE_TURF" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SWIM_ALTERNATE_TURF", + "required_dist": 0, + "finish_on_arrival": true + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_KEY_SWIMMER_COOLDOWN", + "cooldown_duration": "30 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": "30 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SWIM_ALTERNATE_TURF", + "targeting_strategy": "/datum/targeting_strategy/walkable_turf", + "target_source": "/datum/target_source/oview_land_turfs" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SWIM_ALTERNATE_TURF", + "required_dist": 0, + "finish_on_arrival": true + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/swim_splash" + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.dm b/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.dm index ba50023567fe..89eb9829dcb1 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.dm @@ -1,59 +1,15 @@ -#define DEFAULT_TIME_SWIMMER 30 SECONDS +/// Wander between water and land, splashing about now and then. +/datum/bt_node/subtree/go_for_swim + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/go_for_swim.bt.json" -///subtree to go and swim! -/datum/ai_planning_subtree/go_for_swim +/// Splashes about while standing in water. +/datum/bt_node/ai_behavior/swim_splash -/datum/ai_planning_subtree/go_for_swim/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_SWIM_ALTERNATE_TURF)) - controller.queue_behavior(/datum/ai_behavior/travel_towards/swimming, BB_SWIM_ALTERNATE_TURF) - - if(isnull(controller.blackboard[BB_KEY_SWIM_TIME])) - controller.set_blackboard_key(BB_KEY_SWIM_TIME, DEFAULT_TIME_SWIMMER) - - var/mob/living/living_pawn = controller.pawn - var/turf/our_turf = get_turf(living_pawn) - - // we have been taken out of water! - controller.set_blackboard_key(BB_CURRENTLY_SWIMMING, iswaterturf(our_turf)) - - if(controller.blackboard[BB_KEY_SWIM_TIME] < world.time) - controller.queue_behavior(/datum/ai_behavior/find_and_set/swim_alternate, BB_SWIM_ALTERNATE_TURF, /turf/open) - return - - // have some fun in the water - if(controller.blackboard[BB_CURRENTLY_SWIMMING] && SPT_PROB(5, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/perform_emote, "splashes water all around!") - - -///find land if its time to get out of water, otherwise find water -/datum/ai_behavior/find_and_set/swim_alternate - -/datum/ai_behavior/find_and_set/swim_alternate/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) +/datum/bt_node/ai_behavior/swim_splash/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/living_pawn = controller.pawn - if(QDELETED(living_pawn)) - return null - var/look_for_land = controller.blackboard[BB_CURRENTLY_SWIMMING] - var/list/possible_turfs = list() - for(var/turf/possible_turf in oview(search_range, living_pawn)) - if(isclosedturf(possible_turf) || is_space_or_openspace(possible_turf)) - continue - if(possible_turf.is_blocked_turf()) - continue - if(look_for_land == iswaterturf(possible_turf)) - continue - possible_turfs += possible_turf - - if(!length(possible_turfs)) - return null - - return(pick(possible_turfs)) - -/datum/ai_behavior/travel_towards/swimming - clear_target = TRUE - -/datum/ai_behavior/travel_towards/swimming/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - var/time_to_add = controller.blackboard[BB_KEY_SWIMMER_COOLDOWN] ? controller.blackboard[BB_KEY_SWIMMER_COOLDOWN] : DEFAULT_TIME_SWIMMER - controller.set_blackboard_key(BB_KEY_SWIM_TIME, world.time + time_to_add ) - -#undef DEFAULT_TIME_SWIMMER + if(!istype(living_pawn) || !iswaterturf(get_turf(living_pawn))) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!SPT_PROB(5, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + living_pawn.manual_emote("splashes water all around!") + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm b/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm deleted file mode 100644 index 8b8b0d0bf6fc..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm +++ /dev/null @@ -1,119 +0,0 @@ -/// Step away if too close, or towards if too far -/datum/ai_planning_subtree/maintain_distance - /// Blackboard key holding atom we want to stay away from - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// How far do we look for our target? - var/view_distance = 10 - /// the run away behavior we will use - var/run_away_behavior = /datum/ai_behavior/step_away - -/datum/ai_planning_subtree/maintain_distance/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - - var/mob/living/living_pawn = controller.pawn - if(LAZYLEN(living_pawn.do_afters)) - return - - var/atom/target = controller.blackboard[target_key] - if (!isliving(target) || !can_see(controller.pawn, target, view_distance)) - return // Don't run away from cucumbers, they're not snakes - var/range = get_dist(controller.pawn, target) - - var/minimum_distance = controller.blackboard[BB_RANGED_SKIRMISH_MIN_DISTANCE] || 4 - var/maximum_distance = controller.blackboard[BB_RANGED_SKIRMISH_MAX_DISTANCE] || 6 - - if (range < minimum_distance) - controller.queue_behavior(run_away_behavior, target_key, minimum_distance) - return - if (range > maximum_distance) - controller.queue_behavior(/datum/ai_behavior/pursue_to_range, target_key, maximum_distance) - return - -/datum/ai_planning_subtree/maintain_distance/cover_minimum_distance - run_away_behavior = /datum/ai_behavior/cover_minimum_distance - -/// Take one step away -/datum/ai_behavior/step_away - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 0 - action_cooldown = 0.2 SECONDS - -/datum/ai_behavior/step_away/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/current_target = controller.blackboard[target_key] - if (QDELETED(current_target)) - return FALSE - - var/mob/living/our_pawn = controller.pawn - our_pawn.face_atom(current_target) - - var/turf/next_step = get_step_away(controller.pawn, current_target) - if (!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) - set_movement_target(controller, target = next_step, new_movement = /datum/ai_movement/basic_avoidance/backstep) - return TRUE - - var/list/all_dirs = GLOB.alldirs.Copy() - all_dirs -= get_dir(controller.pawn, next_step) - all_dirs -= get_dir(controller.pawn, current_target) - shuffle_inplace(all_dirs) - - for (var/dir in all_dirs) - next_step = get_step(controller.pawn, dir) - if (!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) - set_movement_target(controller, target = next_step, new_movement = /datum/ai_movement/basic_avoidance/backstep) - return TRUE - return FALSE - -/datum/ai_behavior/step_away/perform(seconds_per_tick, datum/ai_controller/controller) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/step_away/finish_action(datum/ai_controller/controller, succeeded) - . = ..() - controller.change_ai_movement_type(initial(controller.ai_movement)) - -/// Pursue a target until we are within a provided range -/datum/ai_behavior/pursue_to_range - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_MOVE_AND_PERFORM - -/datum/ai_behavior/pursue_to_range/setup(datum/ai_controller/controller, target_key, range) - . = ..() - var/atom/current_target = controller.blackboard[target_key] - if (QDELETED(current_target)) - return FALSE - if (get_dist(controller.pawn, current_target) <= range) - return FALSE - set_movement_target(controller, current_target) - -/datum/ai_behavior/pursue_to_range/perform(seconds_per_tick, datum/ai_controller/controller, target_key, range) - var/atom/current_target = controller.blackboard[target_key] - if (!QDELETED(current_target) && get_dist(controller.pawn, current_target) > range) - return AI_BEHAVIOR_INSTANT - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -///instead of taking a single step, we cover the entire distance -/datum/ai_behavior/cover_minimum_distance - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 0 - action_cooldown = 0.2 SECONDS - -/datum/ai_behavior/cover_minimum_distance/setup(datum/ai_controller/controller, target_key, minimum_distance) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - var/required_distance = minimum_distance - get_dist(controller.pawn, target) //the distance we need to move - var/distance = 0 - var/turf/chosen_turf - for(var/turf/open/potential_turf in oview(required_distance, controller.pawn)) - var/new_distance_from_target = get_dist(potential_turf, target) - if(potential_turf.is_blocked_turf()) - continue - if(new_distance_from_target > distance) - chosen_turf = potential_turf - distance = new_distance_from_target - if(isnull(chosen_turf)) - return FALSE - set_movement_target(controller, target = chosen_turf) - -/datum/ai_behavior/cover_minimum_distance/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm b/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm deleted file mode 100644 index 6edc631c7a15..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm +++ /dev/null @@ -1,68 +0,0 @@ -//behavior to find mineable mineral walls - -/datum/ai_planning_subtree/mine_walls - var/find_wall_behavior = /datum/ai_behavior/find_mineral_wall - -/datum/ai_planning_subtree/mine_walls/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_TARGET_MINERAL_WALL)) - controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_TARGET_MINERAL_WALL) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(find_wall_behavior, BB_TARGET_MINERAL_WALL) - -/datum/ai_behavior/mine_wall - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 15 SECONDS - -/datum/ai_behavior/mine_wall/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/mine_wall/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/basic/living_pawn = controller.pawn - var/turf/closed/mineral/target = controller.blackboard[target_key] - var/is_gibtonite_turf = istype(target, /turf/closed/mineral/gibtonite) - if(!controller.ai_interact(target = target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(is_gibtonite_turf) - living_pawn.manual_emote("sighs...") //accept whats about to happen to us - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/mine_wall/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/find_mineral_wall - -/datum/ai_behavior/find_mineral_wall/perform(seconds_per_tick, datum/ai_controller/controller, found_wall_key) - var/mob/living_pawn = controller.pawn - - for(var/turf/closed/mineral/potential_wall in oview(9, living_pawn)) - if(!check_if_mineable(controller, potential_wall)) //check if its surrounded by walls - continue - controller.set_blackboard_key(found_wall_key, potential_wall) //closest wall first! - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/find_mineral_wall/proc/check_if_mineable(datum/ai_controller/controller, turf/target_wall) - var/mob/living/source = controller.pawn - var/direction_to_turf = get_dir(target_wall, source) - if(!ISDIAGONALDIR(direction_to_turf)) - return TRUE - var/list/directions_to_check = list() - for(var/direction_check in GLOB.cardinals) - if(direction_check & direction_to_turf) - directions_to_check += direction_check - - for(var/direction in directions_to_check) - var/turf/test_turf = get_step(target_wall, direction) - if(isnull(test_turf)) - continue - if(!test_turf.is_blocked_turf(ignore_atoms = list(source))) - return TRUE - return FALSE diff --git a/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm b/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm index bcf5c8290251..c36fe040fcff 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm @@ -1,69 +1,64 @@ -/// Try to line up with a cardinal direction of your target -/datum/ai_planning_subtree/move_to_cardinal - /// Behaviour to execute to line ourselves up - var/move_behaviour = /datum/ai_behavior/move_to_cardinal - /// Blackboard key in which to store selected target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - -/datum/ai_planning_subtree/move_to_cardinal/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(target_key)) - return - controller.queue_behavior(move_behaviour, target_key) - -/// Try to line up with a cardinal direction of your target -/datum/ai_behavior/move_to_cardinal - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// How close to our target is too close? +/// Moves to line up with the target along a cardinal direction, so a directional ability can fire down the lane. +/// Reports SUCCESS once lined up and within range, FAILURE when the target is gone, too far, or pathing gives up. +/datum/bt_node/ai_behavior/move_to_cardinal + time_between_perform = 0 + /// Blackboard key holding the atom to line up with. + var/target_key = BB_CURRENT_TARGET + /// How close to our target is too close. var/minimum_distance = 1 - /// How far away is too far? + /// How far away is too far. var/maximum_distance = 9 + /// The cardinal tile of our target we are currently moving toward. + var/atom/destination + /// Set by on_movement_failed() when the movement system gives up pathing. + var/movement_failed = FALSE -/datum/ai_behavior/move_to_cardinal/setup(datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/move_to_cardinal/setup(datum/ai_controller/controller) var/atom/target = controller.blackboard[target_key] if(QDELETED(target)) return FALSE - target_nearest_cardinal(controller, target) + RegisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED, PROC_REF(on_movement_failed)) + move_towards_nearest_cardinal(controller, target) return TRUE -/// Set our movement target to the closest cardinal space to our target -/datum/ai_behavior/move_to_cardinal/proc/target_nearest_cardinal(datum/ai_controller/controller, atom/target) +/datum/bt_node/ai_behavior/move_to_cardinal/proc/on_movement_failed(atom/source) + SIGNAL_HANDLER + movement_failed = TRUE + +/// Begin moving toward the closest unblocked cardinal tile of our target. +/datum/bt_node/ai_behavior/move_to_cardinal/proc/move_towards_nearest_cardinal(datum/ai_controller/controller, atom/target) var/atom/move_target var/closest = INFINITY - - for (var/dir in GLOB.cardinals) + for(var/dir in GLOB.cardinals) var/turf/cardinal_turf = get_ranged_target_turf(target, dir, minimum_distance) - if (cardinal_turf.is_blocked_turf()) + if(cardinal_turf.is_blocked_turf()) continue var/distance_to = get_dist(controller.pawn, cardinal_turf) - if (distance_to >= closest) + if(distance_to >= closest) continue closest = distance_to move_target = cardinal_turf - - if (isnull(move_target)) + if(isnull(move_target)) move_target = target - if (controller.current_movement_target == move_target) - return - set_movement_target(controller, move_target) + controller.ai_movement.start_moving_towards(controller, move_target, 0) + destination = move_target -/datum/ai_behavior/move_to_cardinal/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/move_to_cardinal/perform(seconds_per_tick, datum/ai_controller/controller) + if(movement_failed) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/atom/target = controller.blackboard[target_key] - if (QDELETED(target)) + if(QDELETED(target)) return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - if (!(get_dir(controller.pawn, target) in GLOB.cardinals)) - target_nearest_cardinal(controller, target) - return AI_BEHAVIOR_INSTANT var/distance_to_target = get_dist(controller.pawn, target) - if (distance_to_target < minimum_distance) - target_nearest_cardinal(controller, target) - return AI_BEHAVIOR_INSTANT - if (distance_to_target > maximum_distance) + if(distance_to_target > maximum_distance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!(get_dir(controller.pawn, target) in GLOB.cardinals) || distance_to_target < minimum_distance) + move_towards_nearest_cardinal(controller, target) return AI_BEHAVIOR_INSTANT return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/move_to_cardinal/finish_action(datum/ai_controller/controller, succeeded, target_key) - if (!succeeded) - controller.clear_blackboard_key(target_key) +/datum/bt_node/ai_behavior/move_to_cardinal/finish_action(datum/ai_controller/controller, succeeded) + UnregisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED) + movement_failed = FALSE + controller.ai_movement.stop_moving_towards(controller) return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm b/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm deleted file mode 100644 index 240272d1ef48..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/opportunistic_ventcrawler.dm +++ /dev/null @@ -1,20 +0,0 @@ -/// Opportunistically searches for and hides/scurries through vents. -/datum/ai_planning_subtree/opportunistic_ventcrawler - -/datum/ai_planning_subtree/opportunistic_ventcrawler/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING)) - return SUBTREE_RETURN_FINISH_PLANNING // hold on let me cook - - var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[BB_ENTRY_VENT_TARGET] - - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_ENTRY_VENT_TARGET, /obj/machinery/atmospherics/components/unary/vent_pump) // keep looking otherwise they KILL US AND WE DIE - return - - if(get_turf(controller.pawn) != get_turf(target)) - controller.queue_behavior(/datum/ai_behavior/travel_towards, BB_ENTRY_VENT_TARGET) - return - - controller.set_blackboard_key(BB_CURRENTLY_TARGETING_VENT, TRUE) - controller.queue_behavior(/datum/ai_behavior/crawl_through_vents, BB_ENTRY_VENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING // we are going into this vent... no distractions diff --git a/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm b/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm deleted file mode 100644 index f78574a110ad..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/play_with_owners.dm +++ /dev/null @@ -1,21 +0,0 @@ -/datum/ai_planning_subtree/find_and_hunt_target/play_with_owner - target_key = BB_OWNER_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/play_with_owner - finding_behavior = /datum/ai_behavior/find_hunt_target/find_owner - hunt_targets = list(/mob/living) - hunt_chance = 80 - hunt_range = 9 - -/datum/ai_behavior/find_hunt_target/find_owner - action_cooldown = 1 MINUTES - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_hunt_target/find_owner/valid_dinner(mob/living/source, atom/friend, radius, datum/ai_controller/controller, seconds_per_tick) - return (friend != source) && (source.has_ally(friend)) && can_see(source, friend, radius) - -/datum/ai_behavior/hunt_target/play_with_owner - -/datum/ai_behavior/hunt_target/play_with_owner/target_caught(mob/living/hunter, atom/hunted) - var/list/interactions_list = hunter.ai_controller.blackboard[BB_INTERACTIONS_WITH_OWNER] - var/interaction_message = length(interactions_list) ? pick(interactions_list) : "Plays with" - hunter.manual_emote("[interaction_message] [hunted]!") diff --git a/code/datums/ai/basic_mobs/basic_subtrees/prepare_travel_to_destination.dm b/code/datums/ai/basic_mobs/basic_subtrees/prepare_travel_to_destination.dm deleted file mode 100644 index 2718ca630ff7..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/prepare_travel_to_destination.dm +++ /dev/null @@ -1,23 +0,0 @@ - -///Subtree that checks if we are on the target atom's tile, and sets it as a travel target if not -///The target is taken from the blackboard. This one always requires a specific implementation. -/datum/ai_planning_subtree/prepare_travel_to_destination - var/target_key - var/travel_destination_key = BB_TRAVEL_DESTINATION - -/datum/ai_planning_subtree/prepare_travel_to_destination/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[target_key] - - //Target is deleted, or we are already standing on it - if(QDELETED(target) || (isturf(target) && controller.pawn.loc == target) || (target.loc == controller.pawn.loc)) - return - - //Already set with this value, return - if(controller.blackboard[target_key] == controller.blackboard[travel_destination_key]) - return - - controller.queue_behavior(/datum/ai_behavior/set_travel_destination, target_key, travel_destination_key) - return //continue planning regardless of success - -/datum/ai_planning_subtree/prepare_travel_to_destination/trader - target_key = BB_SHOP_SPOT diff --git a/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.json new file mode 100644 index 000000000000..57d616c63bd5 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": { + "label": "speech_behavior", + "default": "/datum/bt_node/ai_behavior/random_speech_blackboard" + } + }, + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "$bqdqne64" + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.dm b/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.dm new file mode 100644 index 000000000000..2a3db065a1ef --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/random_speech_loop + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/random_speech_loop.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm deleted file mode 100644 index 3004199a70f5..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm +++ /dev/null @@ -1,50 +0,0 @@ -/// Fire a ranged attack without interrupting movement. -/datum/ai_planning_subtree/ranged_skirmish - operational_datums = list(/datum/component/ranged_attacks) - /// Blackboard key holding target atom - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// What AI behaviour do we actually run? - var/attack_behavior = /datum/ai_behavior/ranged_skirmish - /// If target is further away than this we don't fire - var/max_range = 9 - /// If target is closer than this we don't fire - var/min_range = 2 - -/datum/ai_planning_subtree/ranged_skirmish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(target_key)) - return - controller.queue_behavior(attack_behavior, target_key, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION, max_range, min_range) - -/// How often will we try to perform our ranged attack? -/datum/ai_behavior/ranged_skirmish - action_cooldown = 0.5 SECONDS - -/datum/ai_behavior/ranged_skirmish/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key, max_range, min_range) - . = ..() - var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] - return !QDELETED(target) - -/datum/ai_behavior/ranged_skirmish/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key, max_range, min_range) - var/atom/target = controller.blackboard[target_key] - if (QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if(!targeting_strategy.can_attack(controller.pawn, target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/hiding_target = targeting_strategy.find_hidden_mobs(controller.pawn, target) - controller.set_blackboard_key(hiding_location_key, hiding_target) - - target = hiding_target || target - - var/distance = get_dist(controller.pawn, target) - if (distance > max_range || distance < min_range) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target = target, combat_mode = TRUE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_planning_subtree/ranged_skirmish/no_minimum - min_range = 0 diff --git a/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.json new file mode 100644 index 000000000000..e3bbd798047f --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.json @@ -0,0 +1,50 @@ +{ + "dm_type": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": { + "label": "target_key", + "default": "BB_CURRENT_TARGET" + } + }, + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_STOP_FLEEING", + "invert": true, + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_flee_location", + "vars": { + "target_key": "$byk9gqj4", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "destination_key": "BB_FLEE_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FLEE_LOCATION", + "required_dist": 0, + "finish_on_arrival": true + } + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.dm new file mode 100644 index 000000000000..bfdd95157363 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.dm @@ -0,0 +1,8 @@ +/// Flee from BB_CURRENT_TARGET, gated by BB_BASIC_MOB_STOP_FLEEING. +/// Computes a flee waypoint each loop via find_flee_location then moves to it. +/datum/bt_node/subtree/run_away_from_target + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target.bt.json" + +/// Flee variant that fires ranged attacks at the current target while moving. +/datum/bt_node/subtree/run_away_from_target/run_and_shoot + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.json new file mode 100644 index 000000000000..124239b50608 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/run_away_from_target_run_and_shoot.bt.json @@ -0,0 +1,69 @@ +{ + "dm_type": "/datum/bt_node/subtree/run_away_from_target/run_and_shoot", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_STOP_FLEEING", + "invert": true, + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_flee_location", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "destination_key": "BB_FLEE_LOCATION" + } + } + ] + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FLEE_LOCATION", + "required_dist": 0, + "finish_on_arrival": true + } + } + ] + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/run_emote.dm b/code/datums/ai/basic_mobs/basic_subtrees/run_emote.dm deleted file mode 100644 index c0c0da96584a..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/run_emote.dm +++ /dev/null @@ -1,31 +0,0 @@ -/// Intermittently run an emote -/datum/ai_planning_subtree/run_emote - var/emote_key = BB_EMOTE_KEY - var/emote_chance_key = BB_EMOTE_CHANCE - -/datum/ai_planning_subtree/run_emote/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/emote_chance = controller.blackboard[emote_chance_key] || 0 - if (!SPT_PROB(emote_chance, seconds_per_tick)) - return - controller.queue_behavior(/datum/ai_behavior/run_emote, emote_key) - -/// Emote from a blackboard key -/datum/ai_behavior/run_emote - -/datum/ai_behavior/run_emote/perform(seconds_per_tick, datum/ai_controller/controller, emote_key) - var/mob/living/living_pawn = controller.pawn - if (!isliving(living_pawn)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/list/emote_list = controller.blackboard[emote_key] - var/emote - if (islist(emote_list)) - emote = length(emote_list) ? pick(emote_list) : null - else - emote = emote_list - - if(isnull(emote)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - living_pawn.emote(emote) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/shapechange_ambush.dm b/code/datums/ai/basic_mobs/basic_subtrees/shapechange_ambush.dm deleted file mode 100644 index ff01eb804ff7..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/shapechange_ambush.dm +++ /dev/null @@ -1,41 +0,0 @@ -/// Shapeshift when we have no target, until someone has been nearby for long enough -/datum/ai_planning_subtree/shapechange_ambush - operational_datums = list(/datum/component/ai_target_timer) - /// Key where we keep our ability - var/ability_key = BB_SHAPESHIFT_ACTION - /// Key where we keep our target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// How long to lull our target into a false sense of security - var/minimum_target_time = 8 SECONDS - -/datum/ai_planning_subtree/shapechange_ambush/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/is_shifted = ismob(living_pawn.loc) - var/has_target = controller.blackboard_key_exists(target_key) - var/datum/action/cooldown/using_action = controller.blackboard[ability_key] - - if (!is_shifted) - if (has_target) - return // We're busy - - if (using_action?.IsAvailable()) - controller.queue_behavior(/datum/ai_behavior/use_mob_ability/shapeshift, BB_SHAPESHIFT_ACTION) // Shift - return SUBTREE_RETURN_FINISH_PLANNING - - if (!has_target || !using_action?.IsAvailable()) - return SUBTREE_RETURN_FINISH_PLANNING // Lie in wait - var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0 - if (time_on_target < minimum_target_time) - return // Wait a bit longer - controller.queue_behavior(/datum/ai_behavior/use_mob_ability/shapeshift, BB_SHAPESHIFT_ACTION) // Surprise! - -/// Selects a random shapeshift ability before shifting -/datum/ai_behavior/use_mob_ability/shapeshift - -/datum/ai_behavior/use_mob_ability/shapeshift/setup(datum/ai_controller/controller, ability_key) - var/datum/action/cooldown/spell/shapeshift/using_action = controller.blackboard[ability_key] - if (!using_action?.IsAvailable()) - return FALSE - if (isnull(using_action.shapeshift_type)) // If we don't have a shape then pick one, AI can't use context wheels - using_action.shapeshift_type = pick(using_action.possible_shapes) - return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm deleted file mode 100644 index f764568d4ba7..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm +++ /dev/null @@ -1,33 +0,0 @@ -/datum/ai_planning_subtree/basic_melee_attack_subtree - /// What do we do in order to attack someone? - var/datum/ai_behavior/basic_melee_attack/melee_attack_behavior = /datum/ai_behavior/basic_melee_attack - /// Is this the last thing we do? (if we set a movement target, this will usually be yes) - var/end_planning = TRUE - -/datum/ai_planning_subtree/basic_melee_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - if (end_planning) - return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. - -/datum/ai_planning_subtree/basic_ranged_attack_subtree - operational_datums = list(/datum/component/ranged_attacks) - var/datum/ai_behavior/basic_ranged_attack/ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - controller.queue_behavior(ranged_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. - -/datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman - -/datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/movable/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) - return ..() - if(!HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_nearest_target_to_flee.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_nearest_target_to_flee.dm deleted file mode 100644 index 83e514f32702..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_nearest_target_to_flee.dm +++ /dev/null @@ -1,25 +0,0 @@ -/// Find the nearest thing which we assume is hostile and set it as the flee target -/datum/ai_planning_subtree/simple_find_nearest_target_to_flee - -/datum/ai_planning_subtree/simple_find_nearest_target_to_flee/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if (controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return - controller.queue_behavior(/datum/ai_behavior/find_potential_targets/nearest, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - -/// Find the nearest thing on our list of 'things which have done damage to me' and set it as the flee target -/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee - ///the targeting strategy we use - var/targeting_key = BB_TARGETING_STRATEGY - ///what key should we set the target as - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - -/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if (controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return - controller.queue_behavior(/datum/ai_behavior/target_from_retaliate_list/nearest, BB_BASIC_MOB_RETALIATE_LIST, target_key, targeting_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - -/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/from_flee_key - target_key = BB_BASIC_MOB_FLEE_TARGET - targeting_key = BB_FLEE_TARGETING_STRATEGY diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm deleted file mode 100644 index 57fb22b4a956..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm +++ /dev/null @@ -1,26 +0,0 @@ -/datum/ai_planning_subtree/simple_find_target - /// Variable to store target in - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// Targeting strategy key to use - var/strategy_key = BB_TARGETING_STRATEGY - /// Behavior to use to find targets - var/target_behavior = /datum/ai_behavior/find_potential_targets - -/datum/ai_planning_subtree/simple_find_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(target_behavior, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - -// Prevents finding a target if a human is nearby -/datum/ai_planning_subtree/simple_find_target/not_while_observed - -/datum/ai_planning_subtree/simple_find_target/not_while_observed/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - for(var/mob/living/carbon/human/watcher in hearers(7, controller.pawn)) - if(watcher.stat != DEAD) - return - return ..() - -/datum/ai_planning_subtree/simple_find_target/to_flee - target_key = BB_BASIC_MOB_FLEE_TARGET - -/datum/ai_planning_subtree/simple_find_target/hunt - strategy_key = BB_HUNT_TARGETING_STRATEGY diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm deleted file mode 100644 index 7a230014d9ab..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm +++ /dev/null @@ -1,6 +0,0 @@ -/// Selects the most wounded potential target that we can see -/datum/ai_planning_subtree/simple_find_wounded_target - -/datum/ai_planning_subtree/simple_find_wounded_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(/datum/ai_behavior/find_potential_targets/most_wounded, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.json new file mode 100644 index 000000000000..bb2b997b7729 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.json @@ -0,0 +1,88 @@ +{ + "dm_type": "/datum/bt_node/subtree/skittish_brawler_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": "DEFAULT_BASIC_FLEE_DISTANCE" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.dm b/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.dm new file mode 100644 index 000000000000..3ea474d2ded3 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.dm @@ -0,0 +1,4 @@ +/// Cowardly brawler combat: keep our distance from attackers, fleeing if they get close but +/// turning to bite if they hang back just out of reach. +/datum/bt_node/subtree/skittish_brawler_combat + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/skittish_brawler_combat.bt.json" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm deleted file mode 100644 index 649a45d4cc7a..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm +++ /dev/null @@ -1,35 +0,0 @@ -/// Disables AI after a certain amount of time spent with no target, you will have to enable the AI again somewhere else -/datum/ai_planning_subtree/sleep_with_no_target - /// Behaviour to execute when sleeping - var/sleep_behaviour = /datum/ai_behavior/sleep_after_targetless_time - /// Target key to interrogate - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - -/datum/ai_planning_subtree/sleep_with_no_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(sleep_behaviour, BB_BASIC_MOB_CURRENT_TARGET) - -/// Disables AI after a certain amount of time spent with no target, you will have to enable the AI again somewhere else -/datum/ai_behavior/sleep_after_targetless_time - /// Turn off AI if we spend this many seconds without a target - var/time_to_wait = 10 SECONDS - -/datum/ai_behavior/sleep_after_targetless_time/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - return (controller.blackboard_key_exists(target_key)) ? ( AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED) : ( AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED) - -/datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if (!succeeded) - controller.clear_blackboard_key(BB_TARGETLESS_TIME) - return - - if (isnull(controller.blackboard[BB_TARGETLESS_TIME])) - controller.set_blackboard_key(BB_TARGETLESS_TIME, world.time + time_to_wait) - - if (controller.blackboard[BB_TARGETLESS_TIME] < world.time) - enter_sleep(controller) - controller.clear_blackboard_key(BB_TARGETLESS_TIME) - -/// Disables AI, override to do additional things or something else -/datum/ai_behavior/sleep_after_targetless_time/proc/enter_sleep(datum/ai_controller/controller) - controller.set_ai_status(AI_STATUS_OFF) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm deleted file mode 100644 index 2a4a0d336e20..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm +++ /dev/null @@ -1,242 +0,0 @@ -/datum/ai_planning_subtree/random_speech - //The chance of an emote occurring each second - var/speech_chance = 0 - ///Hearable emotes - var/list/emote_hear - ///Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - var/list/emote_see - ///Possible lines of speech the AI can have - var/list/speak - ///The sound effects associated with this speech, if any - var/list/sound - -/datum/ai_planning_subtree/random_speech/New() - . = ..() - if(LAZYLEN(speak)) - speak = string_list(speak) - if(LAZYLEN(sound)) - sound = string_list(sound) - if(LAZYLEN(emote_hear)) - emote_hear = string_list(emote_hear) - if(LAZYLEN(emote_see)) - emote_see = string_list(emote_see) - -/datum/ai_planning_subtree/random_speech/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(speech_chance, seconds_per_tick)) - return - speak(controller) - -/// Actually perform an action -/datum/ai_planning_subtree/random_speech/proc/speak(datum/ai_controller/controller) - var/audible_emotes_length = emote_hear?.len - var/non_audible_emotes_length = emote_see?.len - var/speak_lines_length = speak?.len - - var/total_choices_length = audible_emotes_length + non_audible_emotes_length + speak_lines_length - - if (total_choices_length == 0) - return - - var/random_number_in_range = rand(1, total_choices_length) - var/sound_to_play = length(sound) > 0 ? pick(sound) : null - - if(random_number_in_range <= audible_emotes_length) - controller.queue_behavior(/datum/ai_behavior/perform_emote, pick(emote_hear), sound_to_play) - else if(random_number_in_range <= (audible_emotes_length + non_audible_emotes_length)) - controller.queue_behavior(/datum/ai_behavior/perform_emote, pick(emote_see)) - else - controller.queue_behavior(/datum/ai_behavior/perform_speech, pick(speak), sound_to_play) - -/datum/ai_planning_subtree/random_speech/insect - speech_chance = 5 - sound = list('sound/mobs/non-humanoids/insect/chitter.ogg') - emote_hear = list("chitters.") - -/datum/ai_planning_subtree/random_speech/mothroach - speech_chance = 15 - emote_hear = list("flutters.") - -/datum/ai_planning_subtree/random_speech/mouse - speech_chance = 1 - speak = list("Squeak!", "SQUEAK!", "Squeak?") - sound = list('sound/mobs/non-humanoids/mouse/mousesqueek.ogg') - emote_hear = list("squeaks.") - emote_see = list("runs in a circle.", "shakes.") - -/datum/ai_planning_subtree/random_speech/frog - speech_chance = 3 - emote_see = list("jumps in a circle.", "shakes.") - -/datum/ai_planning_subtree/random_speech/lizard // all of these have to be three words long or i'm killing you. you're dead. - speech_chance = 3 - emote_hear = list("stamps around some.", "hisses a bit.") - emote_see = list("blehs the tongue.", "tilts the head.", "does a spin.") - -/datum/ai_planning_subtree/random_speech/sheep - speech_chance = 5 - speak = list("baaa","baaaAAAAAH!","baaah") - sound = list('sound/mobs/non-humanoids/sheep/sheep1.ogg', 'sound/mobs/non-humanoids/sheep/sheep2.ogg', 'sound/mobs/non-humanoids/sheep/sheep3.ogg') - emote_hear = list("bleats.") - emote_see = list("shakes her head.", "stares into the distance.") - -/datum/ai_planning_subtree/random_speech/rabbit - speech_chance = 10 - speak = list("Mrrp.", "CHIRP!", "Mrrp?") // rabbits make some weird noises dude i don't know what to tell you - emote_hear = list("hops.") - emote_see = list("hops around.", "bounces up and down.") - -/// For the easter subvariant of rabbits, these ones actually speak catchphrases. -/datum/ai_planning_subtree/random_speech/rabbit/easter - speak = list( - "Hop into Easter!", - "Come get your eggs!", - "Prizes for everyone!", - ) - -/// These ones have a space mask on, so their catchphrases are muffled. -/datum/ai_planning_subtree/random_speech/rabbit/easter/space - speak = list( - "Hmph mmph mmmph!", - "Mmphe mmphe mmphe!", - "Hmm mmm mmm!", - ) - -/datum/ai_planning_subtree/random_speech/chicken - speech_chance = 15 // really talkative ladies - speak = list("Cluck!", "BWAAAAARK BWAK BWAK BWAK!", "Bwaak bwak.") - sound = list('sound/mobs/non-humanoids/chicken/clucks.ogg', 'sound/mobs/non-humanoids/chicken/bagawk.ogg') - emote_hear = list("clucks.", "croons.") - emote_see = list("pecks at the ground.","flaps her wings viciously.") - -/datum/ai_planning_subtree/random_speech/chick - speech_chance = 4 - speak = list("Cherp.", "Cherp?", "Chirrup.", "Cheep!") - sound = list('sound/mobs/non-humanoids/chicken/chick_peep.ogg') - emote_hear = list("cheeps.") - emote_see = list("pecks at the ground.","flaps her tiny wings.") - -/datum/ai_planning_subtree/random_speech/cow - speech_chance = 1 - speak = list("moo?","moo","MOOOOOO") - sound = list('sound/mobs/non-humanoids/cow/cow.ogg') - emote_hear = list("brays.") - emote_see = list("shakes her head.") - -///unlike normal cows, wisdom cows speak of wisdom and won't shut the fuck up -/datum/ai_planning_subtree/random_speech/cow/wisdom - speech_chance = 15 - -/datum/ai_planning_subtree/random_speech/cow/wisdom/New() - . = ..() - speak = GLOB.wisdoms //Done here so it's setup properly - sound = list() - -/datum/ai_planning_subtree/random_speech/deer - speech_chance = 1 - speak = list("Weeeeeeee?", "Weeee", "WEOOOOOOOOOO") - emote_hear = list("brays.") - emote_see = list("shakes her head.") - -/datum/ai_planning_subtree/random_speech/dog - speech_chance = 1 - -/datum/ai_planning_subtree/random_speech/dog/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!isdog(controller.pawn)) - return - - // Stay in sync with dog fashion. - var/mob/living/basic/pet/dog/dog_pawn = controller.pawn - dog_pawn.update_dog_speech(src) - - return ..() - -/datum/ai_planning_subtree/random_speech/faithless - speech_chance = 1 - emote_see = list("wails.") - -/datum/ai_planning_subtree/random_speech/garden_gnome - speech_chance = 5 - speak = list("Gnot a gnelf!", "Gnot a gnoblin!", "Howdy chum!") - emote_hear = list("snores.", "burps.") - emote_see = list("blinks.") - -/datum/ai_planning_subtree/random_speech/tree - speech_chance = 3 - emote_see = list("photosynthesizes angrily.") - -/datum/ai_planning_subtree/random_speech/pig - speech_chance = 3 - speak = list("oink?","oink","snurf") - sound = list('sound/mobs/non-humanoids/pig/pig1.ogg', 'sound/mobs/non-humanoids/pig/pig2.ogg') - emote_hear = list("snorts.") - emote_see = list("sniffs around.") - -/datum/ai_planning_subtree/random_speech/pony - speech_chance = 3 - sound = list('sound/mobs/non-humanoids/pony/whinny01.ogg', 'sound/mobs/non-humanoids/pony/whinny02.ogg', 'sound/mobs/non-humanoids/pony/whinny03.ogg') - emote_hear = list("whinnies!") - emote_see = list("horses around.") - -/datum/ai_planning_subtree/random_speech/pony/tamed - speech_chance = 3 - sound = list('sound/mobs/non-humanoids/pony/snort.ogg') - emote_hear = list("snorts.") - emote_see = list("snorts.") - -/datum/ai_planning_subtree/random_speech/killer_tomato - speech_chance = 3 - emote_hear = list("gnashes.", "growls lowly.", "snarls.") - emote_see = list("salivates.") - -/datum/ai_planning_subtree/random_speech/ant - speech_chance = 1 - speak = list("BZZZZT!", "CHTCHTCHT!", "Bzzz", "ChtChtCht") - sound = list('sound/mobs/non-humanoids/insect/chitter.ogg') - emote_hear = list("buzzes.", "clacks.") - emote_see = list("shakes their head.", "twitches their antennae.") - -/datum/ai_planning_subtree/random_speech/fox - speech_chance = 1 - speak = list("Ack-Ack", "Ack-Ack-Ack-Ackawoooo", "Geckers", "Awoo", "Tchoff") - emote_hear = list("howls.", "barks.", "screams.") - emote_see = list("shakes their head.", "shivers.") - -/datum/ai_planning_subtree/random_speech/crab - speech_chance = 1 - sound = list('sound/mobs/non-humanoids/crab/claw_click.ogg') - emote_hear = list("clicks.") - emote_see = list("clacks.") - -/datum/ai_planning_subtree/random_speech/penguin - speech_chance = 5 - speak = list("Gah Gah!", "NOOT NOOT!", "NOOT!", "Noot", "noot", "Prah!", "Grah!") - emote_hear = list("squawks", "gakkers") - -/datum/ai_planning_subtree/random_speech/bear - speech_chance = 5 - emote_hear = list("rawrs.","grumbles.","grawls.", "stomps!") - emote_see = list("stares ferociously.") - -/datum/ai_planning_subtree/random_speech/cats - speech_chance = 10 - sound = list(SFX_CAT_MEOW) - emote_hear = list("meows.") - emote_see = list("meows.") - -/datum/ai_planning_subtree/random_speech/blackboard //literal tower of babel, subtree form - speech_chance = 1 - -/datum/ai_planning_subtree/random_speech/blackboard/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/list/speech_lines = controller.blackboard[BB_BASIC_MOB_SPEAK_LINES] - if(isnull(speech_lines)) - return ..() - - // Note to future developers: this behaviour a singleton so this probably doesn't work as you would expect - // The whole speech tree really needs to be refactored because this isn't how we use AI data these days - speak = speech_lines[BB_EMOTE_SAY] || list() - emote_see = speech_lines[BB_EMOTE_SEE] || list() - emote_hear = speech_lines[BB_EMOTE_HEAR] || list() - sound = speech_lines[BB_EMOTE_SOUND] || list() - speech_chance = speech_lines[BB_SPEAK_CHANCE] ? speech_lines[BB_SPEAK_CHANCE] : initial(speech_chance) - - return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/stare_at_thing.dm b/code/datums/ai/basic_mobs/basic_subtrees/stare_at_thing.dm deleted file mode 100644 index 5263c8f82c6d..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/stare_at_thing.dm +++ /dev/null @@ -1,12 +0,0 @@ -/// Locate a thing (practically any atom) to stop and stare at. -/datum/ai_planning_subtree/stare_at_thing - -/datum/ai_planning_subtree/stare_at_thing/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_STATIONARY_CAUSE] - - if(isnull(target)) // No target? Time to locate one using the list we set in this mob's blackboard. - var/list/potential_scares = controller.blackboard[BB_STATIONARY_TARGETS] - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_STATIONARY_CAUSE, potential_scares) - return - - controller.queue_behavior(/datum/ai_behavior/stop_and_stare, BB_STATIONARY_CAUSE) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm b/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm deleted file mode 100644 index c86994054613..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm +++ /dev/null @@ -1,94 +0,0 @@ -/// Sets the BB target to a mob which you can see and who has recently attacked you -/datum/ai_planning_subtree/target_retaliate - operational_datums = list(/datum/element/ai_retaliate, /datum/component/ai_retaliate_advanced) - /// Blackboard key which tells us how to select valid targets - var/targeting_strategy_key = BB_TARGETING_STRATEGY - /// Blackboard key in which to store selected target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// Blackboard key in which to store selected target's hiding place - var/hiding_place_key = BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION - /// do we check for faction? - var/check_faction = FALSE - /// Behavior to use to select our target - var/target_behavior = /datum/ai_behavior/target_from_retaliate_list - -/datum/ai_planning_subtree/target_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - controller.queue_behavior(target_behavior, BB_BASIC_MOB_RETALIATE_LIST, target_key, targeting_strategy_key, hiding_place_key, check_faction) - -/datum/ai_planning_subtree/target_retaliate/check_faction - check_faction = TRUE - -/// Places a mob which you can see and who has recently attacked you into some 'run away from this' AI keys -/// Can use a different targeting strategy than you use to select attack targets -/// Not required if fleeing is the only target behaviour or uses the same target datum -/datum/ai_planning_subtree/target_retaliate/to_flee - targeting_strategy_key = BB_FLEE_TARGETING_STRATEGY - target_key = BB_BASIC_MOB_FLEE_TARGET - hiding_place_key = BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION - -/** - * Picks a target from a provided list of atoms who have been pissing you off - * You will probably need /datum/element/ai_retaliate to take advantage of this unless you're populating the blackboard yourself - */ -/datum/ai_behavior/target_from_retaliate_list - action_cooldown = 2 SECONDS - /// How far can we see stuff? - var/vision_range = 9 - -/datum/ai_behavior/target_from_retaliate_list/perform(seconds_per_tick, datum/ai_controller/controller, shitlist_key, target_key, targeting_strategy_key, hiding_location_key, check_faction) - var/mob/living/living_mob = controller.pawn - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if(!targeting_strategy) - . = AI_BEHAVIOR_DELAY - CRASH("No target datum was supplied in the blackboard for [controller.pawn]") - - var/list/shitlist = controller.blackboard[shitlist_key] - var/atom/existing_target = controller.blackboard[target_key] - - var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY]) - var/existing_priority = 0 - // If we have an existing target and its priority is higher than our new target's, don't switch focus - if (priority_strategy && existing_target) - existing_priority = priority_strategy.get_target_priority(controller, existing_target) - - if (!check_faction) - controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE) - - if (!QDELETED(existing_target) && targeting_strategy.can_attack(living_mob, existing_target, vision_range)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - var/list/enemies_list = list() - for(var/mob/living/potential_target as anything in shitlist) - if(!targeting_strategy.can_attack(living_mob, potential_target, vision_range)) - continue - // Strict comparasion because priority strategies might not care about retaliation, so this makes existing targets not override potential retaliates - if (priority_strategy && priority_strategy.get_target_priority(controller, potential_target) < existing_priority) - continue - enemies_list += potential_target - - if(!length(enemies_list)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/atom/new_target = pick_final_target(controller, enemies_list) - controller.set_blackboard_key(target_key, new_target) - - var/atom/potential_hiding_location = targeting_strategy.find_hidden_mobs(living_mob, new_target) - - if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially. - controller.set_blackboard_key(hiding_location_key, potential_hiding_location) - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/// Returns the desired final target from the filtered list of enemies -/datum/ai_behavior/target_from_retaliate_list/proc/pick_final_target(datum/ai_controller/controller, list/enemies_list) - var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY]) - if (!priority_strategy) - return pick(enemies_list) - return priority_strategy.select_target(controller, enemies_list) - -/datum/ai_behavior/target_from_retaliate_list/finish_action(datum/ai_controller/controller, succeeded, shitlist_key, target_key, targeting_strategy_key, hiding_location_key, check_faction) - . = ..() - if (succeeded || check_faction) - return - var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE - controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm deleted file mode 100644 index 010dd04a425e..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm +++ /dev/null @@ -1,34 +0,0 @@ -/// Attempts to use a mob ability on a target -/datum/ai_planning_subtree/targeted_mob_ability - /// Blackboard key for the ability - var/ability_key = BB_TARGETED_ACTION - /// Blackboard key for where the target ref is stored - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - /// Behaviour to perform using ability - var/use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability - /// If true we terminate planning after trying to use the ability. - var/finish_planning = TRUE - -/datum/ai_planning_subtree/targeted_mob_ability/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!ability_key) - CRASH("You forgot to tell this mob where to find its ability") - - if (!controller.blackboard_key_exists(target_key)) - return - - var/datum/action/cooldown/using_action = controller.blackboard[ability_key] - if (!using_action?.IsAvailable()) - return - if (!additional_ability_checks(controller, using_action)) - return - - controller.queue_behavior(use_ability_behaviour, ability_key, target_key) - if (finish_planning) - return SUBTREE_RETURN_FINISH_PLANNING - -/// Any additional checks before we queue the behaviour -/datum/ai_planning_subtree/targeted_mob_ability/proc/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action) - return TRUE - -/datum/ai_planning_subtree/targeted_mob_ability/continue_planning - finish_planning = FALSE diff --git a/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm deleted file mode 100644 index 25f0e4a42496..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm +++ /dev/null @@ -1,55 +0,0 @@ -///behavior to activate ability to escape from target -/datum/ai_planning_subtree/teleport_away_from_target - ///minimum distance away from the target before we execute behavior - var/minimum_distance = 2 - ///the ability we will execute - var/ability_key - -/datum/ai_planning_subtree/teleport_away_from_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - var/distance_from_target = get_dist(target, controller.pawn) - if(distance_from_target >= minimum_distance) - controller.clear_blackboard_key(BB_ESCAPE_DESTINATION) - return - var/datum/action/cooldown/ability = controller.blackboard[ability_key] - if(!ability?.IsAvailable()) - return - var/turf/location_turf = controller.blackboard[BB_ESCAPE_DESTINATION] - - if(isnull(location_turf)) - controller.queue_behavior(/datum/ai_behavior/find_furthest_turf_from_target, BB_BASIC_MOB_CURRENT_TARGET, BB_ESCAPE_DESTINATION, minimum_distance) - return SUBTREE_RETURN_FINISH_PLANNING - - if(get_dist(location_turf, target) < minimum_distance || !can_see(controller.pawn, location_turf)) //target moved close too close or we moved too far since finding the target turf - controller.clear_blackboard_key(BB_ESCAPE_DESTINATION) - return - - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target, ability_key, BB_ESCAPE_DESTINATION) - -///find furtherst turf target so we may teleport to it -/datum/ai_behavior/find_furthest_turf_from_target - -/datum/ai_behavior/find_furthest_turf_from_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, set_key, range) - var/mob/living/living_target = controller.blackboard[target_key] - if(QDELETED(living_target)) - return AI_BEHAVIOR_INSTANT - - var/distance = 0 - var/turf/chosen_turf - for(var/turf/open/potential_destination in oview(range, living_target)) - if(potential_destination.is_blocked_turf()) - continue - var/new_distance_to_target = get_dist(potential_destination, living_target) - if(new_distance_to_target > distance) - chosen_turf = potential_destination - distance = new_distance_to_target - if(distance == range) - break //we have already found the max distance - - if(isnull(chosen_turf)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(set_key, chosen_turf) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.json b/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.json new file mode 100644 index 000000000000..862d917bde0b --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.json @@ -0,0 +1,13 @@ +{ + "dm_type": "/datum/bt_node/subtree/tip_reaction", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_BASIC_MOB_TIP_REACTING", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/tipped_reaction" + } +} diff --git a/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm deleted file mode 100644 index b502860a6beb..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm +++ /dev/null @@ -1,10 +0,0 @@ -///used by cows -/datum/ai_planning_subtree/tip_reaction - -/datum/ai_planning_subtree/tip_reaction/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/tip_reacting = controller.blackboard[BB_BASIC_MOB_TIP_REACTING] - if(!tip_reacting) - return - controller.queue_behavior(/datum/ai_behavior/tipped_reaction, BB_BASIC_MOB_TIPPER, BB_BASIC_MOB_TIP_REACTING) - return SUBTREE_RETURN_FINISH_PLANNING //no point in trying, boy. you're TIPPED. diff --git a/code/datums/ai/basic_mobs/basic_subtrees/travel_to_point.dm b/code/datums/ai/basic_mobs/basic_subtrees/travel_to_point.dm deleted file mode 100644 index 0b5e5d4776f9..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/travel_to_point.dm +++ /dev/null @@ -1,21 +0,0 @@ -/// Simply walk to a location -/datum/ai_planning_subtree/travel_to_point - /// Blackboard key where we travel a place we walk to - var/location_key = BB_TRAVEL_DESTINATION - /// What do we do in order to travel - var/travel_behaviour = /datum/ai_behavior/travel_towards - -/datum/ai_planning_subtree/travel_to_point/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/target = controller.blackboard[location_key] - if (QDELETED(target)) - return - controller.queue_behavior(travel_behaviour, location_key) - return SUBTREE_RETURN_FINISH_PLANNING - - -/datum/ai_planning_subtree/travel_to_point/and_clear_target - travel_behaviour = /datum/ai_behavior/travel_towards/stop_on_arrival - -/datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce - location_key = BB_BASIC_MOB_REINFORCEMENT_TARGET diff --git a/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm deleted file mode 100644 index 4c794a73d8ad..000000000000 --- a/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Simple behaviours which simply try to use an ability whenever it is available. - * For something which wants a target try `targeted_mob_ability`. - */ -/datum/ai_planning_subtree/use_mob_ability - /// Blackboard key for the ability - var/ability_key = BB_GENERIC_ACTION - /// Behaviour to perform using ability - var/use_ability_behaviour = /datum/ai_behavior/use_mob_ability - /// If true we terminate planning after trying to use the ability. - var/finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!ability_key) - CRASH("You forgot to tell this mob where to find its ability") - - var/datum/action/using_action = controller.blackboard[ability_key] - if (!using_action?.IsAvailable()) - return - - controller.queue_behavior(use_ability_behaviour, ability_key) - if (finish_planning) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/use_mob_ability - -/datum/ai_behavior/use_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key) - var/datum/action/using_action = controller.blackboard[ability_key] - if (QDELETED(using_action)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - if(using_action.Trigger()) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED diff --git a/code/datums/ai/basic_mobs/generic_controllers.dm b/code/datums/ai/basic_mobs/generic_controllers.dm index b6581a5cb8b9..0a0661f5a8d7 100644 --- a/code/datums/ai/basic_mobs/generic_controllers.dm +++ b/code/datums/ai/basic_mobs/generic_controllers.dm @@ -5,138 +5,121 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk + behavior_tree_json = ABSTRACT_AI_CLASS + + +/datum/bt_node/subtree/simple_hostile_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile_combat.bt.json" + -/// The most basic AI tree which just finds a guy and then runs at them to click them /datum/ai_controller/basic_controller/simple/simple_hostile - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile.bt.json" + + +/datum/bt_node/subtree/simple_ranged_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ranged_combat.bt.json" + +/datum/bt_node/subtree/simple_ranged_retaliate_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.json" + + +/datum/bt_node/subtree/simple_skirmisher_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.json" + +/datum/bt_node/subtree/simple_ability_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_combat.bt.json" + +/datum/bt_node/subtree/simple_ability_retaliate_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.json" + +/datum/bt_node/subtree/simple_ability_melee_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.json" + +/datum/bt_node/subtree/simple_ability_ranged_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.json" + + +/datum/bt_node/subtree/simple_retaliate_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_retaliate_combat.bt.json" + +/datum/bt_node/subtree/simple_capricious_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_capricious_combat.bt.json" + +/datum/bt_node/subtree/simple_fearful_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_fearful_combat.bt.json" + +/datum/bt_node/subtree/simple_skittish_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_skittish_combat.bt.json" + +/datum/bt_node/subtree/simple_hostile_obstacles_combat + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.json" + + /// Find a target, walk at target, attack intervening obstacles /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.json" -/// Find a target, walk at target, attack intervening obstacles +/// Find a target, maintain distance, shoot them /datum/ai_controller/basic_controller/simple/simple_ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/ranged_skirmish, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ranged.bt.json" /datum/ai_controller/basic_controller/simple/simple_ranged_retaliate - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/ranged_skirmish, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.json" /// Find a target, walk towards it AND shoot it /datum/ai_controller/basic_controller/simple/simple_skirmisher - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/ranged_skirmish, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_skirmisher.bt.json" /// Use an ability on target on cooldown /datum/ai_controller/basic_controller/simple/simple_ability - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/targeted_mob_ability, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability.bt.json" /datum/ai_controller/basic_controller/simple/simple_ability_retaliate - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/targeted_mob_ability, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_retaliate.bt.json" /// Use an ability on target on cooldown, then try to punch them /datum/ai_controller/basic_controller/simple/simple_ability_melee - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_melee.bt.json" /// Use an ability on target on cooldown, then try to shoot them /datum/ai_controller/basic_controller/simple/simple_ability_ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/targeted_mob_ability, - /datum/ai_planning_subtree/ranged_skirmish, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_ability_ranged.bt.json" /// Fight back if attacked /datum/ai_controller/basic_controller/simple/simple_retaliate + behavior_tree_json = "code/datums/ai/basic_mobs/simple_retaliate.bt.json" ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Get pissed at random people for no reason /datum/ai_controller/basic_controller/simple/simple_capricious + behavior_tree_json = "code/datums/ai/basic_mobs/simple_capricious.bt.json" ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/capricious_retaliate, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Runs away from anyone it sees /datum/ai_controller/basic_controller/simple/simple_fearful + behavior_tree_json = "code/datums/ai/basic_mobs/simple_fearful.bt.json" ai_traits = PASSIVE_AI_FLAGS - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - /datum/ai_planning_subtree/flee_target, - ) /// Runs away when attacked /datum/ai_controller/basic_controller/simple/simple_skittish + behavior_tree_json = "code/datums/ai/basic_mobs/simple_skittish.bt.json" ai_traits = PASSIVE_AI_FLAGS - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - ) /// Does what it is told and protects da boss +/// TODO: port pet command system to BT so pet_planning functions correctly /datum/ai_controller/basic_controller/simple/simple_goon + behavior_tree_json = "code/datums/ai/basic_mobs/simple_goon.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - ) -/// Literally does nothing except random speedh +/// Literally does nothing except random speech /datum/ai_controller/basic_controller/talk - idle_behavior = null - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/blackboard, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/talk.bt.json" + + +/datum/bt_node/subtree/simple_hostile_combat_with_retaliate + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.json" diff --git a/code/datums/ai/basic_mobs/pet_commands/fetch.dm b/code/datums/ai/basic_mobs/pet_commands/fetch.dm deleted file mode 100644 index 5ff208560d2a..000000000000 --- a/code/datums/ai/basic_mobs/pet_commands/fetch.dm +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Traverse to a target with the intention of picking it up. - * If we can't do that, add it to a list of ignored items. - */ -/datum/ai_behavior/fetch_seek - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/fetch_seek/setup(datum/ai_controller/controller, target_key, delivery_key) - . = ..() - var/obj/item/fetch_thing = controller.blackboard[target_key] - // It stopped existing - if (QDELETED(fetch_thing)) - return FALSE - set_movement_target(controller, fetch_thing) - -/datum/ai_behavior/fetch_seek/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key) - var/obj/item/fetch_thing = controller.blackboard[target_key] - - // It stopped existing - if (QDELETED(fetch_thing)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - // We can't pick this up - if (fetch_thing.anchored) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/fetch_seek/finish_action(datum/ai_controller/controller, success, target_key, delivery_key) - . = ..() - if (success) - return - // Blacklist item if we failed - var/obj/item/target = controller.blackboard[target_key] - if (target) - controller.set_blackboard_key_assoc_lazylist(BB_FETCH_IGNORE_LIST, target, TRUE) - controller.clear_blackboard_key(target_key) - controller.clear_blackboard_key(delivery_key) - -/** - * The second half of fetching, deliver the item to a target. - */ -/datum/ai_behavior/deliver_fetched_item - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/deliver_fetched_item/setup(datum/ai_controller/controller, delivery_key, storage_key) - . = ..() - var/mob/living/return_target = controller.blackboard[delivery_key] - if(QDELETED(return_target)) // Guess it's mine now - return FALSE - set_movement_target(controller, return_target) - -/datum/ai_behavior/deliver_fetched_item/perform(seconds_per_tick, datum/ai_controller/controller, delivery_key, storage_key) - var/mob/living/return_target = controller.blackboard[delivery_key] - if(QDELETED(return_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(!deliver_item(controller, return_target, storage_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/deliver_fetched_item/finish_action(datum/ai_controller/controller, success, delivery_key) - . = ..() - controller.clear_blackboard_key(delivery_key) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - -/// Actually deliver the fetched item to the target, if we still have it -/// Returns TRUE if we succeeded, FALSE if we failed -/datum/ai_behavior/deliver_fetched_item/proc/deliver_item(datum/ai_controller/controller, return_target, storage_key) - var/mob/pawn = controller.pawn - var/obj/item/carried_item = controller.blackboard[storage_key] - if(QDELETED(carried_item) || carried_item.loc != pawn) - pawn.visible_message(span_notice("[pawn] looks around as if [pawn.p_they()] [pawn.p_have()] lost something.")) - return FALSE - - pawn.visible_message(span_notice("[pawn] delivers [carried_item] to [return_target].")) - carried_item.forceMove(get_turf(return_target)) - controller.clear_blackboard_key(storage_key) - return TRUE - -/** - * The alternate second half of fetching, attack the item if we can eat it. - * Or make pleading eyes at someone who has picked it up. - * - * Unfortunately this doesn't work because food can't currently be eaten by mobs. - */ -/datum/ai_behavior/eat_fetched_snack - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - action_cooldown = 0.8 SECONDS - -/datum/ai_behavior/eat_fetched_snack/setup(datum/ai_controller/controller, target_key, delivery_key) - . = ..() - var/obj/item/snack = controller.blackboard[target_key] - if(!istype(snack) || !IS_EDIBLE(snack) || !(isturf(snack.loc) || ishuman(snack.loc))) - return FALSE // This isn't food at all! - set_movement_target(controller, snack) - -/datum/ai_behavior/eat_fetched_snack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key) - var/obj/item/snack = controller.blackboard[target_key] - var/is_living_loc = isliving(snack.loc) - if(QDELETED(snack) || (!isturf(snack.loc) && !is_living_loc)) - // Where did it go? - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/basic/basic_pawn = controller.pawn - if(is_living_loc) - if(SPT_PROB(10, seconds_per_tick)) - basic_pawn.manual_emote("Stares at [snack.loc]'s [snack.name] intently.") - return AI_BEHAVIOR_DELAY - - if(!basic_pawn.Adjacent(snack)) - return AI_BEHAVIOR_DELAY - - controller.ai_interact(target = snack) - - if(QDELETED(snack)) // we ate it! - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/eat_fetched_snack/finish_action(datum/ai_controller/controller, succeeded, target_key, delivery_key) - . = ..() - controller.clear_blackboard_key(target_key) - controller.clear_blackboard_key(delivery_key) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - -/** - * Clear our failed fetch list every so often - */ -/datum/ai_behavior/forget_failed_fetches - /// How long to wait between resetting the list - var/cooldown_duration = AI_FETCH_IGNORE_DURATION - /// Time until we should forget things we failed to pick up - COOLDOWN_DECLARE(reset_ignore_cooldown) - -/datum/ai_behavior/forget_failed_fetches/setup(datum/ai_controller/controller, ...) - . = ..() - if (!COOLDOWN_FINISHED(src, reset_ignore_cooldown)) - return FALSE - if (!length(controller.blackboard[BB_FETCH_IGNORE_LIST])) - return - -/datum/ai_behavior/forget_failed_fetches/perform(seconds_per_tick, datum/ai_controller/controller) - COOLDOWN_START(src, reset_ignore_cooldown, cooldown_duration) - controller.clear_blackboard_key(BB_FETCH_IGNORE_LIST) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.json new file mode 100644 index 000000000000..ca4446c07f76 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.json @@ -0,0 +1,48 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/attack", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.json new file mode 100644 index 000000000000..c8aa95fd4528 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.json @@ -0,0 +1,48 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/attack/dog", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack/dog", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.json new file mode 100644 index 000000000000..c1c6ab6b77a6 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.json @@ -0,0 +1,48 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/attack/minebot", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/minebot", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.json new file mode 100644 index 000000000000..dd8ff0b694a4 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.json @@ -0,0 +1,46 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/attack/ranged/glockroach", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.json new file mode 100644 index 000000000000..d5344983e85a --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.json @@ -0,0 +1,23 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/beehive", + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_HOME", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enter_exit_hive" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.json new file mode 100644 index 000000000000..18d2ef34057d --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.json @@ -0,0 +1,64 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/breed", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_in_typelist", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "typelist_key": "BB_BABIES_PARTNER_TYPES" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/keys_different_gender", + "vars": { + "invert": true, + "key_a": "BB_MY_PAWN", + "key_b": "BB_CURRENT_PET_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "Seems confused" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "combat_mode": false + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] + } + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_bt.dm b/code/datums/ai/basic_mobs/pet_commands/pet_command_bt.dm new file mode 100644 index 000000000000..d969b6aa5f90 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_bt.dm @@ -0,0 +1,132 @@ +// Pet-command-specific BT behaviors and override subtrees. +// Generic leaf behaviors (wait, play_dead, pick_up_item_virtual, pass_item_virtual, ai_interact) live in basic_ai_behaviors/. + +/// Validates a protect_owner target; clears command + target if invalid. +/datum/bt_node/ai_behavior/protect_owner_check + +/datum/bt_node/ai_behavior/protect_owner_check/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/victim = controller.blackboard[BB_CURRENT_PET_TARGET] + if(QDELETED(victim)) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(controller.blackboard[BB_PET_TARGETING_STRATEGY]) + if(!targeter?.is_valid_target(controller.pawn, victim)) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/minimum_stat = controller.blackboard[BB_TARGET_MINIMUM_STAT] + var/checked_stat = victim.stat + if(controller.blackboard[BB_TREAT_UNCONSCIOUS_AS_HARDCRIT] && IS_UNCONSCIOUS(victim)) + checked_stat = max(checked_stat, HARD_CRIT) + + if((isnum(minimum_stat) && checked_stat > minimum_stat) || victim == controller.pawn) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Validates a fetch item at target_key; adds to ignore list and clears keys on failure. +/datum/bt_node/ai_behavior/fetch_seek + var/target_key + +/datum/bt_node/ai_behavior/fetch_seek/setup(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/fetch_seek/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/fetch_thing = controller.blackboard[target_key] + if(QDELETED(fetch_thing)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(fetch_thing.anchored) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/fetch_seek/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + if(succeeded) + return + var/obj/item/target = controller.blackboard[target_key] + if(target) + controller.set_blackboard_key_assoc_lazylist(BB_FETCH_IGNORE_LIST, target, TRUE) + controller.clear_blackboard_key(target_key) + controller.clear_blackboard_key(BB_FETCH_DELIVER_TO) + +/// Clears the fetch ignore list at most once per AI_FETCH_IGNORE_DURATION. Always succeeds. +/datum/bt_node/ai_behavior/forget_failed_fetches + COOLDOWN_DECLARE(clear_cooldown) + +/datum/bt_node/ai_behavior/forget_failed_fetches/perform(seconds_per_tick, datum/ai_controller/controller) + if(COOLDOWN_FINISHED(src, clear_cooldown) && LAZYLEN(controller.blackboard[BB_FETCH_IGNORE_LIST])) + COOLDOWN_START(src, clear_cooldown, AI_FETCH_IGNORE_DURATION) + controller.clear_blackboard_key(BB_FETCH_IGNORE_LIST) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Clears BB_ACTIVE_PET_COMMAND and removes the SUBPLAN_ID_PET_COMMAND override. +/datum/bt_node/ai_behavior/clear_pet_command + +/datum/bt_node/ai_behavior/clear_pet_command/perform(seconds_per_tick, datum/ai_controller/controller) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, null) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +/// Waits forever; blocks normal AI while stay/idle is active. +/datum/bt_node/subtree/pet_command/stay + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.json" + +/// Loops move_to_target toward BB_CURRENT_PET_TARGET until the key is cleared. +/datum/bt_node/subtree/pet_command/follow + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.json" + +/// Plays dead (10%/tick to get up). Clears command on revival. +/datum/bt_node/subtree/pet_command/play_dead + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.json" + +/// Attacks BB_CURRENT_PET_TARGET in a looping melee combat parallel. +/datum/bt_node/subtree/pet_command/attack + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_attack.bt.json" + +/// Protect owner: loops a validity check then melee attack. Clears command if target invalid. +/datum/bt_node/subtree/pet_command/protect_owner + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.json" + +/// Travels to BB_CURRENT_PET_TARGET, clears command on arrival. +/datum/bt_node/subtree/pet_command/move_to + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.json" + +/// Moves to BB_CURRENT_PET_TARGET and fishes there on a loop. +/datum/bt_node/subtree/pet_command/fish + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.json" + +/// Moves to BB_CURRENT_PET_TARGET and breeds once. Clears command on completion. +/datum/bt_node/subtree/pet_command/breed + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_breed.bt.json" + +/// Moves to BB_CURRENT_PET_TARGET and fires BB_TARGETED_ACTION on it once. +/datum/bt_node/subtree/pet_command/targeted_ability + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.json" + +/// Fires the ability stored in BB_PET_ACTIVE_ABILITY once (untargeted). +/datum/bt_node/subtree/pet_command/untargeted_ability + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.json" + +/// Fetch: seek > pick up > deliver. Falls back to clear_pet_command if nothing to do. +/datum/bt_node/subtree/pet_command/fetch + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.json" + +/// Attacks BB_CURRENT_PET_TARGET using the dog's melee behavior (paws if BB_DOG_HARASS_HARM is false, bites otherwise). +/datum/bt_node/subtree/pet_command/attack/dog + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_attack_dog.bt.json" + +/// Attacks BB_CURRENT_PET_TARGET with glockroach ranged attack (1s cooldown). +/datum/bt_node/subtree/pet_command/attack/ranged/glockroach + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_attack_ranged_glockroach.bt.json" + +/// Attacks BB_CURRENT_PET_TARGET with minebot ranged attack (avoids friendly fire). +/datum/bt_node/subtree/pet_command/attack/minebot + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_attack_minebot.bt.json" + +/// Protect owner: loops validity check then glockroach ranged attack. Clears command if target invalid. +/datum/bt_node/subtree/pet_command/protect_owner/ranged/glockroach + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.json" diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.json new file mode 100644 index 000000000000..c6ef1ab9177a --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_fetch.bt.json @@ -0,0 +1,107 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/fetch", + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/forget_failed_fetches" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/fetch_seek", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up_item_virtual", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "storage_key": "BB_SIMPLE_CARRY_ITEM" + } + } + ] + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SIMPLE_CARRY_ITEM", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FETCH_DELIVER_TO", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FETCH_DELIVER_TO", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pass_item_virtual", + "vars": { + "delivery_key": "BB_FETCH_DELIVER_TO", + "storage_key": "BB_SIMPLE_CARRY_ITEM" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true, + "key": "BB_CURRENT_PET_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.json new file mode 100644 index 000000000000..62e0d24abe7a --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_fish.bt.json @@ -0,0 +1,38 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/fish", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "combat_mode": false + } + } + ] + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.json new file mode 100644 index 000000000000..9a70605a7aed --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_follow.bt.json @@ -0,0 +1,25 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/follow", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.json new file mode 100644 index 000000000000..f7a76f638f67 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.json @@ -0,0 +1,46 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/mine_walls", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/mine_wall", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mineral_wall", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.json new file mode 100644 index 000000000000..98fd4fd007e3 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_move_to.bt.json @@ -0,0 +1,27 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/move_to", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 0, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_planning.dm b/code/datums/ai/basic_mobs/pet_commands/pet_command_planning.dm deleted file mode 100644 index bf4885b817b6..000000000000 --- a/code/datums/ai/basic_mobs/pet_commands/pet_command_planning.dm +++ /dev/null @@ -1,14 +0,0 @@ -/** - * # Pet Planning - * Perform behaviour based on what pet commands you have received. This is delegated to the pet command datum. - * When a command is set, we blackboard a key to our currently active command. - * The blackboard also has a weak reference to every command datum available to us. - * We use the key to figure out which datum to run, then ask it to figure out how to execute its action. - */ -/datum/ai_planning_subtree/pet_planning - -/datum/ai_planning_subtree/pet_planning/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/pet_command/command = controller.blackboard[BB_ACTIVE_PET_COMMAND] - if (!command) - return // Do something else - return command.execute_action(controller) diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.json new file mode 100644 index 000000000000..c7a182918b30 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_play_dead.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/play_dead", + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/play_dead" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.json new file mode 100644 index 000000000000..3c9f545eb663 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner.bt.json @@ -0,0 +1,57 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/protect_owner", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/protect_owner_check" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.json new file mode 100644 index 000000000000..b8cdd00f02f1 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_protect_owner_ranged_glockroach.bt.json @@ -0,0 +1,55 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/protect_owner/ranged/glockroach", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/protect_owner_check" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.json new file mode 100644 index 000000000000..d3d3517b2b77 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.json @@ -0,0 +1,17 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/scatter", + "type": "sequence", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_CURRENT_PET_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.json new file mode 100644 index 000000000000..d374016e90cb --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_stay.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/stay", + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": 0 + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.json new file mode 100644 index 000000000000..baf9fc2e7d4d --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.json @@ -0,0 +1,81 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/swirl", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SWARM_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_PET_ATTACK_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/swirl_around_target" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SWIRL_TURF", + "required_dist": 0, + "finish_on_arrival": true + } + } + ] + } + ] + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.json new file mode 100644 index 000000000000..aa3b6b5b9360 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_targeted_ability.bt.json @@ -0,0 +1,51 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/targeted_ability", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_PET_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.json b/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.json new file mode 100644 index 000000000000..a993a27f8e50 --- /dev/null +++ b/code/datums/ai/basic_mobs/pet_commands/pet_command_untargeted_ability.bt.json @@ -0,0 +1,17 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/untargeted_ability", + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_PET_ACTIVE_ABILITY" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_pet_command" + } + ] +} diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_follow_friend.dm b/code/datums/ai/basic_mobs/pet_commands/pet_follow_friend.dm deleted file mode 100644 index 38a6939c9018..000000000000 --- a/code/datums/ai/basic_mobs/pet_commands/pet_follow_friend.dm +++ /dev/null @@ -1,16 +0,0 @@ -/// Just keep following the target until the command is interrupted -/datum/ai_behavior/pet_follow_friend - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - -/datum/ai_behavior/pet_follow_friend/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if (QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/pet_follow_friend/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - if (QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - return AI_BEHAVIOR_DELAY diff --git a/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm b/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm index c90ffa785900..e69de29bb2d1 100644 --- a/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm +++ b/code/datums/ai/basic_mobs/pet_commands/pet_use_targeted_ability.dm @@ -1,39 +0,0 @@ -/// Pet owners can't see their pet's ability cooldowns so we keep attempting to use an ability until we succeed -/datum/ai_behavior/pet_use_ability - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - -/datum/ai_behavior/pet_use_ability/setup(datum/ai_controller/controller, ability_key, target_key) - . = ..() - var/mob/living/target = controller.blackboard[target_key] - if (QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/pet_use_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) - var/datum/action/cooldown/mob_cooldown/ability = controller.blackboard[ability_key] - var/mob/living/target = controller.blackboard[target_key] - if (QDELETED(ability) || QDELETED(target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - var/mob/pawn = controller.pawn - if(QDELETED(pawn) || ability.InterceptClickOn(pawn, null, target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_INSTANT - -/datum/ai_behavior/pet_use_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/pet_use_ability/then_attack - -/datum/ai_behavior/pet_use_ability/then_attack/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) - . = ..() - if(succeeded) - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack, target_key, BB_PET_TARGETING_STRATEGY) - -/datum/ai_behavior/pet_use_ability/then_attack/short_ranged - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - required_distance = 4 - -/datum/ai_behavior/pet_use_ability/then_attack/long_ranged - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - required_distance = 8 diff --git a/code/datums/ai/basic_mobs/pet_commands/play_dead.dm b/code/datums/ai/basic_mobs/pet_commands/play_dead.dm deleted file mode 100644 index 5a7a09430260..000000000000 --- a/code/datums/ai/basic_mobs/pet_commands/play_dead.dm +++ /dev/null @@ -1,26 +0,0 @@ -/// Pretend to be dead -/datum/ai_behavior/play_dead - -/datum/ai_behavior/play_dead/setup(datum/ai_controller/controller) - . = ..() - var/mob/living/basic/basic_pawn = controller.pawn - if(!istype(basic_pawn) || basic_pawn.stat) // Can't act dead if you're dead - return - basic_pawn.emote("deathgasp", intentional=FALSE) - ADD_TRAIT(basic_pawn, TRAIT_FAKEDEATH, BASIC_MOB_DEATH_TRAIT) - basic_pawn.look_dead() - -/datum/ai_behavior/play_dead/perform(seconds_per_tick, datum/ai_controller/controller) - if(SPT_PROB(10, seconds_per_tick)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/play_dead/finish_action(datum/ai_controller/controller, succeeded) - . = ..() - var/mob/living/basic/basic_pawn = controller.pawn - if(QDELETED(basic_pawn) || basic_pawn.stat) // imagine actually dying while playing dead. hell, imagine being the kid waiting for your pup to get back up :( - return - basic_pawn.visible_message(span_notice("[basic_pawn] miraculously springs back to life!")) - REMOVE_TRAIT(basic_pawn, TRAIT_FAKEDEATH, BASIC_MOB_DEATH_TRAIT) - basic_pawn.look_alive() - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) diff --git a/code/datums/ai/basic_mobs/simple_ability.bt.json b/code/datums/ai/basic_mobs/simple_ability.bt.json new file mode 100644 index 000000000000..3a741326a407 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ability", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_combat.bt.json b/code/datums/ai/basic_mobs/simple_ability_combat.bt.json new file mode 100644 index 000000000000..7415f3130c3a --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_combat.bt.json @@ -0,0 +1,101 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ability_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": false, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_WIZARD_SPELL_COOLDOWN", + "cooldown_duration": "WIZARD_SPELL_COOLDOWN" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_TARGETED_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_SECONDARY_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_BLINK_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 3, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_melee.bt.json b/code/datums/ai/basic_mobs/simple_ability_melee.bt.json new file mode 100644 index 000000000000..7bb2f68b2876 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_melee.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ability_melee", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_melee_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.json b/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.json new file mode 100644 index 000000000000..4da9075ce8a2 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_melee_combat.bt.json @@ -0,0 +1,91 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ability_melee_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_ranged.bt.json b/code/datums/ai/basic_mobs/simple_ability_ranged.bt.json new file mode 100644 index 000000000000..971a7059245c --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_ranged.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ability_ranged", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_ranged_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.json b/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.json new file mode 100644 index 000000000000..78d229fec93d --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_ranged_combat.bt.json @@ -0,0 +1,82 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ability_ranged_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.json b/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.json new file mode 100644 index 000000000000..546fb2ed02ad --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_retaliate.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ability_retaliate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_retaliate_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.json b/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.json new file mode 100644 index 000000000000..9f1548488534 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ability_retaliate_combat.bt.json @@ -0,0 +1,70 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ability_retaliate_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_BASIC_MOB_RETALIATE_LIST" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 3, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_capricious.bt.json b/code/datums/ai/basic_mobs/simple_capricious.bt.json new file mode 100644 index 000000000000..70ba4d20a29d --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_capricious.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_capricious", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_capricious_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_capricious_combat.bt.json b/code/datums/ai/basic_mobs/simple_capricious_combat.bt.json new file mode 100644 index 000000000000..265658831d87 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_capricious_combat.bt.json @@ -0,0 +1,66 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_capricious_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_BASIC_MOB_RETALIATE_LIST" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/capricious_pick_target" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_fearful.bt.json b/code/datums/ai/basic_mobs/simple_fearful.bt.json new file mode 100644 index 000000000000..d4f2d92caebd --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_fearful.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_fearful", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_fearful_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_fearful_combat.bt.json b/code/datums/ai/basic_mobs/simple_fearful_combat.bt.json new file mode 100644 index 000000000000..e4bca3a86142 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_fearful_combat.bt.json @@ -0,0 +1,41 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_fearful_combat", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_goon.bt.json b/code/datums/ai/basic_mobs/simple_goon.bt.json new file mode 100644 index 000000000000..2add30ed0878 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_goon.bt.json @@ -0,0 +1,15 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_goon", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_hostile.bt.json b/code/datums/ai/basic_mobs/simple_hostile.bt.json new file mode 100644 index 000000000000..9e4077fd8b98 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_hostile.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_hostile", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_hostile_combat.bt.json b/code/datums/ai/basic_mobs/simple_hostile_combat.bt.json new file mode 100644 index 000000000000..537387307936 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_hostile_combat.bt.json @@ -0,0 +1,98 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_hostile_combat", + "bindings": { + "bbrbyj7y": { + "label": "combat_speech_behavior", + "default": "" + }, + "bp3p5vvb": { + "label": "walk_chance", + "default": "25" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + }, + { + "type": "leaf", + "behavior": "$bbrbyj7y" + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": "$bp3p5vvb" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.json b/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.json new file mode 100644 index 000000000000..b5a026e01a6a --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_hostile_combat_with_retaliate.bt.json @@ -0,0 +1,107 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_hostile_combat_with_retaliate", + "bindings": { + "bhvqd9fh": { + "label": "speech loop subtree", + "default": "/datum/bt_node/subtree" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "subtree", + "subtype": "$bhvqd9fh" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.json b/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.json new file mode 100644 index 000000000000..3ce7b8cac9b8 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_hostile_obstacles.bt.json @@ -0,0 +1,49 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_hostile_obstacles", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack" + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pick_retaliate_target" + } + ] + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.json b/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.json new file mode 100644 index 000000000000..846e921552a1 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_hostile_obstacles_combat.bt.json @@ -0,0 +1,109 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_hostile_obstacles_combat", + "bindings": { + "b95z0f0c": { + "label": "while_attacking_subtree", + "default": "" + }, + "by235tlw": { + "label": "cant_attack_subtree", + "default": "" + }, + "bqul7l8t": { + "label": "speech_subtree", + "default": "/datum/bt_node/subtree" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "subtree", + "subtype": "$b95z0f0c" + } + ] + } + }, + { + "type": "subtree", + "subtype": "$by235tlw" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "$bqul7l8t" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ranged.bt.json b/code/datums/ai/basic_mobs/simple_ranged.bt.json new file mode 100644 index 000000000000..cc8e48db271d --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ranged.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ranged", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ranged_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ranged_combat.bt.json b/code/datums/ai/basic_mobs/simple_ranged_combat.bt.json new file mode 100644 index 000000000000..6da0fd0be51f --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ranged_combat.bt.json @@ -0,0 +1,77 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ranged_combat", + "bindings": { + "bsjjwub2": { + "label": "idle_behavior", + "default": "/datum/bt_node/subtree/random_walk" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "time_between_perform": "0.6 SECONDS", + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "approach_movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "$bsjjwub2" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.json b/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.json new file mode 100644 index 000000000000..92e64b045f15 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ranged_retaliate.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_ranged_retaliate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ranged_retaliate_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.json b/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.json new file mode 100644 index 000000000000..06747f4e9e8d --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_ranged_retaliate_combat.bt.json @@ -0,0 +1,69 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_ranged_retaliate_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_retaliate.bt.json b/code/datums/ai/basic_mobs/simple_retaliate.bt.json new file mode 100644 index 000000000000..1155f2378688 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_retaliate.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_retaliate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.json b/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.json new file mode 100644 index 000000000000..105e0ef8b7a1 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_retaliate_combat.bt.json @@ -0,0 +1,87 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_retaliate_combat", + "bindings": { + "b2jvnm5d": { + "label": "check_faction", + "default": "FALSE" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": "$b2jvnm5d" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_skirmisher.bt.json b/code/datums/ai/basic_mobs/simple_skirmisher.bt.json new file mode 100644 index 000000000000..3a671abba85b --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_skirmisher.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_skirmisher", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_skirmisher_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.json b/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.json new file mode 100644 index 000000000000..6c5d60b593f2 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_skirmisher_combat.bt.json @@ -0,0 +1,92 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_skirmisher_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_skittish.bt.json b/code/datums/ai/basic_mobs/simple_skittish.bt.json new file mode 100644 index 000000000000..36828f42ae53 --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_skittish.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/simple/simple_skittish", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_skittish_combat" + } + ] +} diff --git a/code/datums/ai/basic_mobs/simple_skittish_combat.bt.json b/code/datums/ai/basic_mobs/simple_skittish_combat.bt.json new file mode 100644 index 000000000000..6db93fc782db --- /dev/null +++ b/code/datums/ai/basic_mobs/simple_skittish_combat.bt.json @@ -0,0 +1,41 @@ +{ + "dm_type": "/datum/bt_node/subtree/simple_skittish_combat", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] +} diff --git a/code/datums/ai/basic_mobs/talk.bt.json b/code/datums/ai/basic_mobs/talk.bt.json new file mode 100644 index 000000000000..20584cd412b7 --- /dev/null +++ b/code/datums/ai/basic_mobs/talk.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/talk", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" +} diff --git a/code/datums/ai/basic_mobs/target_sources/_target_source.dm b/code/datums/ai/basic_mobs/target_sources/_target_source.dm new file mode 100644 index 000000000000..0a501c5ca131 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/_target_source.dm @@ -0,0 +1,106 @@ +/// Singleton datum that defines how an acquire_target behavior gathers candidates. +/// Subtype collect_candidates() to change what atoms are considered. +/// Subtype with a typecache var to pre-filter by type before targeting_strategy validation. +/datum/target_source + +/// Returns a list of candidate atoms for the behavior to filter and select from. +/datum/target_source/proc/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + return list() + +/// Gathers nearby atoms via oview(). No type pre-filtering. +/datum/target_source/oview + +/datum/target_source/oview/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = oview(range, pawn) + return candidates + +/// Gathers nearby atoms via oview(), pre-filtered by a typecache +/datum/target_source/oview_typed + /// Optional typecache for pre-filtering candidates; null means no type pre-filter. + var/list/typecache + +/datum/target_source/oview_typed/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + if(isnull(typecache)) + CRASH("[pawn] using [controller] ran [src] with no typecache.") + var/list/candidates = typecache_filter_list(oview(range, pawn), typecache) + return candidates + +/// Gathers nearby atoms via oview(), pre-filtered by a typecache stored in a blackboard key. +/// Use this when the typecache varies per mob species (e.g. BB_BASIC_FOODS). +/datum/target_source/oview_typed/from_bb_key + /// Blackboard key whose value is the typecache list to filter by. + var/typecache_key + +/datum/target_source/oview_typed/from_bb_key/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/bb_typecache = controller.blackboard[typecache_key] + var/list/candidates + if(isnull(bb_typecache)) + candidates = oview(range, pawn) + else + candidates = typecache_filter_list(oview(range, pawn), bb_typecache) + return candidates + +/// Gathers nearby atoms via hearers() plus any hostile machines on the same z-level. (I should probably split out the hostile machine part but oh well) +/datum/target_source/hearers + +/datum/target_source/hearers/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = hearers(range, get_turf(pawn)) - pawn + var/turf/mob_turf = get_turf(pawn) + if(mob_turf?.z) + for(var/atom/hostile_machine as anything in GLOB.hostile_machines_by_z[mob_turf.z]) + if(can_see(pawn, hostile_machine, range)) + candidates += hostile_machine + + return candidates + +/// Gathers turfs in range via RANGE_TURFS(). +/datum/target_source/range_turfs + +/datum/target_source/range_turfs/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + return RANGE_TURFS(range, pawn) + +/// Gathers items currently held in the pawn's hands. +/datum/target_source/held_items + +/datum/target_source/held_items/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + return pawn.held_items || list() + +/// Reads a typecache from BB_BASIC_FOODS and filters oview candidates by it. +/// For mobs whose food list varies by species (set in Initialize via set_blackboard_key). +/datum/target_source/oview_typed/from_bb_key/basic_foods + typecache_key = BB_BASIC_FOODS + +/// Reads a typecache from BB_HUNTABLE_PREY and filters oview candidates by it. +/datum/target_source/oview_typed/from_bb_key/huntable_prey + typecache_key = BB_HUNTABLE_PREY + +/// Gathers items the pawn is carrying that match the prey typecache in BB_HUNTABLE_PREY. +/datum/target_source/carried_huntable_prey + +/datum/target_source/carried_huntable_prey/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/prey = controller.blackboard[BB_HUNTABLE_PREY] + if(isnull(prey)) + return list() + return typecache_filter_list(pawn.contents, prey) + +/// Reads candidates directly from a blackboard list. No spatial filtering; range is ignored. +/datum/target_source/from_bb_list + var/list_key + +/datum/target_source/from_bb_list/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + return controller.blackboard[list_key] || list() + +/// Reads from BB_BASIC_MOB_RETALIATE_LIST. +/datum/target_source/from_bb_list/retaliate_list + list_key = BB_BASIC_MOB_RETALIATE_LIST + + +/datum/target_source/oview_items + +/datum/target_source/oview_items/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/obj/item/item_candidate in oview(range, pawn)) + candidates += item_candidate + + return candidates diff --git a/code/datums/ai/basic_mobs/target_sources/held_items_then_oview.dm b/code/datums/ai/basic_mobs/target_sources/held_items_then_oview.dm new file mode 100644 index 000000000000..721a7c09545a --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/held_items_then_oview.dm @@ -0,0 +1,11 @@ +/// Returns the pawn's held items first, then nearby atoms via oview(). Used for food searches that prefer held items. +/datum/target_source/held_items_then_oview + +/datum/target_source/held_items_then_oview/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = list() + for(var/obj/item/candidate_item as anything in pawn.held_items) + if(isnull(candidate_item)) + continue + candidates += candidate_item + candidates += oview(range, pawn) + return candidates diff --git a/code/datums/ai/basic_mobs/target_sources/held_items_typed.dm b/code/datums/ai/basic_mobs/target_sources/held_items_typed.dm new file mode 100644 index 000000000000..3f12cfa3957d --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/held_items_typed.dm @@ -0,0 +1,13 @@ +/// Gathers held items matching a fixed typepath. +/datum/target_source/held_items_typed + var/locate_typepath + +/datum/target_source/held_items_typed/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = list() + for(var/atom/candidate as anything in pawn.held_items) + if(istype(candidate, locate_typepath)) + candidates += candidate + return candidates + +/datum/target_source/held_items_typed/instrument + locate_typepath = /obj/item/instrument diff --git a/code/datums/ai/basic_mobs/target_sources/mobs_in_oview.dm b/code/datums/ai/basic_mobs/target_sources/mobs_in_oview.dm new file mode 100644 index 000000000000..e0d0e34e3f9d --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/mobs_in_oview.dm @@ -0,0 +1,20 @@ +/datum/target_source/oview_living + +/datum/target_source/oview_living/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/mob/living/living_candidate in oview(range, pawn)) + candidates += living_candidate + + return candidates + + +/datum/target_source/oview_raptor_babies + +/datum/target_source/oview_raptor_babies/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/mob/living/basic/raptor/candidate in oview(range, pawn)) + candidates += candidate + + return candidates diff --git a/code/datums/ai/basic_mobs/target_sources/near_village_humans.dm b/code/datums/ai/basic_mobs/target_sources/near_village_humans.dm new file mode 100644 index 000000000000..893fc25a4617 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/near_village_humans.dm @@ -0,0 +1,12 @@ +/// Gathers nearby carbon humans within BB_MAXIMUM_DISTANCE_TO_VILLAGE of the BB_HOME_VILLAGE anchor. +/datum/target_source/near_village_humans + +/datum/target_source/near_village_humans/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/atom/anchor = controller.blackboard[BB_HOME_VILLAGE] + var/max_dist = controller.blackboard[BB_MAXIMUM_DISTANCE_TO_VILLAGE] || range + var/list/candidates = list() + for(var/mob/living/carbon/human/candidate in oview(max_dist, pawn)) + if(!isnull(anchor) && get_dist(candidate, anchor) > max_dist) + continue + candidates += candidate + return candidates diff --git a/code/datums/ai/basic_mobs/target_sources/oview_single_type.dm b/code/datums/ai/basic_mobs/target_sources/oview_single_type.dm new file mode 100644 index 000000000000..0c8835332a41 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/oview_single_type.dm @@ -0,0 +1,58 @@ +/** + * Gathers all targets of a type in the area. This is a define so we can do cheap loops over oview with astype. Yeah it sucks. + * You can do /datum/target_source/oview_single_type/[sub_type] to use it + */ +#define OVIEW_TARGET_SOURCE(subtype, path) \ +/datum/target_source/oview_single_type/##subtype/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) { \ + var/list/candidates = list(); \ + for(var/##path/candidate in oview(range, pawn)) { \ + candidates += candidate; \ + } \ + return candidates; \ +} + +/// Abstract parent for target sources that return every nearby atom of a single fixed type. +/// Subtypes are generated with the OVIEW_TARGET_SOURCE() macro above. +/datum/target_source/oview_single_type + +OVIEW_TARGET_SOURCE(carbon_mob, mob/living/carbon) +OVIEW_TARGET_SOURCE(human_mob, mob/living/carbon/human) +OVIEW_TARGET_SOURCE(living_mob, mob/living) +OVIEW_TARGET_SOURCE(disposal_unit, obj/machinery/disposal) +OVIEW_TARGET_SOURCE(paper, obj/item/paper) +OVIEW_TARGET_SOURCE(watering_can, obj/item/reagent_containers/cup/watering_can) +OVIEW_TARGET_SOURCE(ore_stand, obj/structure/ore_container/material_stand) +OVIEW_TARGET_SOURCE(flora_tree, obj/structure/flora/tree) +OVIEW_TARGET_SOURCE(vent_pump, obj/machinery/atmospherics/components/unary/vent_pump) +OVIEW_TARGET_SOURCE(tribal_chief, mob/living/basic/mining/mook/worker/tribal_chief) +OVIEW_TARGET_SOURCE(apc, obj/machinery/power/apc) +OVIEW_TARGET_SOURCE(machine, obj/machinery) +OVIEW_TARGET_SOURCE(beehive, obj/structure/beebox) +OVIEW_TARGET_SOURCE(penguin_egg, obj/item/food/egg/penguin_egg) +OVIEW_TARGET_SOURCE(raptor, mob/living/basic/raptor) +OVIEW_TARGET_SOURCE(raptor_trough, obj/structure/ore_container/food_trough/raptor_trough) +OVIEW_TARGET_SOURCE(gutlunch_trough, obj/structure/ore_container/food_trough/gutlunch_trough) +OVIEW_TARGET_SOURCE(mouse, mob/living/basic/mouse) +OVIEW_TARGET_SOURCE(oven, obj/machinery/oven/range) +OVIEW_TARGET_SOURCE(cable, obj/structure/cable) +OVIEW_TARGET_SOURCE(donut, obj/item/food/donut) +OVIEW_TARGET_SOURCE(hydroponics, obj/machinery/hydroponics) +OVIEW_TARGET_SOURCE(cheese, obj/item/food/cheese) +OVIEW_TARGET_SOURCE(piano_synth, obj/item/instrument/piano_synth) +OVIEW_TARGET_SOURCE(orbie, mob/living/basic/orbie) +OVIEW_TARGET_SOURCE(ore_vent, obj/structure/ore_vent) +OVIEW_TARGET_SOURCE(mushroom_food, obj/item/food/grown/mushroom) +OVIEW_TARGET_SOURCE(ore, obj/item/stack/ore) +OVIEW_TARGET_SOURCE(minebot_target, obj/effect/temp_visual/minebot_target) +OVIEW_TARGET_SOURCE(node_drone, mob/living/basic/node_drone) +OVIEW_TARGET_SOURCE(icy_rock, obj/structure/flora/rock/icy) +OVIEW_TARGET_SOURCE(ice_whelp, mob/living/basic/mining/ice_whelp) +OVIEW_TARGET_SOURCE(hivebot, mob/living/basic/hivebot) +OVIEW_TARGET_SOURCE(carrot, obj/item/food/grown/carrotlike/carrot) +OVIEW_TARGET_SOURCE(ants, obj/effect/decal/cleanable/ants) +OVIEW_TARGET_SOURCE(cat_house, obj/structure/cat_house) +OVIEW_TARGET_SOURCE(honeycomb, obj/item/food/honeycomb) +OVIEW_TARGET_SOURCE(kitten, mob/living/basic/pet/cat/kitten) +OVIEW_TARGET_SOURCE(deer_animals, mob/living/basic/deer) + +#undef OVIEW_TARGET_SOURCE diff --git a/code/datums/ai/basic_mobs/target_sources/oview_typed_from_bb_key.dm b/code/datums/ai/basic_mobs/target_sources/oview_typed_from_bb_key.dm new file mode 100644 index 000000000000..49c526fd74cc --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/oview_typed_from_bb_key.dm @@ -0,0 +1,19 @@ +/// Subtypes of oview_typed/from_bb_key for interaction searches. + +/datum/target_source/oview_typed/from_bb_key/stationary_targets + typecache_key = BB_STATIONARY_TARGETS + +/datum/target_source/oview_typed/from_bb_key/mook_heal_targets + typecache_key = BB_MOOK_HEAL_TARGETS + +/datum/target_source/oview_typed/from_bb_key/bonfire_targets + typecache_key = BB_BONFIRE_TARGETS + +/datum/target_source/oview_typed/from_bb_key/hunt_target_list + typecache_key = BB_HUNT_TARGET_LIST + +/datum/target_source/oview_typed/from_bb_key/turtle_headbutt_types + typecache_key = BB_TURTLE_HEADBUTT_TYPES + +/datum/target_source/oview_typed/from_bb_key/turtle_flora_types + typecache_key = BB_TURTLE_FLORA_TYPES diff --git a/code/datums/ai/basic_mobs/target_sources/range_turfs_typecache_visible.dm b/code/datums/ai/basic_mobs/target_sources/range_turfs_typecache_visible.dm new file mode 100644 index 000000000000..d8b2617c2c14 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/range_turfs_typecache_visible.dm @@ -0,0 +1,34 @@ +/// Gathers visible turfs in range matching a typecache. Shuffled for variety. +/datum/target_source/range_turfs/typecache_visible + var/list/typecache + +/datum/target_source/range_turfs/typecache_visible/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/found = RANGE_TURFS(range, pawn) + var/list/valid = list() + for(var/turf/candidate as anything in found) + if(!is_type_in_typecache(candidate, typecache)) + continue + if(can_see(pawn, candidate, range)) + valid += candidate + return valid + +/datum/target_source/range_turfs/typecache_visible/deer_grass + typecache = list(/turf/open/floor/grass, /turf/open/misc/grass) + +/datum/target_source/range_turfs/typecache_visible/deer_grass/New() + . = ..() + typecache = typecacheof(typecache) + +/datum/target_source/range_turfs/typecache_visible/deer_water + typecache = list(/turf/open/water) + +/datum/target_source/range_turfs/typecache_visible/deer_water/New() + . = ..() + typecache = typecacheof(typecache) + +/datum/target_source/range_turfs/typecache_visible/ice + typecache = list(/turf/open/misc/ice) + +/datum/target_source/range_turfs/typecache_visible/ice/New() + . = ..() + typecache = typecacheof(typecache) diff --git a/code/datums/ai/basic_mobs/target_sources/slime_source.dm b/code/datums/ai/basic_mobs/target_sources/slime_source.dm new file mode 100644 index 000000000000..f4597cedde39 --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/slime_source.dm @@ -0,0 +1,11 @@ +/datum/target_source/oview_living_no_slimes + +/datum/target_source/oview_living_no_slimes/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/mob/living/living_candidate in oview(range, pawn)) + if(isslime(living_candidate)) + continue + candidates += living_candidate + + return candidates diff --git a/code/datums/ai/basic_mobs/target_sources/turfs_in_oview.dm b/code/datums/ai/basic_mobs/target_sources/turfs_in_oview.dm new file mode 100644 index 000000000000..5dbf4f8ba91e --- /dev/null +++ b/code/datums/ai/basic_mobs/target_sources/turfs_in_oview.dm @@ -0,0 +1,23 @@ + +/datum/target_source/oview_water_turfs + +/datum/target_source/oview_water_turfs/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/turf/open/water/water_turf in oview(range, pawn)) + candidates += water_turf + + return candidates + + +/datum/target_source/oview_land_turfs + +/datum/target_source/oview_water_turfs/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + . = ..() + var/list/candidates = list() + for(var/turf/open/potential_floor in oview(range, pawn)) + if(iswaterturf(potential_floor)) + continue + candidates += potential_floor + + return candidates diff --git a/code/datums/ai/basic_mobs/targeting_strategies/_targeting_strategy.dm b/code/datums/ai/basic_mobs/targeting_strategies/_targeting_strategy.dm index 5720602c6fc7..9e6e61cab28c 100644 --- a/code/datums/ai/basic_mobs/targeting_strategies/_targeting_strategy.dm +++ b/code/datums/ai/basic_mobs/targeting_strategies/_targeting_strategy.dm @@ -2,25 +2,41 @@ ///Global, just like ai_behaviors /datum/targeting_strategy -///Returns true or false depending on if the target can be attacked by the mob -/datum/targeting_strategy/proc/can_attack(mob/living/living_mob, atom/target, vision_range) - return +///Returns true or false depending on if the target can be attacked by the mob. +///Base proc checks if target is within vision_range distance. +/datum/targeting_strategy/proc/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + if(QDELETED(target)) + return FALSE -///Returns something the target might be hiding inside of + if(!vision_range) + return TRUE + + return get_dist(living_mob, target) <= vision_range + +/// Returns an atom the target might be hiding inside of, or null if none. /datum/targeting_strategy/proc/find_hidden_mobs(mob/living/living_mob, atom/target) - var/atom/target_hiding_location - if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper)) - target_hiding_location = target.loc - return target_hiding_location + return null + +/// Returns TRUE if we should keep tracking an existing target when no new candidates are visible. +/// Called with the loss range (typically larger than vision_range). +/// Default delegates to is_valid_target so all normal checks still apply. +/datum/targeting_strategy/proc/can_keep_target(mob/living/living_mob, atom/target, range, datum/ai_controller/controller = null) + return is_valid_target(living_mob, target, range, controller) /// Simply always returns true if you have a target, so only use this if you're pre-checking the targets somewhere else /datum/targeting_strategy/anything -/datum/targeting_strategy/anything/can_attack(mob/living/living_mob, atom/target, vision_range) - return !!target +/datum/targeting_strategy/anything/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + return TRUE ///A very simple targeting strategy that checks that the target is a valid fishing spot. /datum/targeting_strategy/fishing -/datum/targeting_strategy/fishing/can_attack(mob/living/living_mob, atom/target, vision_range) +/datum/targeting_strategy/fishing/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE return HAS_TRAIT(target, TRAIT_FISHING_SPOT) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/accessible_cable.dm b/code/datums/ai/basic_mobs/targeting_strategies/accessible_cable.dm new file mode 100644 index 000000000000..7a62b9d4c02b --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/accessible_cable.dm @@ -0,0 +1,14 @@ +/// Accepts visible cables sitting on accessible open floor tiles. +/datum/targeting_strategy/accessible_cable + +/datum/targeting_strategy/accessible_cable/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/structure/cable/candidate = target + if(!istype(candidate) || !can_see(living_mob, candidate, vision_range)) + return FALSE + var/turf/open/floor/below_the_cable = get_turf(candidate) + if(!istype(below_the_cable)) + return FALSE + return below_the_cable.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/ally_mob.dm b/code/datums/ai/basic_mobs/targeting_strategies/ally_mob.dm new file mode 100644 index 000000000000..e881ccafd5d3 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/ally_mob.dm @@ -0,0 +1,10 @@ +/// Accepts visible mobs that are allies of the pawn (and not the pawn itself). +/datum/targeting_strategy/ally_mob + +/datum/targeting_strategy/ally_mob/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(target == living_mob || !living_mob.has_ally(target)) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/baby_raptor.dm b/code/datums/ai/basic_mobs/targeting_strategies/baby_raptor.dm new file mode 100644 index 000000000000..bcdcdce2fdf4 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/baby_raptor.dm @@ -0,0 +1,11 @@ +/// Accepts visible, living baby raptors. ONLY USE ON BABY RAPTORS WE ASSUME IT IS ONE +/datum/targeting_strategy/healthy_raptor_baby + +/datum/targeting_strategy/healthy_raptor_baby/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/basic/raptor/candidate = target + if(candidate.stat == DEAD || candidate.growth_stage != RAPTOR_BABY) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm b/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm index be1d1484225c..80aaf28a9fcf 100644 --- a/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm +++ b/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm @@ -1,20 +1,37 @@ /datum/targeting_strategy/basic + ///Whether we ignore faction checks + var/ignore_target_status = FALSE /// When we do our basic faction check, do we look for exact faction matches? var/check_factions_exactly = FALSE /// Whether we care for seeing the target or not var/ignore_sight = FALSE + /// Whether we skip faction checks entirely, never rejecting a target over factions + var/ignore_faction = FALSE + /// If TRUE the faction check is inverted, making us only target mobs that DO share a faction with us + var/invert_faction_check = FALSE + /// Set to TRUE on subtypes that override faction_check() with custom logic, routing the check through the proc instead of the inlined macro + var/custom_faction_check = FALSE /// Blackboard key containing the minimum stat of a living mob to target var/minimum_stat_key = BB_TARGET_MINIMUM_STAT /// If this blackboard key is TRUE, makes us only target wounded mobs var/target_wounded_key -/datum/targeting_strategy/basic/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) + // checks are ordered cheapest first so invalid targets are rejected before the expensive sight check + if(isturf(the_target) || isnull(the_target)) // bail out on invalids + return FALSE + var/datum/ai_controller/basic_controller/our_controller = living_mob.ai_controller if(isnull(our_controller)) return FALSE - if(isturf(the_target) || isnull(the_target)) // bail out on invalids + if(living_mob.see_invisible < the_target.invisibility) //Target's invisible to us, forget it + return FALSE + + if(!isturf(living_mob.loc)) + return FALSE + if(isturf(the_target.loc) && living_mob.z != the_target.z) // z check will always fail if target is in a mech or pawn is shapeshifted or jaunting return FALSE if(isobj(the_target.loc)) @@ -31,35 +48,32 @@ if (vision_range && get_dist(living_mob, the_target) > vision_range) return FALSE - if(!ignore_sight && !can_see(living_mob, the_target, vision_range)) //Target has moved behind cover and we have lost line of sight to it - return FALSE - - if(living_mob.see_invisible < the_target.invisibility) //Target's invisible to us, forget it - return FALSE - - if(!isturf(living_mob.loc)) - return FALSE - if(isturf(the_target.loc) && living_mob.z != the_target.z) // z check will always fail if target is in a mech or pawn is shapeshifted or jaunting - return FALSE - if(isliving(the_target)) //Targeting vs living mobs - var/mob/living/living_target = the_target - if(faction_check(our_controller, living_mob, living_target)) - return FALSE - if(living_target.stat > our_controller.blackboard[minimum_stat_key]) - return FALSE - if(target_wounded_key && our_controller.blackboard[target_wounded_key] && living_target.health == living_target.maxHealth) - return FALSE + if(!ignore_target_status) + var/mob/living/living_target = the_target + if(custom_faction_check ? faction_check(our_controller, living_mob, living_target) : TARGETING_FACTION_CHECK(src, our_controller, living_mob, living_target)) + return FALSE - return TRUE + var/checked_stat = living_target.stat + if(our_controller.blackboard[BB_TREAT_UNCONSCIOUS_AS_HARDCRIT] && IS_UNCONSCIOUS(living_target)) + checked_stat = max(checked_stat, HARD_CRIT) - if(ismecha(the_target)) //Targeting vs mechas + if(checked_stat > our_controller.blackboard[minimum_stat_key]) + return FALSE + if(target_wounded_key && our_controller.blackboard[target_wounded_key] && living_target.health == living_target.maxHealth) + return FALSE + + else if(ismecha(the_target)) //Targeting vs mechas var/obj/vehicle/sealed/mecha/M = the_target + var/valid_occupant = FALSE for(var/occupant in M.occupants) - if(can_attack(living_mob, occupant)) //Can we attack any of the occupants? - return TRUE + if(is_valid_target(living_mob, occupant)) //Can we attack any of the occupants? + valid_occupant = TRUE + break + if(!valid_occupant) + return FALSE - if(istype(the_target, /obj/machinery/porta_turret)) //Cringe turret! kill it! + else if(istype(the_target, /obj/machinery/porta_turret)) //Cringe turret! kill it! var/obj/machinery/porta_turret/P = the_target if(P.in_faction(living_mob)) //Don't attack if the turret is in the same faction return FALSE @@ -67,21 +81,33 @@ return FALSE if(P.machine_stat & BROKEN) //Or turrets that are already broken return FALSE - return TRUE - return FALSE + else //Not a type of thing we can target + return FALSE -/// Returns true if the mob and target share factions -/datum/targeting_strategy/basic/proc/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) - if (controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || controller.blackboard[BB_TEMPORARILY_IGNORE_FACTION]) + if(!ignore_sight && !can_see(living_mob, the_target, vision_range)) //Sight check goes last, it's by far the most expensive return FALSE - return living_mob.faction_check_atom(the_target, exact_match = check_factions_exactly) + return TRUE + +/datum/targeting_strategy/basic/find_hidden_mobs(mob/living/living_mob, atom/target) + . = ..() + if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper)) + return target.loc + return null + +/datum/targeting_strategy/basic/can_keep_target(mob/living/living_mob, atom/target, range) + return can_see(living_mob, target, range) + +/// Returns true if the mob and target share factions. +/// Slow path for subtypes with custom_faction_check set; everything else uses TARGETING_FACTION_CHECK directly +/datum/targeting_strategy/basic/proc/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) + return TARGETING_FACTION_CHECK(src, controller, living_mob, the_target) /// Subtype more forgiving for items. /// Careful, this can go wrong and keep a mob hyper-focused on an item it can't lose aggro on /datum/targeting_strategy/basic/allow_items -/datum/targeting_strategy/basic/allow_items/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/allow_items/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) . = ..() if(isitem(the_target)) // trust fall exercise @@ -89,7 +115,7 @@ /datum/targeting_strategy/basic/require_traits -/datum/targeting_strategy/basic/require_traits/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/require_traits/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) . = ..() if (!.) return FALSE @@ -109,7 +135,7 @@ /// If true, we will return mobs which are the same size as us. var/inclusive = TRUE -/datum/targeting_strategy/basic/of_size/can_attack(mob/living/owner, atom/target, vision_range) +/datum/targeting_strategy/basic/of_size/is_valid_target(mob/living/owner, atom/target, vision_range, datum/ai_controller/controller = null) if(!isliving(target)) return FALSE . = ..() @@ -136,13 +162,11 @@ /// Makes the mob only attack their own faction. Useful mostly if their attacks do something helpful (e.g. healing touch). /datum/targeting_strategy/basic/same_faction - -/datum/targeting_strategy/basic/same_faction/faction_check(mob/living/living_mob, mob/living/the_target) - return !..() // inverts logic to ONLY target mobs that share a faction + invert_faction_check = TRUE /datum/targeting_strategy/basic/allow_turfs -/datum/targeting_strategy/basic/allow_turfs/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/allow_turfs/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) if(isturf(the_target)) return TRUE return ..() @@ -150,7 +174,7 @@ /// Subtype which searches for mobs that havent been gutted by megafauna /datum/targeting_strategy/basic/no_gutted_mobs -/datum/targeting_strategy/basic/no_gutted_mobs/can_attack(mob/living/owner, mob/living/target, vision_range) +/datum/targeting_strategy/basic/no_gutted_mobs/is_valid_target(mob/living/owner, mob/living/target, vision_range, datum/ai_controller/controller = null) if(!istype(target) || target.has_status_effect(/datum/status_effect/gutted)) return FALSE return ..() @@ -160,7 +184,7 @@ /datum/targeting_strategy/basic/exact_match/ignore_friends -/datum/targeting_strategy/basic/exact_match/ignore_friends/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/exact_match/ignore_friends/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) . = ..() if (!.) return FALSE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/beamable_hydro.dm b/code/datums/ai/basic_mobs/targeting_strategies/beamable_hydro.dm new file mode 100644 index 000000000000..f86c5e65c29e --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/beamable_hydro.dm @@ -0,0 +1,11 @@ +/// Accepts hydroponics trays whose plant health is below the seed's endurance threshold. +/datum/targeting_strategy/beamable_hydro + +/datum/targeting_strategy/beamable_hydro/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/hydroponics/hydro = target + if(!istype(hydro) || isnull(hydro.myseed)) + return FALSE + return hydro.plant_health < hydro.myseed.endurance diff --git a/code/datums/ai/basic_mobs/targeting_strategies/befriendable_cultist.dm b/code/datums/ai/basic_mobs/targeting_strategies/befriendable_cultist.dm new file mode 100644 index 000000000000..8b0f1dafb636 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/befriendable_cultist.dm @@ -0,0 +1,13 @@ +/// Accepts carbon cultists that the pawn has not already befriended. +/datum/targeting_strategy/befriendable_cultist + +/datum/targeting_strategy/befriendable_cultist/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!iscarbon(target)) + return FALSE + var/mob/living/carbon/carbon_target = target + if(!IS_CULTIST(carbon_target)) + return FALSE + return !living_mob.has_ally(carbon_target) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/cat_food.dm b/code/datums/ai/basic_mobs/targeting_strategies/cat_food.dm new file mode 100644 index 000000000000..f5e1e98feecf --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/cat_food.dm @@ -0,0 +1,12 @@ +/// Accepts visible food that has no kitten (other than the pawn) nearby. +/// Pair with a food typecache source (e.g. oview_typed/from_bb_key/basic_foods). +/datum/targeting_strategy/cat_food + +/datum/targeting_strategy/cat_food/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/nearby_kitten = locate(/mob/living/basic/pet/cat/kitten) in oview(2, target) + if(nearby_kitten && nearby_kitten != living_mob) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/chargeable_apc.dm b/code/datums/ai/basic_mobs/targeting_strategies/chargeable_apc.dm new file mode 100644 index 000000000000..601e790c95db --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/chargeable_apc.dm @@ -0,0 +1,14 @@ +/// Accepts APCs that have a cell which is not at full charge, and are visible to the pawn. +/datum/targeting_strategy/chargeable_apc + +/datum/targeting_strategy/chargeable_apc/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/power/apc/candidate = target + if(!istype(candidate) || !candidate.cell) + return FALSE + var/obj/item/stock_parts/power_store/cell/apc_cell = candidate.cell + if(apc_cell.charge == apc_cell.maxcharge) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/conscious_human.dm b/code/datums/ai/basic_mobs/targeting_strategies/conscious_human.dm new file mode 100644 index 000000000000..4f94b38af02a --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/conscious_human.dm @@ -0,0 +1,13 @@ +/// Targets non-incapable human carbons with a mind. Used for interaction targets (traders, etc.). +/datum/targeting_strategy/capable_human + +/datum/targeting_strategy/capable_human/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!ishuman(the_target)) + return FALSE + var/mob/living/carbon/human/human_target = the_target + if(human_target.incapacitated || isnull(human_target.mind)) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/conscious_mob.dm b/code/datums/ai/basic_mobs/targeting_strategies/conscious_mob.dm new file mode 100644 index 000000000000..4f86ab32e514 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/conscious_mob.dm @@ -0,0 +1,11 @@ +/// Accepts living mobs that are not dead or incapacitated, and visible to the pawn. +/datum/targeting_strategy/conscious_mob + +/datum/targeting_strategy/conscious_mob/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate) || candidate.incapacitated) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/conscious_snail.dm b/code/datums/ai/basic_mobs/targeting_strategies/conscious_snail.dm new file mode 100644 index 000000000000..1acef32535cb --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/conscious_snail.dm @@ -0,0 +1,13 @@ +/// Accepts visible, conscious carbon mobs of the snail species. +/datum/targeting_strategy/conscious_snail + +/datum/targeting_strategy/conscious_snail/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/carbon/candidate = target + if(!istype(candidate) || IS_UNCONSCIOUS_OR_CRIT(candidate)) + return FALSE + if(!is_species(candidate, /datum/species/snail)) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/damaged_eyes.dm b/code/datums/ai/basic_mobs/targeting_strategies/damaged_eyes.dm new file mode 100644 index 000000000000..3b10ad9579fc --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/damaged_eyes.dm @@ -0,0 +1,17 @@ +/// Accepts carbon mobs whose eyes are damaged at or beyond the controller's BB_EYE_DAMAGE_THRESHOLD. +/datum/targeting_strategy/damaged_eyes + +/datum/targeting_strategy/damaged_eyes/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!iscarbon(target)) + return FALSE + var/threshold = controller?.blackboard[BB_EYE_DAMAGE_THRESHOLD] + if(!threshold) + return FALSE + var/mob/living/carbon/carbon_target = target + var/obj/item/organ/eyes/eyes = carbon_target.get_organ_slot(ORGAN_SLOT_EYES) + if(isnull(eyes) || eyes.damage < threshold) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/damaged_machine.dm b/code/datums/ai/basic_mobs/targeting_strategies/damaged_machine.dm new file mode 100644 index 000000000000..459733c7e885 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/damaged_machine.dm @@ -0,0 +1,11 @@ +/// Accepts machinery below max integrity that is visible to the pawn. +/datum/targeting_strategy/damaged_machine + +/datum/targeting_strategy/damaged_machine/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/candidate = target + if(!istype(candidate) || candidate.get_integrity() >= candidate.max_integrity) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/dead_mob.dm b/code/datums/ai/basic_mobs/targeting_strategies/dead_mob.dm new file mode 100644 index 000000000000..0cce3346d4b0 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/dead_mob.dm @@ -0,0 +1,20 @@ +/// Accepts dead living mobs that are visible to the pawn. +/datum/targeting_strategy/dead_mob + +/datum/targeting_strategy/dead_mob/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate) || candidate.stat != DEAD) + return FALSE + return TRUE + +/// As dead_mob, but rejects corpses already being dragged by something. +/datum/targeting_strategy/dead_mob/not_pulled + +/datum/targeting_strategy/dead_mob/not_pulled/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + var/mob/living/candidate = target + if(isliving(candidate) && candidate.pulledby) + return FALSE + return ..() diff --git a/code/datums/ai/basic_mobs/targeting_strategies/decorated_donut.dm b/code/datums/ai/basic_mobs/targeting_strategies/decorated_donut.dm new file mode 100644 index 000000000000..b6bb107bb7f9 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/decorated_donut.dm @@ -0,0 +1,11 @@ +/// Accepts decorated donuts that are visible to the pawn. +/datum/targeting_strategy/decorated_donut + +/datum/targeting_strategy/decorated_donut/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/item/food/donut/candidate = target + if(!istype(candidate) || !candidate.is_decorated) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/dont_target_friends.dm b/code/datums/ai/basic_mobs/targeting_strategies/dont_target_friends.dm index 268b4ca88428..e13d02fdccee 100644 --- a/code/datums/ai/basic_mobs/targeting_strategies/dont_target_friends.dm +++ b/code/datums/ai/basic_mobs/targeting_strategies/dont_target_friends.dm @@ -1,5 +1,6 @@ /// Don't target an atom in our friends list (or turfs), anything else is fair game /datum/targeting_strategy/basic/not_friends + ignore_faction = TRUE //friends dont care about factions /// Stop regarding someone as a valid target once they pass this stat level, setting it to DEAD means you will happily attack corpses var/attack_until_past_stat = HARD_CRIT /// If we can try to closed turfs or not @@ -8,7 +9,7 @@ var/attack_obj = FALSE ///Returns true or false depending on if the target can be attacked by the mob -/datum/targeting_strategy/basic/not_friends/can_attack(mob/living/living_mob, atom/target, vision_range) +/datum/targeting_strategy/basic/not_friends/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) if(attack_closed_turf && isclosedturf(target)) return TRUE @@ -21,10 +22,6 @@ return ..() -///friends dont care about factions -/datum/targeting_strategy/basic/not_friends/faction_check(mob/living/living_mob, mob/living/the_target) - return FALSE - /datum/targeting_strategy/basic/not_friends/attack_closed_turfs attack_closed_turf = TRUE @@ -40,7 +37,7 @@ /// Subtype that allows us to target items while deftly avoiding attacking our allies. Be careful when it comes to targeting items as an AI could get trapped targeting something it can't destroy. /datum/targeting_strategy/basic/not_friends/allow_items -/datum/targeting_strategy/basic/not_friends/allow_items/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/not_friends/allow_items/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) . = ..() if(isitem(the_target)) // trust fall exercise diff --git a/code/datums/ai/basic_mobs/targeting_strategies/drillable_ice.dm b/code/datums/ai/basic_mobs/targeting_strategies/drillable_ice.dm new file mode 100644 index 000000000000..1f092ce20d60 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/drillable_ice.dm @@ -0,0 +1,12 @@ +/// Accepts ice turfs that can have a hole made in them and are visible to the pawn. +/// Pair with a range_turfs source. +/datum/targeting_strategy/drillable_ice + +/datum/targeting_strategy/drillable_ice/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/turf/open/misc/ice/candidate = target + if(!istype(candidate) || !candidate.can_make_hole) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/empty_paper.dm b/code/datums/ai/basic_mobs/targeting_strategies/empty_paper.dm new file mode 100644 index 000000000000..8c0145c6f815 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/empty_paper.dm @@ -0,0 +1,11 @@ +/// Accepts pieces of paper that have nothing written on them yet. +/datum/targeting_strategy/empty_paper + +/datum/targeting_strategy/empty_paper/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/item/paper/candidate = target + if(!istype(candidate) || !candidate.is_empty()) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/finished_stove.dm b/code/datums/ai/basic_mobs/targeting_strategies/finished_stove.dm new file mode 100644 index 000000000000..c8d49aa68a67 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/finished_stove.dm @@ -0,0 +1,14 @@ +/// Accepts closed ovens whose tray holds finished (no longer bakeable) goods. +/datum/targeting_strategy/finished_stove + +/datum/targeting_strategy/finished_stove/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/oven/range/candidate = target + if(!istype(candidate) || candidate.open || !length(candidate.used_tray?.contents)) + return FALSE + for(var/atom/baking as anything in candidate.used_tray) + if(HAS_TRAIT(baking, TRAIT_BAKEABLE)) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/food_or_drink.dm b/code/datums/ai/basic_mobs/targeting_strategies/food_or_drink.dm new file mode 100644 index 000000000000..6233824fdc0b --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/food_or_drink.dm @@ -0,0 +1,30 @@ +/// Accepts items that are edible or consumable from a bowl, or drinks when allowed by the controller blackboard. +/datum/targeting_strategy/pickup_item/food_or_drink + +/datum/targeting_strategy/pickup_item/food_or_drink/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/find_drinks = controller?.blackboard[BB_IGNORE_DRINKS] + return _is_food(target) || (find_drinks && _is_drink(target)) + +/datum/targeting_strategy/pickup_item/food_or_drink/proc/_is_food(obj/item/thing) + if(IS_EDIBLE(thing)) + return TRUE + if(istype(thing, /obj/item/reagent_containers/cup/bowl)) + return thing.reagents.total_volume > 0 + return FALSE + +/datum/targeting_strategy/pickup_item/food_or_drink/proc/_is_drink(obj/item/thing) + if(istype(thing, /obj/item/reagent_containers/cup/glass)) + return thing.reagents.total_volume > 0 + return FALSE + +/// Like food_or_drink but always accepts drinks regardless of BB_IGNORE_DRINKS. +/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks + +/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + return _is_food(target) || _is_drink(target) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/goliath_diggable_turf.dm b/code/datums/ai/basic_mobs/targeting_strategies/goliath_diggable_turf.dm new file mode 100644 index 000000000000..cc7d01b4a8e6 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/goliath_diggable_turf.dm @@ -0,0 +1,11 @@ +/// Accepts undig asteroid turfs. Pair with a range_turfs source. +/datum/targeting_strategy/goliath_diggable_turf + +/datum/targeting_strategy/goliath_diggable_turf/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/turf/open/misc/asteroid/candidate = target + if(!istype(candidate) || candidate.dug) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/goose_edible.dm b/code/datums/ai/basic_mobs/targeting_strategies/goose_edible.dm new file mode 100644 index 000000000000..b2c52be2d368 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/goose_edible.dm @@ -0,0 +1,11 @@ +/// Accepts items that are edible or made of plastic. Used by goose eating. +/datum/targeting_strategy/goose_edible + +/datum/targeting_strategy/goose_edible/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/item/thing = target + if(!isitem(thing)) + return FALSE + return IS_EDIBLE(thing) || thing.has_material_type(/datum/material/plastic) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/huntable.dm b/code/datums/ai/basic_mobs/targeting_strategies/huntable.dm new file mode 100644 index 000000000000..e7ae75556d03 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/huntable.dm @@ -0,0 +1,12 @@ +/// Accepts any visible target that isn't a dead mob. Mirrors the legacy hunt finder's validity check. +/datum/targeting_strategy/huntable + +/datum/targeting_strategy/huntable/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(isliving(target)) + var/mob/living/living_target = target + if(living_target.stat == DEAD) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/huntable_mouse.dm b/code/datums/ai/basic_mobs/targeting_strategies/huntable_mouse.dm new file mode 100644 index 000000000000..ee688815b796 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/huntable_mouse.dm @@ -0,0 +1,11 @@ +/// Accepts living, mindless mice that are visible to the pawn. +/datum/targeting_strategy/huntable_mouse + +/datum/targeting_strategy/huntable_mouse/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate) || candidate.stat == DEAD || candidate.mind) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/injured_mob.dm b/code/datums/ai/basic_mobs/targeting_strategies/injured_mob.dm new file mode 100644 index 000000000000..6afb807c2d11 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/injured_mob.dm @@ -0,0 +1,19 @@ +/// Accepts mobs below their maximum health (no line-of-sight requirement). +/datum/targeting_strategy/injured_mob + +/datum/targeting_strategy/injured_mob/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate)) + return FALSE + return candidate.health < candidate.maxHealth + +/// As injured_mob, but never accepts the searching pawn itself. +/datum/targeting_strategy/injured_mob/not_self + +/datum/targeting_strategy/injured_mob/not_self/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + if(target == living_mob) + return FALSE + return ..() diff --git a/code/datums/ai/basic_mobs/targeting_strategies/injured_raptor.dm b/code/datums/ai/basic_mobs/targeting_strategies/injured_raptor.dm new file mode 100644 index 000000000000..36c7a2b8cae9 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/injured_raptor.dm @@ -0,0 +1,6 @@ +/datum/targeting_strategy/injured_mob/not_self/injured_raptor + +/datum/targeting_strategy/injured_mob/not_self/injured_raptor/is_valid_target(mob/living/living_mob, mob/living/basic/raptor/target, vision_range, datum/ai_controller/controller = null) + if(!istype(target)) + return FALSE + return ..() diff --git a/code/datums/ai/basic_mobs/targeting_strategies/legged_conscious_human.dm b/code/datums/ai/basic_mobs/targeting_strategies/legged_conscious_human.dm new file mode 100644 index 000000000000..971427a8730f --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/legged_conscious_human.dm @@ -0,0 +1,13 @@ +/// Accepts conscious humans who have at least one leg and are visible to the pawn. +/datum/targeting_strategy/legged_conscious_human + +/datum/targeting_strategy/legged_conscious_human/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/carbon/human/candidate = target + if(!istype(candidate) || IS_UNCONSCIOUS_OR_CRIT(candidate)) + return FALSE + if(isnull(candidate.get_bodypart(BODY_ZONE_R_LEG)) && isnull(candidate.get_bodypart(BODY_ZONE_L_LEG))) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/living_not_dead.dm b/code/datums/ai/basic_mobs/targeting_strategies/living_not_dead.dm new file mode 100644 index 000000000000..e712d70f3889 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/living_not_dead.dm @@ -0,0 +1,12 @@ +/// Accepts living mobs that are not dead and visible to the pawn. Skips faction checks, +/// so it's suitable for finding allies (e.g. a hivebot looking for another hivebot). +/datum/targeting_strategy/living_not_dead + +/datum/targeting_strategy/living_not_dead/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate) || candidate.stat == DEAD) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/non_stump_tree.dm b/code/datums/ai/basic_mobs/targeting_strategies/non_stump_tree.dm new file mode 100644 index 000000000000..c969e8d24122 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/non_stump_tree.dm @@ -0,0 +1,10 @@ +/// Accepts trees that are not stumps and are visible to the pawn. +/datum/targetingUI_strategy/non_stump_tree + +/datum/targeting_strategy/non_stump_tree/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(istype(target, /obj/structure/flora/tree/stump)) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/pickup_item.dm b/code/datums/ai/basic_mobs/targeting_strategies/pickup_item.dm new file mode 100644 index 000000000000..dc7efb60626d --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/pickup_item.dm @@ -0,0 +1,10 @@ +///Ensures the item is actually on a turf, else we can't go there to pick it up! +/datum/targeting_strategy/pickup_item + +/datum/targeting_strategy/pickup_item/is_valid_target(mob/living/living_mob, obj/item/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!isturf(target.loc) && !living_mob.is_holding(target)) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/playable_deer.dm b/code/datums/ai/basic_mobs/targeting_strategies/playable_deer.dm new file mode 100644 index 000000000000..e140b8ee2fd9 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/playable_deer.dm @@ -0,0 +1,11 @@ +/// Accepts living (non-dead) deer that are visible to the pawn. +/datum/targeting_strategy/playable_deer + +/datum/targeting_strategy/playable_deer/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/candidate = target + if(!isliving(candidate) || candidate.stat == DEAD) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/playable_synthesizer.dm b/code/datums/ai/basic_mobs/targeting_strategies/playable_synthesizer.dm new file mode 100644 index 000000000000..3c83efdd4f60 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/playable_synthesizer.dm @@ -0,0 +1,12 @@ +/// Accepts a visible piano synthesizer sitting on the floor (not a wearable headphones variant). +/datum/targeting_strategy/playable_synthesizer + +/datum/targeting_strategy/playable_synthesizer/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(target.type != /obj/item/instrument/piano_synth) + return FALSE + if(!isturf(target.loc)) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/pollinatable_hydro.dm b/code/datums/ai/basic_mobs/targeting_strategies/pollinatable_hydro.dm new file mode 100644 index 000000000000..44b785485034 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/pollinatable_hydro.dm @@ -0,0 +1,11 @@ +/// Accepts visible hydroponics trays a bee can currently pollinate. +/datum/targeting_strategy/pollinatable_hydro + +/datum/targeting_strategy/pollinatable_hydro/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/hydroponics/candidate = target + if(!istype(candidate) || !candidate.can_bee_pollinate()) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/raptor_trough.dm b/code/datums/ai/basic_mobs/targeting_strategies/raptor_trough.dm new file mode 100644 index 000000000000..5f51cf840086 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/raptor_trough.dm @@ -0,0 +1,10 @@ +/// Looks for filled troughs raptors can eat from +/datum/targeting_strategy/raptor_trough + +/datum/targeting_strategy/raptor_trough/is_valid_target(mob/living/living_mob, obj/structure/ore_container/food_trough/raptor_trough/trough, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!can_see(living_mob, trough, vision_range)) + return FALSE + return !!(locate(/obj/item/stack/ore) in trough.contents) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/slime_food.dm b/code/datums/ai/basic_mobs/targeting_strategies/slime_food.dm new file mode 100644 index 000000000000..8f63df2315c7 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/slime_food.dm @@ -0,0 +1,44 @@ +/// Accepts edible targets for slimes based on hunger level, faction, and species. +/// Requires the controller (reads slime hunger/rabid/current-target blackboard keys). +/datum/targeting_strategy/slime_food + +/datum/targeting_strategy/slime_food/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!controller) + return FALSE + // Anything that attacked us is a valid target even if we can't eat it. + if(target in controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]) + var/datum/targeting_strategy/retaliate_strategy = GET_TARGETING_STRATEGY(/datum/targeting_strategy/basic/not_friends) + return retaliate_strategy.is_valid_target(living_mob, target, vision_range) + + var/mob/living/basic/slime/hunter = living_mob + var/mob/living/candidate = target + if(!isliving(candidate)) + return FALSE + + // We're already latched onto them and feeding; don't invalidate our own meal. + if(hunter.buckled == candidate) + return TRUE + + var/static/list/slime_faction + if(isnull(slime_faction)) + slime_faction = string_list(list(FACTION_SLIME)) + + if(FAST_FACTION_CHECK(slime_faction, candidate.get_faction(), hunter.allies, candidate.allies, FALSE)) + return FALSE + + if(!hunter.can_feed_on(candidate, check_adjacent = FALSE)) + return FALSE + + if(candidate == controller.blackboard[BB_CURRENT_TARGET]) + return can_see(hunter, candidate, vision_range) + + if(controller.blackboard[BB_SLIME_HUNGER_LEVEL] == SLIME_HUNGER_STARVING && controller.blackboard[BB_SLIME_RABID]) + return can_see(hunter, candidate, vision_range) + + if(islarva(candidate) || ismonkey(candidate) || ishuman(candidate) || isalienadult(candidate)) + return can_see(hunter, candidate, vision_range) + + return FALSE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/sniffable_hydro.dm b/code/datums/ai/basic_mobs/targeting_strategies/sniffable_hydro.dm new file mode 100644 index 000000000000..61ab84289f18 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/sniffable_hydro.dm @@ -0,0 +1,15 @@ +/// Accepts hydroponics trays with a growing seed that are not too weedy or pest-ridden, and visible. +/datum/targeting_strategy/sniffable_hydro + +/datum/targeting_strategy/sniffable_hydro/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/hydroponics/candidate = target + if(!istype(candidate)) + return FALSE + if(isnull(candidate.myseed)) + return FALSE + if(candidate.weedlevel > 5 || candidate.pestlevel > 5) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/stealable_item.dm b/code/datums/ai/basic_mobs/targeting_strategies/stealable_item.dm new file mode 100644 index 000000000000..15c0128be6c2 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/stealable_item.dm @@ -0,0 +1,11 @@ +/// Accepts visible items that aren't anchored or already being pulled. +/datum/targeting_strategy/pickup_item/stealable_item + +/datum/targeting_strategy/pickup_item/stealable_item/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/item/candidate = target + if(!isitem(candidate) || candidate.anchored || candidate.pulledby) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/stocked_beehive.dm b/code/datums/ai/basic_mobs/targeting_strategies/stocked_beehive.dm new file mode 100644 index 000000000000..7b21dc743943 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/stocked_beehive.dm @@ -0,0 +1,11 @@ +/// Accepts beehives that contain at least one honeycomb and are visible to the pawn. +/datum/targeting_strategy/stocked_beehive + +/datum/targeting_strategy/stocked_beehive/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/structure/beebox/candidate = target + if(!istype(candidate) || !length(candidate.honeycombs)) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/treatable_hydro.dm b/code/datums/ai/basic_mobs/targeting_strategies/treatable_hydro.dm new file mode 100644 index 000000000000..79f922bfb3c3 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/treatable_hydro.dm @@ -0,0 +1,19 @@ +/// Accepts hydroponics trays that need watering (if pawn holds a watering can) or weeding/dead plant removal. +/// Reads thresholds from the controller blackboard. +/datum/targeting_strategy/treatable_hydro + +/datum/targeting_strategy/treatable_hydro/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/hydroponics/hydro = target + if(!istype(hydro) || isnull(hydro.myseed)) + return FALSE + if(!controller) + return FALSE + var/waterlevel_threshold = controller.blackboard[BB_WATERLEVEL_THRESHOLD] + var/weedlevel_threshold = controller.blackboard[BB_WEEDLEVEL_THRESHOLD] + if(hydro.waterlevel < waterlevel_threshold) + if(locate(/obj/item/reagent_containers/cup/watering_can) in living_mob) + return TRUE + return hydro.weedlevel > weedlevel_threshold || hydro.plant_status == HYDROTRAY_PLANT_DEAD diff --git a/code/datums/ai/basic_mobs/targeting_strategies/trough_with_ore.dm b/code/datums/ai/basic_mobs/targeting_strategies/trough_with_ore.dm new file mode 100644 index 000000000000..10a2781bd980 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/trough_with_ore.dm @@ -0,0 +1,10 @@ +/// Accepts containers (e.g. troughs) that hold at least one ore item. +/datum/targeting_strategy/trough_with_ore + +/datum/targeting_strategy/trough_with_ore/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!ismovable(target)) + return FALSE + return !!(locate(/obj/item/stack/ore) in target.contents) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/unbroken_light.dm b/code/datums/ai/basic_mobs/targeting_strategies/unbroken_light.dm new file mode 100644 index 000000000000..9289e0de4faf --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/unbroken_light.dm @@ -0,0 +1,11 @@ +/// Accepts light fixtures that are not already broken and are visible to the pawn. +/datum/targeting_strategy/unbroken_light + +/datum/targeting_strategy/unbroken_light/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/light/candidate = target + if(!istype(candidate) || candidate.status == LIGHT_BROKEN) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/uncarried_egg.dm b/code/datums/ai/basic_mobs/targeting_strategies/uncarried_egg.dm new file mode 100644 index 000000000000..4cf778ac7103 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/uncarried_egg.dm @@ -0,0 +1,10 @@ +/// Accepts visible penguin eggs the pawn is not already carrying. +/datum/targeting_strategy/uncarried_egg + +/datum/targeting_strategy/uncarried_egg/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(target in living_mob.contents) + return FALSE + return can_see(living_mob, target, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/unlit_bonfire.dm b/code/datums/ai/basic_mobs/targeting_strategies/unlit_bonfire.dm new file mode 100644 index 000000000000..f9cf2e6dbb18 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/unlit_bonfire.dm @@ -0,0 +1,11 @@ +/// Accepts bonfires that are not currently burning and are visible to the pawn. +/datum/targeting_strategy/unlit_bonfire + +/datum/targeting_strategy/unlit_bonfire/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/structure/bonfire/candidate = target + if(!istype(candidate) || candidate.burning) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/valid_cat_home.dm b/code/datums/ai/basic_mobs/targeting_strategies/valid_cat_home.dm new file mode 100644 index 000000000000..7ce512001522 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/valid_cat_home.dm @@ -0,0 +1,11 @@ +/// Accepts cat houses that don't already have a resident cat. +/datum/targeting_strategy/valid_cat_home + +/datum/targeting_strategy/valid_cat_home/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/structure/cat_house/home = target + if(!istype(home) || home.resident_cat) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/valid_kitten.dm b/code/datums/ai/basic_mobs/targeting_strategies/valid_kitten.dm new file mode 100644 index 000000000000..55056a0b5b46 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/valid_kitten.dm @@ -0,0 +1,15 @@ +/// Accepts living kittens that don't already have huntable food sitting next to them. +/// Reads the prey typecache from BB_HUNTABLE_PREY on the searching controller. +/datum/targeting_strategy/valid_kitten + +/datum/targeting_strategy/valid_kitten/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/kitten = target + if(!isliving(kitten) || kitten.stat == DEAD) + return FALSE + var/list/prey = controller?.blackboard[BB_HUNTABLE_PREY] + if(prey && length(typecache_filter_list(oview(2, kitten), prey))) + return FALSE + return TRUE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/walkable_turf.dm b/code/datums/ai/basic_mobs/targeting_strategies/walkable_turf.dm new file mode 100644 index 000000000000..bfb6618236b1 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/walkable_turf.dm @@ -0,0 +1,9 @@ +/// Base strategy for checking if a turf is walkable +/datum/targeting_strategy/walkable_turf + +/datum/targeting_strategy/walkable_turf/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/turf/candidate = target + return !candidate.is_blocked_turf() diff --git a/code/datums/ai/basic_mobs/targeting_strategies/water_dispenser.dm b/code/datums/ai/basic_mobs/targeting_strategies/water_dispenser.dm new file mode 100644 index 000000000000..3de583899d4e --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/water_dispenser.dm @@ -0,0 +1,13 @@ +/// Accepts sinks or reagent dispensers that contain water and are visible to the pawn. +/datum/targeting_strategy/water_dispenser + +/datum/targeting_strategy/water_dispenser/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/atom/movable/dispenser = target + if(!istype(dispenser, /obj/structure/sink) && !istype(dispenser, /obj/structure/reagent_dispensers)) + return FALSE + if(!dispenser.reagents || !(locate(/datum/reagent/water) in dispenser.reagents.reagent_list)) + return FALSE + return can_see(living_mob, dispenser, vision_range) diff --git a/code/datums/ai/basic_mobs/targeting_strategies/with_object.dm b/code/datums/ai/basic_mobs/targeting_strategies/with_object.dm index 7cc76d3010c9..e255eef33647 100644 --- a/code/datums/ai/basic_mobs/targeting_strategies/with_object.dm +++ b/code/datums/ai/basic_mobs/targeting_strategies/with_object.dm @@ -8,11 +8,13 @@ /datum/targeting_strategy/basic/holding_object /// BB key that holds the target typepath to use var/target_item_key = BB_TARGET_HELD_ITEM + ///We dont care that they're dead, if they got tongs theyre bad + ignore_target_status = TRUE ///Returns true or false depending on if the target can be attacked by the mob -/datum/targeting_strategy/basic/holding_object/can_attack(mob/living/living_mob, atom/target, vision_range) - var/datum/ai_controller/controller = living_mob.ai_controller - var/object_type_path = controller.blackboard[target_item_key] +/datum/targeting_strategy/basic/holding_object/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + var/datum/ai_controller/our_controller = living_mob.ai_controller + var/object_type_path = our_controller.blackboard[target_item_key] if (object_type_path == null) return FALSE // no op @@ -22,8 +24,8 @@ // Look at me, type casting like a grown up var/mob/targetmob = target // Check if our parent behaviour agrees we can attack this target (we ignore faction by default) - var/can_attack = ..() - if(can_attack && targetmob.is_holding_item_of_type(object_type_path)) + var/is_valid_target = ..() + if(is_valid_target && targetmob.is_holding_item_of_type(object_type_path)) return TRUE // they have the item // No valid target return FALSE diff --git a/code/datums/ai/basic_mobs/targeting_strategies/working_machine.dm b/code/datums/ai/basic_mobs/targeting_strategies/working_machine.dm new file mode 100644 index 000000000000..9f982d1bdf68 --- /dev/null +++ b/code/datums/ai/basic_mobs/targeting_strategies/working_machine.dm @@ -0,0 +1,11 @@ +/// Accepts machinery that is not broken and is visible to the pawn. +/datum/targeting_strategy/working_machine + +/datum/targeting_strategy/working_machine/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/machinery/candidate = target + if(!istype(candidate) || (candidate.machine_stat & BROKEN)) + return FALSE + return can_see(living_mob, candidate, vision_range) diff --git a/code/datums/ai/bots/bot_decorators.dm b/code/datums/ai/bots/bot_decorators.dm new file mode 100644 index 000000000000..e36668a3a029 --- /dev/null +++ b/code/datums/ai/bots/bot_decorators.dm @@ -0,0 +1,45 @@ +/// Gates child on pawn being emagged. Use invert = TRUE for the opposite. Checked each tick. +/datum/bt_node/decorator/bot_is_emagged + +/datum/bt_node/decorator/bot_is_emagged/check_condition(datum/ai_controller/controller) + var/mob/living/basic/bot/bot_pawn = controller.pawn + return !!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + +/// Gates child on pawn having the specified bot_mode_flag. Observes COMSIG_BOT_MODE_FLAGS_SET. +/datum/bt_node/decorator/bot_mode_flag + var/flag + +/datum/bt_node/decorator/bot_mode_flag/register_observe_signals(atom/pawn) + RegisterSignal(pawn, COMSIG_BOT_MODE_FLAGS_SET, PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/bot_mode_flag/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, COMSIG_BOT_MODE_FLAGS_SET) + +/datum/bt_node/decorator/bot_mode_flag/check_condition(datum/ai_controller/controller) + var/mob/living/basic/bot/bot_pawn = controller.pawn + return !!(bot_pawn.bot_mode_flags & flag) + +/// Gates child on the pawn's current mode matching `mode` (e.g. BOT_DELIVER). Use invert = TRUE for the opposite. Checked each tick. +/datum/bt_node/decorator/bot_mode + var/mode + +/datum/bt_node/decorator/bot_mode/check_condition(datum/ai_controller/controller) + var/mob/living/basic/bot/bot_pawn = controller.pawn + return bot_pawn.mode == mode + +/// Gates child on the pawn's `wire` being cut. Use invert = TRUE to gate on the wire being intact. Checked each tick. +/datum/bt_node/decorator/bot_wire_cut + var/wire + +/datum/bt_node/decorator/bot_wire_cut/check_condition(datum/ai_controller/controller) + var/mob/living/basic/bot/bot_pawn = controller.pawn + return !!(bot_pawn.wires?.is_cut(wire)) + +/// Gates child when pawn has the specified medical mode flag. Use invert = TRUE for the opposite. Checked each tick. +/datum/bt_node/decorator/bot_medical_flag + var/flag + +/datum/bt_node/decorator/bot_medical_flag/check_condition(datum/ai_controller/controller) + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + return !!(bot_pawn.medical_mode_flags & flag) diff --git a/code/datums/ai/bots/bot_patrol.bt.json b/code/datums/ai/bots/bot_patrol.bt.json new file mode 100644 index 000000000000..faa8708e2e41 --- /dev/null +++ b/code/datums/ai/bots/bot_patrol.bt.json @@ -0,0 +1,88 @@ +{ + "dm_type": "/datum/bt_node/subtree/bot_patrol", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BOT_BEACON_COOLDOWN" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_mode_flag", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "flag": "BOT_MODE_AUTOPATROL" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_PREVIOUS_BEACON_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_next_beacon_target", + "vars": { + "target_key": "BB_BEACON_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_first_beacon_target", + "vars": { + "target_key": "BB_BEACON_TARGET" + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BEACON_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BEACON_TARGET", + "required_dist": 0, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/arrive_at_beacon", + "vars": { + "target_key": "BB_BEACON_TARGET" + } + } + ] + } + } + ] + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": "5 SECONDS" + } + } + ] +} diff --git a/code/datums/ai/bots/bot_respond_to_summon.bt.json b/code/datums/ai/bots/bot_respond_to_summon.bt.json new file mode 100644 index 000000000000..7d0661fa7e6a --- /dev/null +++ b/code/datums/ai/bots/bot_respond_to_summon.bt.json @@ -0,0 +1,29 @@ +{ + "dm_type": "/datum/bt_node/subtree/bot_respond_to_summon", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BOT_SUMMON_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BOT_SUMMON_TARGET", + "required_dist": 0, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/complete_summon_travel", + "vars": { + "target_key": "BB_BOT_SUMMON_TARGET" + } + } + ] + } +} diff --git a/code/datums/ai/bots/bot_salute_authority.bt.json b/code/datums/ai/bots/bot_salute_authority.bt.json new file mode 100644 index 000000000000..b7a6609f6378 --- /dev/null +++ b/code/datums/ai/bots/bot_salute_authority.bt.json @@ -0,0 +1,29 @@ +{ + "dm_type": "/datum/bt_node/subtree/bot_salute_authority", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_SALUTE_COOLDOWN", + "cooldown_duration": "60 SECONDS" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_valid_authority", + "vars": { + "target_key": "BB_SALUTE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/salute_authority", + "vars": { + "target_key": "BB_SALUTE_TARGET", + "salute_keys": "BB_SALUTE_MESSAGES" + } + } + ] + } +} diff --git a/code/datums/ai/bots/bot_subtrees.dm b/code/datums/ai/bots/bot_subtrees.dm new file mode 100644 index 000000000000..efabf9984da7 --- /dev/null +++ b/code/datums/ai/bots/bot_subtrees.dm @@ -0,0 +1,281 @@ +#define BOT_NO_BEACON_PATH_PENALTY 30 SECONDS + +/** + * Searches for a valid target in oview and sets a blackboard key when found. + * Subtypes override valid_target() to refine selection criteria. + * looking_for is an optional typecache pre-filter; pass null to check all atoms via valid_target(). + */ +/datum/bt_node/ai_behavior/bot_search + var/target_key + var/looking_for = null + var/radius = 5 + var/pathing_distance = 10 + var/bypass_add_blacklist = FALSE + var/turf_search = FALSE + /// How close the path must get to the target (0 = onto/adjacent). Repairbot raises this so it stops next to the walls/girders it repairs. + var/minimum_distance = 0 + time_between_perform = 2 SECONDS + /// Stashed candidate list between perform() and the async worker (not a blackboard value). + VAR_PRIVATE/list/candidate_stash + +/datum/bt_node/ai_behavior/bot_search/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + if(!istype(controller)) + stack_trace("attempted to give [controller.pawn] the bot search behavior!") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/async_flags = handle_async() + if(async_flags) + return async_flags + + if(isnull(looking_for)) + looking_for = get_looking_for_typecache() + + // Build candidate list synchronously (no sleeping), then hand off to async. + var/mob/living/living_pawn = controller.pawn + var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + var/list/candidates = list() + for(var/atom/potential_target as anything in (turf_search ? RANGE_TURFS(radius, controller.pawn) : oview(radius, controller.pawn))) + if(!isnull(looking_for) && !is_type_in_typecache(potential_target, looking_for)) + continue + if(LAZYACCESS(ignore_list, potential_target)) + continue + if(!valid_target(controller, potential_target)) + continue + if(!can_see(controller.pawn, potential_target, radius)) + continue + candidates += potential_target + + if(!length(candidates)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[living_pawn] bot_search ([type]): no valid target found in radius [radius]") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + candidate_stash = candidates + return start_async() + +/datum/bt_node/ai_behavior/bot_search/perform_async(datum/ai_controller/basic_controller/bot/controller) + var/mob/living/living_pawn = controller.pawn + var/found = FALSE + for(var/atom/potential_target as anything in candidate_stash) + if(!async_still_valid()) + break + if(controller.set_if_can_reach(key = target_key, target = potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist, minimum_distance = minimum_distance)) + found = TRUE + break + if(!async_still_valid()) + return + if(!found) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[living_pawn] bot_search ([type]): no reachable target found") + finish_async(found ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/datum/bt_node/ai_behavior/bot_search/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + candidate_stash = null + +/datum/bt_node/ai_behavior/bot_search/proc/get_looking_for_typecache() + return + +/datum/bt_node/ai_behavior/bot_search/proc/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + return TRUE + + +///Performs bot speech from a list of options +/datum/bt_node/ai_behavior/bot_speech + var/list/list_to_pick_from + var/announce_key + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/bot_speech/perform(seconds_per_tick, datum/ai_controller/controller) + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[announce_key] + if(isnull(announcement) || !length(list_to_pick_from)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + announcement.announce(pick(list_to_pick_from)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + +///Interact with an object. Could probably be moved to a generic behavior as the only unique thing is the blacklist. +/datum/bt_node/ai_behavior/bot_interact + var/target_key + var/clear_target = TRUE + +/datum/bt_node/ai_behavior/bot_interact/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(get_dist(living_pawn, target) > 1) + return AI_BEHAVIOR_INSTANT + living_pawn.UnarmedAttack(target, proximity_flag = TRUE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/bot_interact/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(clear_target) + controller.clear_blackboard_key(target_key) + if(!succeeded && !isnull(target)) + controller.add_to_blacklist(target) + +/// Variant that keeps the target key after interacting (caller must clear it). +/datum/bt_node/ai_behavior/bot_interact/keep_target + clear_target = FALSE + + +/// Searches GLOB.deliverybeacons for a beacon whose location matches the tag in tag_key, and sets it as target_key. +/datum/bt_node/ai_behavior/find_delivery_beacon + var/target_key + /// Blackboard key holding the location tag string to match against beacon.location. + var/tag_key + time_between_perform = 2 SECONDS + +/datum/bt_node/ai_behavior/find_delivery_beacon/perform(seconds_per_tick, datum/ai_controller/controller) + var/beacon_tag = controller.blackboard[tag_key] + if(isnull(beacon_tag)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + for(var/obj/machinery/navbeacon/beacon as anything in GLOB.deliverybeacons) + if(beacon.location != beacon_tag) + continue + controller.set_blackboard_key(target_key, beacon) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +///Find the closest beacon and set it as the target +/datum/bt_node/ai_behavior/find_first_beacon_target + var/target_key + +/datum/bt_node/ai_behavior/find_first_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/closest_distance = INFINITY + var/mob/living/basic/bot/bot_pawn = controller.pawn + var/atom/final_target + var/atom/previous_target = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] + for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) + var/dist = get_dist(bot_pawn, beacon) + if(beacon == previous_target || dist <= 1) + continue + if(dist > closest_distance) + continue + closest_distance = dist + final_target = beacon + + if(isnull(final_target)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] find_first_beacon_target: no beacon found on z=[bot_pawn.z] (previous=[previous_target])") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] first beacon target: [final_target]", get_turf(final_target), "Beacon") + EVLOG_LINES(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "Beacon path", get_turf(bot_pawn), get_turf(final_target)) + controller.set_blackboard_key(target_key, final_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +///Find the next beacon from a previous target and set it as the new target +/datum/bt_node/ai_behavior/find_next_beacon_target + var/target_key + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + + var/mob/living/basic/bot/bot_pawn = controller.pawn + var/obj/machinery/navbeacon/prev_beacon = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] + if(QDELETED(prev_beacon)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] find_next_beacon_target: previous beacon is deleted") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/atom/final_target + for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) + if(beacon.location == prev_beacon.codes[NAVBEACON_PATROL_NEXT]) + final_target = beacon + break + + if(isnull(final_target)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] find_next_beacon_target: no beacon with location=[prev_beacon.codes[NAVBEACON_PATROL_NEXT]] (prev=[prev_beacon])") + controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, final_target) + controller.set_blackboard_key(target_key, final_target) + return AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/find_next_beacon_target/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + + +/// Records the beacon as visited and clears the target key once the bot is on the same turf. +/datum/bt_node/ai_behavior/arrive_at_beacon + var/target_key + +/datum/bt_node/ai_behavior/arrive_at_beacon/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + var/obj/machinery/navbeacon/beacon = controller.blackboard[target_key] + if(QDELETED(beacon)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(get_dist(controller.pawn, beacon) > 0) + return AI_BEHAVIOR_INSTANT + controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, beacon) + controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +/// Completes summon travel once the bot reaches the summon target's turf. +/datum/bt_node/ai_behavior/complete_summon_travel + var/target_key + +/datum/bt_node/ai_behavior/complete_summon_travel/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/bot/bot_pawn = controller.pawn + if(QDELETED(bot_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/atom/target = controller.blackboard[target_key] + if(get_dist(bot_pawn, target) > 0) + return AI_BEHAVIOR_INSTANT + bot_pawn.calling_ai_ref = null + bot_pawn.update_bot_mode(new_mode = BOT_IDLE) + controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +///Find a valid authority to salute and set them as the target +/datum/bt_node/ai_behavior/find_valid_authority + var/target_key + +/datum/bt_node/ai_behavior/find_valid_authority/perform(seconds_per_tick, datum/ai_controller/controller) + for(var/mob/living/nearby_mob in oview(7, controller.pawn)) + if(!HAS_TRAIT(nearby_mob, TRAIT_COMMISSIONED)) + continue + controller.set_blackboard_key(target_key, nearby_mob) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +///Salute the authority /(o.o) +/datum/bt_node/ai_behavior/salute_authority + var/target_key + var/salute_keys + +/datum/bt_node/ai_behavior/salute_authority/perform(seconds_per_tick, datum/ai_controller/controller) + if(!controller.blackboard_key_exists(target_key)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/list/salute_list = controller.blackboard[salute_keys] + if(!length(salute_list)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/basic/bot/bot_pawn = controller.pawn + var/obj/item/our_hat = (locate(/obj/item/clothing/head) in bot_pawn) + if(our_hat) + salute_list += "tips [our_hat] at " + bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]!") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + + +/// Travel to BB_BOT_SUMMON_TARGET if set, completing when on the same turf. +/datum/bt_node/subtree/bot_respond_to_summon + behavior_tree_json = "code/datums/ai/bots/bot_respond_to_summon.bt.json" + +/// Salute any commissioned officer in range +/datum/bt_node/subtree/bot_salute_authority + behavior_tree_json = "code/datums/ai/bots/bot_salute_authority.bt.json" + +/** + * Patrol to navbeacons in sequence when autopatrol is enabled and not on cooldown. + * Priority: travel to current target -> find next in chain -> find first (nearest) beacon. + */ +/datum/bt_node/subtree/bot_patrol + behavior_tree_json = "code/datums/ai/bots/bot_patrol.bt.json" + + +#undef BOT_NO_BEACON_PATH_PENALTY diff --git a/code/datums/ai/bt_viewer.dm b/code/datums/ai/bt_viewer.dm new file mode 100644 index 000000000000..04106fca6d15 --- /dev/null +++ b/code/datums/ai/bt_viewer.dm @@ -0,0 +1,161 @@ +GLOBAL_DATUM_INIT(bt_viewer, /datum/bt_viewer, new()) + +/datum/bt_viewer + /// The controller currently being viewed. + var/datum/ai_controller/viewing_controller = null + /// The mob owning the controller. + var/mob/viewing_mob = null + /// TRUE while waiting for admin to click a mob. + var/awaiting_pick = FALSE + /// The admin waiting to pick, used to unregister the click signal. + var/mob/awaiting_pick_user = null + +/datum/bt_viewer/Destroy() + _clear_target() + _end_pick() + return ..() + +/datum/bt_viewer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "BehaviorTreeViewer", "Behavior Tree Viewer") + ui.open() + +/datum/bt_viewer/ui_close(mob/user) + . = ..() + if(awaiting_pick_user == user) + _end_pick() + +/datum/bt_viewer/ui_state(mob/user) + return ADMIN_STATE(R_DEBUG) + +/datum/bt_viewer/ui_assets(mob/user) + return list(get_asset_datum(/datum/asset/simple/plane_background)) + +/datum/bt_viewer/ui_data(mob/user) + var/list/data = list() + data["mob_name"] = viewing_mob ? viewing_mob.name : null + data["controller_type"] = viewing_controller ? "[viewing_controller.type]" : null + data["active_execution_index"] = viewing_controller ? viewing_controller.active_execution_index : 0 + if(viewing_controller?.bt_execution_log != null) + data["fired_indices"] = viewing_controller.bt_execution_log.Copy() + viewing_controller.bt_execution_log.Cut() //Clear the list every time we update + else + data["fired_indices"] = list() + data["awaiting_pick"] = awaiting_pick + if(viewing_controller) + var/list/root_indices = list() + var/list/node_list = list() + collect_nodes(root_indices, node_list) + data["roots"] = root_indices + data["nodes"] = node_list + var/list/bb_entries = list() + var/list/bb = viewing_controller.blackboard + for(var/key in bb) + var/value = bb[key] + var/str_val + if(isnull(value)) + str_val = "null" + else if(islist(value)) + str_val = "list([length(value)])" + else + str_val = "[value]" + bb_entries += list(list("key" = key, "value" = str_val)) + data["blackboard"] = bb_entries + else + data["roots"] = list() + data["nodes"] = list() + data["blackboard"] = list() + return data + +/datum/bt_viewer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/user = ui.user + switch(action) + if("pick_target") + _end_pick() + awaiting_pick = TRUE + awaiting_pick_user = user + RegisterSignal(user, COMSIG_MOB_CLICKON, PROC_REF(on_pick_click)) + return TRUE + if("clear") + _clear_target() + return TRUE + +/datum/bt_viewer/proc/on_pick_click(mob/source, atom/clicked, list/modifiers) + SIGNAL_HANDLER + _end_pick() + + var/atom/target = clicked + if(!target.ai_controller) + return NONE + set_target(target) + return NONE + +/datum/bt_viewer/proc/set_target(mob/target) + _clear_target() + viewing_mob = target + viewing_controller = target.ai_controller + viewing_controller.bt_execution_log = list() + RegisterSignal(viewing_mob, COMSIG_PREQDELETED, PROC_REF(on_mob_deleted)) + +/datum/bt_viewer/proc/on_mob_deleted(datum/source) + SIGNAL_HANDLER + _clear_target() + +/datum/bt_viewer/proc/_clear_target() + if(viewing_mob) + UnregisterSignal(viewing_mob, COMSIG_PREQDELETED) + if(viewing_controller) + viewing_controller.bt_execution_log = null + viewing_mob = null + viewing_controller = null + +/datum/bt_viewer/proc/_end_pick() + awaiting_pick = FALSE + if(awaiting_pick_user) + UnregisterSignal(awaiting_pick_user, COMSIG_MOB_CLICKON) + awaiting_pick_user = null + +/datum/bt_viewer/proc/collect_nodes(list/root_indices, list/node_list) + var/priority = 1 + for(var/datum/bt_node/root as anything in viewing_controller.behavior_nodes) + root_indices += root.execution_index + _collect_node(root, node_list, priority++) + +// Recursively adds node and all descendants to node_list as flat entries. +// Each entry has exec_index as its unique key, with children as a flat list of child exec_indices. +/datum/bt_viewer/proc/_collect_node(datum/bt_node/node, list/node_list, priority_index) + var/exec = node.execution_index || 0 + var/last = node.last_execution_index || 0 + + var/list/node_data = list( + "exec_index" = exec, + "label" = node.label, + "node_type" = node.node_type, + "priority" = priority_index, + ) + if(last != exec) + node_data["last_exec_index"] = last + + if(node.node_type == BT_NODE_DECORATOR) + var/datum/bt_node/decorator/dec = node + if(dec.observer_abort) + node_data["observer_abort"] = dec.observer_abort + if(dec.invert) + node_data["invert"] = TRUE + + var/list/children = node.get_children() + if(length(children)) + var/list/child_indices = list() + for(var/i in 1 to length(children)) + var/datum/bt_node/child = children[i] + if(!child) + continue + child_indices += child.execution_index + _collect_node(child, node_list, i) + node_data["children"] = child_indices + + node_list += list(node_data) diff --git a/code/datums/ai/cursed/cursed.bt.json b/code/datums/ai/cursed/cursed.bt.json new file mode 100644 index 000000000000..9ec75cdfe204 --- /dev/null +++ b/code/datums/ai/cursed/cursed.bt.json @@ -0,0 +1,72 @@ +{ + "dm_type": "/datum/ai_controller/cursed", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_HAUNT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": 20, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/throw_attack/haunted", + "vars": { + "target_key": "BB_HAUNT_TARGET", + "throw_count_key": "BB_HAUNTED_THROW_ATTEMPT_COUNT", + "haunt_list_key": "BB_TO_HAUNT_LIST" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HAUNT_TARGET", + "required_dist": 3, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_ghost_item" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURSE_TARGET", + "target_source": "/datum/target_source/oview_single_type/carbon_mob", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": "CURSED_VIEW_RANGE" + } + } + ] +} diff --git a/code/datums/ai/cursed/cursed_behaviors.dm b/code/datums/ai/cursed/cursed_behaviors.dm index 63e7310f968e..76c991e08d7c 100644 --- a/code/datums/ai/cursed/cursed_behaviors.dm +++ b/code/datums/ai/cursed/cursed_behaviors.dm @@ -1,7 +1,8 @@ -/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed +/// Cursed variant: keeps target if still in range when throws are exhausted. +/datum/bt_node/ai_behavior/throw_attack/cursed -/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key) - var/atom/throw_target = controller.blackboard[target_key] - //dropping our target from the blackboard if they are no longer a valid target after the attack behavior +/datum/bt_node/ai_behavior/throw_attack/cursed/on_throws_exhausted(datum/ai_controller/controller, atom/throw_target, target_key, throw_count_key) + controller.set_blackboard_key(throw_count_key, 0) if(get_dist(throw_target, controller.pawn) > CURSED_VIEW_RANGE) controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED diff --git a/code/datums/ai/cursed/cursed_controller.dm b/code/datums/ai/cursed/cursed_controller.dm index aa32496f3572..ba38888c3ea3 100644 --- a/code/datums/ai/cursed/cursed_controller.dm +++ b/code/datums/ai/cursed/cursed_controller.dm @@ -6,14 +6,13 @@ * Added by /datum/element/cursed, and as such will try to remove this element and go dormant when it finds a victim to curse */ /datum/ai_controller/cursed + behavior_tree_json = "code/datums/ai/cursed/cursed.bt.json" movement_delay = 0.4 SECONDS blackboard = list( BB_CURSE_TARGET, BB_TARGET_SLOT, BB_CURSED_THROW_ATTEMPT_COUNT ) - planning_subtrees = list(/datum/ai_planning_subtree/cursed) - idle_behavior = /datum/idle_behavior/idle_ghost_item /datum/ai_controller/cursed/TryPossessPawn(atom/new_pawn) if(!isitem(new_pawn)) diff --git a/code/datums/ai/cursed/cursed_subtrees.dm b/code/datums/ai/cursed/cursed_subtrees.dm deleted file mode 100644 index dd33312900ab..000000000000 --- a/code/datums/ai/cursed/cursed_subtrees.dm +++ /dev/null @@ -1,13 +0,0 @@ -/datum/ai_planning_subtree/cursed/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/obj/item/item_pawn = controller.pawn - - //make sure we have a target - var/mob/living/carbon/curse_target = controller.blackboard[BB_CURSE_TARGET] - if(!curse_target) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_CURSE_TARGET, /mob/living/carbon, CURSED_VIEW_RANGE) - return - //make sure attack is valid - if(get_dist(curse_target, item_pawn) > CURSED_VIEW_RANGE) - controller.clear_blackboard_key(BB_CURSE_TARGET) - return - controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed, BB_CURSE_TARGET) diff --git a/code/datums/ai/dog/dog.bt.json b/code/datums/ai/dog/dog.bt.json new file mode 100644 index 000000000000..102947488c03 --- /dev/null +++ b/code/datums/ai/dog/dog.bt.json @@ -0,0 +1,44 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/dog", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/dog_harassment" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_dog" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/datums/ai/dog/dog_behaviors.dm b/code/datums/ai/dog/dog_behaviors.dm deleted file mode 100644 index dc031a1d3f11..000000000000 --- a/code/datums/ai/dog/dog_behaviors.dm +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Pursue the target, growl if we're close, and bite if we're adjacent - * Dogs are actually not very aggressive and won't attack unless you approach them - * Adds a floor to the melee damage of the dog, as most pet dogs don't actually have any melee strength - */ -/datum/ai_behavior/basic_melee_attack/dog - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - required_distance = 3 - -/datum/ai_behavior/basic_melee_attack/dog/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - controller.behavior_cooldowns[src] = world.time + get_cooldown(controller) - var/mob/living/living_pawn = controller.pawn - if(!(isturf(living_pawn.loc) || HAS_TRAIT(living_pawn, TRAIT_AI_BAGATTACK))) // Void puppies can attack from inside bags - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - // Unfortunately going to repeat this check in parent call but what can you do - var/atom/target = controller.blackboard[target_key] - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if (!targeting_strategy.can_attack(living_pawn, target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - if (!living_pawn.Adjacent(target)) - growl_at(living_pawn, target, seconds_per_tick) - return AI_BEHAVIOR_INSTANT - - if(!controller.blackboard[BB_DOG_HARASS_HARM]) - paw_harmlessly(living_pawn, target, seconds_per_tick) - return AI_BEHAVIOR_INSTANT - - . = ..() // Bite time - - return AI_BEHAVIOR_DELAY - -/// Swat at someone we don't like but won't hurt -/datum/ai_behavior/basic_melee_attack/dog/proc/paw_harmlessly(mob/living/living_pawn, atom/target, seconds_per_tick) - if(!SPT_PROB(20, seconds_per_tick)) - return - living_pawn.do_attack_animation(target, ATTACK_EFFECT_DISARM) - playsound(target, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1) - target.visible_message(span_danger("[living_pawn] paws ineffectually at [target]!"), span_danger("[living_pawn] paws ineffectually at you!")) - -/// Let them know we mean business -/datum/ai_behavior/basic_melee_attack/dog/proc/growl_at(mob/living/living_pawn, atom/target, seconds_per_tick) - if(!SPT_PROB(15, seconds_per_tick)) - return - living_pawn.manual_emote("[pick("barks", "growls", "stares")] menacingly at [target]!") - if(!SPT_PROB(40, seconds_per_tick)) - return - playsound(living_pawn, SFX_GROWL, 50, TRUE, -1) diff --git a/code/datums/ai/dog/dog_bt.dm b/code/datums/ai/dog/dog_bt.dm new file mode 100644 index 000000000000..978f8a961076 --- /dev/null +++ b/code/datums/ai/dog/dog_bt.dm @@ -0,0 +1,113 @@ + +/** + * BT version of basic_melee_attack/dog. + * When adjacent and BB_DOG_HARASS_HARM = FALSE: paws harmlessly (animation, no damage). + * When adjacent and BB_DOG_HARASS_HARM = TRUE: bites normally. + * Returns FAILURE if targeting strategy rejects the target. + */ +/datum/bt_node/ai_behavior/basic_melee_attack/dog + +/datum/bt_node/ai_behavior/basic_melee_attack/dog/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!(isturf(living_pawn.loc) || HAS_TRAIT(living_pawn, TRAIT_AI_BAGATTACK))) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/atom/target = controller.blackboard[target_key] + + if(!ispath(targeting_strategy)) + targeting_strategy = controller.blackboard[targeting_strategy] + + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(targeting_strategy) + if(QDELETED(target) || !strategy?.is_valid_target(living_pawn, target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if(!controller.blackboard[BB_DOG_HARASS_HARM]) + paw_harmlessly(living_pawn, target, seconds_per_tick) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return ..() + +/// Swat at someone we don't like but won't hurt +/datum/bt_node/ai_behavior/basic_melee_attack/dog/proc/paw_harmlessly(mob/living/living_pawn, atom/target, seconds_per_tick) + if(!SPT_PROB(20, seconds_per_tick)) + return + living_pawn.do_attack_animation(target, ATTACK_EFFECT_DISARM) + playsound(target, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1) + target.visible_message(span_danger("[living_pawn] paws ineffectually at [target]!"), span_danger("[living_pawn] paws ineffectually at you!")) + + +/** + * Searches for a target with TRAIT_HATED_BY_DOGS within 2 tiles. Sets BB_DOG_HARASS_TARGET and + * BB_DOG_HARASS_HARM = FALSE if found. Returns FAILURE if no valid target is found. + * Combine with a cooldown decorator or embed the SPT_PROB gate in the parent tree. + */ +/datum/bt_node/ai_behavior/find_hated_dog_target + var/target_key + var/targeting_strategy = BB_TARGETING_STRATEGY + +/datum/bt_node/ai_behavior/find_hated_dog_target/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(10, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/dog = controller.pawn + + if(!ispath(targeting_strategy)) + targeting_strategy = controller.blackboard[targeting_strategy] + + + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(targeting_strategy) + for(var/mob/living/iter_living in oview(2, dog)) + if(IS_UNCONSCIOUS_OR_CRIT(iter_living) || !HAS_TRAIT(iter_living, TRAIT_HATED_BY_DOGS)) + continue + if(!isnull(dog.buckled)) + dog.audible_message(span_notice("[dog] growls at [iter_living], yet [dog.p_they()] [dog.p_are()] much too comfy to move."), hearing_distance = COMBAT_MESSAGE_RANGE) + continue + if(!strategy?.is_valid_target(dog, iter_living)) + continue + dog.audible_message(span_warning("[dog] growls at [iter_living], seemingly annoyed by [iter_living.p_their()] presence."), hearing_distance = COMBAT_MESSAGE_RANGE) + controller.set_blackboard_key(target_key, iter_living) + controller.set_blackboard_key(BB_DOG_HARASS_HARM, FALSE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + +/** + * Dog-specific idle: random walks at a dog-appropriate rate, and occasionally spins/tail-chases. + * Reads BB_DOG_IS_SLOW to determine movement chance. Always returns BT_RUNNING. + */ +/datum/bt_node/ai_behavior/idle_dog + +/datum/bt_node/ai_behavior/idle_dog/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + + var/obj/item/carry_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM] + if(carry_item && SPT_PROB(5, seconds_per_tick)) + living_pawn.visible_message(span_notice("[living_pawn] gently teethes on \the [carry_item] in [living_pawn.p_their()] mouth."), vision_distance = COMBAT_MESSAGE_RANGE) + + var/move_chance = controller.blackboard[BB_DOG_IS_SLOW] ? 2.5 : 5 + if(isturf(living_pawn.loc) && !living_pawn.pulledby) + if(SPT_PROB(move_chance, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE)) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + else if(SPT_PROB(2, seconds_per_tick)) + living_pawn.manual_emote(pick("dances around.", "chases [living_pawn.p_their()] tail!")) + living_pawn.AddComponent(/datum/component/spinny) + + return AI_BEHAVIOR_DELAY + +///Dog speech updates the BB keys based on the dogs swag +/datum/bt_node/ai_behavior/random_speech_blackboard/dog_random_speech + +/datum/bt_node/ai_behavior/random_speech_blackboard/dog/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/pet/dog/dog_pawn = controller.pawn + if(!istype(dog_pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + dog_pawn.update_dog_speak_blackboard(controller) + return ..() + + +/// Dog harassment: find a TRAIT_HATED_BY_DOGS target nearby, then approach and paw/bite it. +/datum/bt_node/subtree/dog_harassment + behavior_tree_json = "code/datums/ai/dog/dog_harassment.bt.json" diff --git a/code/datums/ai/dog/dog_controller.dm b/code/datums/ai/dog/dog_controller.dm index 29cb890c3ffe..208660f7c44e 100644 --- a/code/datums/ai/dog/dog_controller.dm +++ b/code/datums/ai/dog/dog_controller.dm @@ -5,18 +5,14 @@ BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_dog - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/dog, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_and_hunt_target/find_dog_food, // DARKPACK EDIT ADD - NPC - /datum/ai_planning_subtree/dog_harassment, - ) + behavior_tree_json = "modular_darkpack/master_files/code/datums/ai/dog/dog.bt.json" // DARKPACK EDIT CHANGE - (Find dog food.) /** * Same thing but with make tiny corgis and use access cards. */ /datum/ai_controller/basic_controller/dog/corgi + behavior_tree_json = "code/datums/ai/dog/dog_corgi.bt.json" + blackboard = list( BB_DOG_HARASS_HARM = TRUE, BB_VISION_RANGE = AI_DOG_VISION_RANGE, @@ -29,17 +25,6 @@ BB_BABIES_CHILD_TYPES = list(/mob/living/basic/pet/dog/corgi/puppy), ) - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/dog, - /datum/ai_planning_subtree/make_babies, // Ian WILL prioritise sex over following your instructions - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/dog_harassment, - // Find targets to run away from (uses the targeting strategy from above) - /datum/ai_planning_subtree/simple_find_target, - // Flee from that target - /datum/ai_planning_subtree/flee_target, - ) - /datum/ai_controller/basic_controller/dog/corgi/get_access() var/mob/living/basic/pet/dog/corgi/corgi_pawn = pawn if(!istype(corgi_pawn)) @@ -55,11 +40,5 @@ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/holding_object, // With tongs in hand! BB_TARGET_HELD_ITEM = /obj/item/kitchen/tongs, - ) - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/dog, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/dog_harassment, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/flee_target, + BB_FUCKS = FALSE, // Puppies don't ) diff --git a/code/datums/ai/dog/dog_corgi.bt.json b/code/datums/ai/dog/dog_corgi.bt.json new file mode 100644 index 000000000000..3248cba5b994 --- /dev/null +++ b/code/datums/ai/dog/dog_corgi.bt.json @@ -0,0 +1,74 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/dog/corgi", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/dog_harassment" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_dog" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/datums/ai/dog/dog_harassment.bt.json b/code/datums/ai/dog/dog_harassment.bt.json new file mode 100644 index 000000000000..eca37f0a690a --- /dev/null +++ b/code/datums/ai/dog/dog_harassment.bt.json @@ -0,0 +1,39 @@ +{ + "dm_type": "/datum/bt_node/subtree/dog_harassment", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.1 + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_hated_dog_target", + "vars": { + "target_key": "BB_DOG_HARASS_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DOG_HARASS_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack/dog", + "vars": { + "target_key": "BB_DOG_HARASS_TARGET", + "targeting_strategy": "BB_PET_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } +} diff --git a/code/datums/ai/dog/dog_subtrees.dm b/code/datums/ai/dog/dog_subtrees.dm deleted file mode 100644 index 74c075adad3a..000000000000 --- a/code/datums/ai/dog/dog_subtrees.dm +++ /dev/null @@ -1,38 +0,0 @@ -/// Find someone we don't like and annoy them -/datum/ai_planning_subtree/dog_harassment - -/datum/ai_planning_subtree/dog_harassment/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(10, seconds_per_tick)) - return - controller.queue_behavior(/datum/ai_behavior/find_hated_dog_target, BB_DOG_HARASS_TARGET, BB_PET_TARGETING_STRATEGY) - var/atom/harass_target = controller.blackboard[BB_DOG_HARASS_TARGET] - if (isnull(harass_target)) - return - - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/dog, BB_DOG_HARASS_TARGET, BB_PET_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/find_hated_dog_target - -/datum/ai_behavior/find_hated_dog_target/setup(datum/ai_controller/controller, target_key, targeting_strategy_key) - . = ..() - var/mob/living/dog = controller.pawn - var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - for(var/mob/living/iter_living in oview(2, dog)) - if(iter_living.stat != CONSCIOUS || !HAS_TRAIT(iter_living, TRAIT_HATED_BY_DOGS)) - continue - if(!isnull(dog.buckled)) - dog.audible_message(span_notice("[dog] growls at [iter_living], yet [dog.p_they()] [dog.p_are()] much too comfy to move."), hearing_distance = COMBAT_MESSAGE_RANGE) - continue - if(!targeting_strategy.can_attack(dog, iter_living)) - continue - - dog.audible_message(span_warning("[dog] growls at [iter_living], seemingly annoyed by [iter_living.p_their()] presence."), hearing_distance = COMBAT_MESSAGE_RANGE) - controller.set_blackboard_key(target_key, iter_living) - controller.set_blackboard_key(BB_DOG_HARASS_HARM, FALSE) - return TRUE - - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/find_hated_dog_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm deleted file mode 100644 index ced56912c91c..000000000000 --- a/code/datums/ai/generic/find_and_set.dm +++ /dev/null @@ -1,223 +0,0 @@ -/**find and set - * Finds an item near themselves, sets a blackboard key as it. Very useful for ais that need to use machines or something. - * if you want to do something more complicated than find a single atom, change the search_tactic() proc - * cool tip: search_tactic() can set lists - */ -/datum/ai_behavior/find_and_set - action_cooldown = 2 SECONDS - -/datum/ai_behavior/find_and_set/perform(seconds_per_tick, datum/ai_controller/controller, set_key, locate_path, search_range) - if (controller.blackboard_key_exists(set_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(QDELETED(controller.pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - var/find_this_thing = search_tactic(controller, locate_path, search_range) - if(isnull(find_this_thing)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [find_this_thing] as a target for blackboard key [set_key]! Behavior: [src]", get_turf(find_this_thing), "Target: [find_this_thing]") - EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(find_this_thing)) - controller.set_blackboard_key(set_key, find_this_thing) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_and_set/proc/search_tactic(datum/ai_controller/controller, locate_path, search_range = 3) - return locate(locate_path) in oview(search_range, controller.pawn) - -/** - * Variant of find and set that fails if the living pawn doesn't hold something - */ -/datum/ai_behavior/find_and_set/pawn_must_hold_item - -/datum/ai_behavior/find_and_set/pawn_must_hold_item/search_tactic(datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - if(!living_pawn.get_num_held_items()) - return //we want to fail the search if we don't have something held - return ..() - -/** - * Variant of find and set that also requires the item to be edible. checks hands too - */ -/datum/ai_behavior/find_and_set/food_or_drink - var/force_find_drinks = FALSE - -/datum/ai_behavior/find_and_set/food_or_drink/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - var/find_drinks = force_find_drinks || controller.blackboard[BB_IGNORE_DRINKS] || FALSE - - for(var/atom/held_candidate in living_pawn.held_items) - if(is_food_or_drink(controller, held_candidate, find_drinks)) - return held_candidate - - for(var/atom/local_candidate in oview(search_range, controller.pawn)) - if(is_food_or_drink(controller, local_candidate, find_drinks) && istype(local_candidate, locate_path)) - return local_candidate - - return null - -/datum/ai_behavior/find_and_set/food_or_drink/proc/is_food_or_drink(datum/ai_controller/controller, obj/item/thing, find_drinks = FALSE) - return is_food(thing) || (find_drinks && is_drink(thing)) - -/datum/ai_behavior/find_and_set/food_or_drink/proc/is_food(obj/item/thing) - if(IS_EDIBLE(thing)) - return TRUE - if(istype(thing, /obj/item/reagent_containers/cup/bowl)) - return thing.reagents.total_volume > 0 - return FALSE - -/datum/ai_behavior/find_and_set/food_or_drink/proc/is_drink(obj/item/thing) - if(istype(thing, /obj/item/reagent_containers/cup/glass)) - return thing.reagents.total_volume > 0 - return FALSE - -/datum/ai_behavior/find_and_set/food_or_drink/to_eat - -/datum/ai_behavior/find_and_set/food_or_drink/to_serve - force_find_drinks = TRUE - -/** - * Variant of find and set that only checks in hands, search range should be excluded for this - */ -/datum/ai_behavior/find_and_set/in_hands - -/datum/ai_behavior/find_and_set/in_hands/search_tactic(datum/ai_controller/controller, locate_path) - var/mob/living/living_pawn = controller.pawn - return locate(locate_path) in living_pawn.held_items - -/datum/ai_behavior/find_and_set/in_hands/given_list - -/datum/ai_behavior/find_and_set/in_hands/given_list/search_tactic(datum/ai_controller/controller, locate_paths) - var/list/found = typecache_filter_list(controller.pawn, locate_paths) - if(length(found)) - return pick(found) - -/** - * Variant of find and set that takes a list of things to find. - */ -/datum/ai_behavior/find_and_set/in_list - -/datum/ai_behavior/find_and_set/in_list/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) - if(length(found)) - return pick(found) - -/// Like find_and_set/in_list, but we return the turf location of the item instead of the item itself. -/datum/ai_behavior/find_and_set/in_list/turf_location - -/datum/ai_behavior/find_and_set/in_list/turf_location/search_tactic(datum/ai_controller/controller, locate_paths, search_range) - . = ..() - if(isnull(.)) - return null - - return get_turf(.) - -/** - * Variant of find and set which returns an object which can be animated with a staff of change - */ -/datum/ai_behavior/find_and_set/animatable - -/datum/ai_behavior/find_and_set/animatable/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - - var/list/nearby_items = list() - for (var/obj/new_friend in oview(search_range, controller.pawn)) - if (!isitem(new_friend) && !isstructure(new_friend)) - continue - if (is_type_in_list(new_friend, GLOB.animatable_blacklist)) - continue - if (living_pawn.see_invisible < new_friend.invisibility) - continue - nearby_items += new_friend - - if(nearby_items.len) - return pick(nearby_items) - -/** - * Variant of find and set which returns the nearest wall which isn't invulnerable - */ -/datum/ai_behavior/find_and_set/nearest_wall - -/datum/ai_behavior/find_and_set/nearest_wall/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - - var/list/nearby_walls = list() - for (var/turf/closed/new_wall in oview(search_range, controller.pawn)) - if (isindestructiblewall(new_wall)) - continue - nearby_walls += new_wall - - if(nearby_walls.len) - return get_closest_atom(/turf/closed/, nearby_walls, living_pawn) - -/** - * Variant of find and set which returns corpses who share your faction - */ -/datum/ai_behavior/find_and_set/friendly_corpses - -/datum/ai_behavior/find_and_set/friendly_corpses/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - var/list/nearby_bodies = list() - for (var/mob/living/dead_pal in oview(search_range, controller.pawn)) - if (!isturf(dead_pal.loc)) - continue - if (!dead_pal.stat || dead_pal.health > 0) - continue - if (living_pawn.see_invisible < dead_pal.invisibility) - continue - if (!living_pawn.faction_check_atom(dead_pal)) - continue - nearby_bodies += dead_pal - - if (nearby_bodies.len) - return pick(nearby_bodies) - -/** - * A variant that looks for a human who is not dead or incapacitated, and has a mind - */ -/datum/ai_behavior/find_and_set/conscious_person - -/datum/ai_behavior/find_and_set/conscious_person/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/customers = list() - for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) - if(IS_DEAD_OR_INCAP(target) || !target.mind) - continue - customers += target - - if(customers.len) - return pick(customers) - - return null - -/datum/ai_behavior/find_and_set/nearby_friends - action_cooldown = 2 SECONDS - -/datum/ai_behavior/find_and_set/nearby_friends/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/atom/friend = locate(/mob/living/carbon/human) in oview(search_range, controller.pawn) - - if(isnull(friend)) - return null - - var/mob/living/living_pawn = controller.pawn - var/potential_friend = living_pawn.has_ally(friend) ? friend : null - return potential_friend - - -/datum/ai_behavior/find_and_set/in_list/turf_types - -/datum/ai_behavior/find_and_set/in_list/turf_types/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = RANGE_TURFS(search_range, controller.pawn) - shuffle_inplace(found) - for(var/turf/possible_turf as anything in found) - if(!is_type_in_typecache(possible_turf, locate_paths)) - continue - if(can_see(controller.pawn, possible_turf, search_range)) - return possible_turf - return null - -/datum/ai_behavior/find_and_set/in_list/closest_turf - -/datum/ai_behavior/find_and_set/in_list/closest_turf/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = RANGE_TURFS(search_range, controller.pawn) - for(var/turf/possible_turf as anything in found) - if(!is_type_in_typecache(possible_turf, locate_paths) || !can_see(controller.pawn, possible_turf, search_range)) - found -= possible_turf - return (length(found)) ? get_closest_atom(/turf, found, controller.pawn) : null diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm deleted file mode 100644 index d0e8c25c9d77..000000000000 --- a/code/datums/ai/generic/generic_behaviors.dm +++ /dev/null @@ -1,377 +0,0 @@ - -/datum/ai_behavior/resist/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - living_pawn.ai_controller.set_blackboard_key(BB_RESISTING, TRUE) - living_pawn.execute_resist() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/battle_screech - ///List of possible screeches the behavior has - var/list/screeches - -/datum/ai_behavior/battle_screech/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(screeches)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -///Moves to target then finishes -/datum/ai_behavior/move_to_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - -/datum/ai_behavior/move_to_target/perform(seconds_per_tick, datum/ai_controller/controller) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - -/datum/ai_behavior/break_spine - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 0.7 SECONDS - var/give_up_distance = 10 - -/datum/ai_behavior/break_spine/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/break_spine/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/batman = controller.blackboard[target_key] - var/mob/living/big_guy = controller.pawn //he was molded by the darkness - - if(QDELETED(batman) || get_dist(batman, big_guy) >= give_up_distance) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - if(batman.stat != CONSCIOUS) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - - big_guy.start_pulling(batman) - big_guy.face_atom(batman) - - batman.visible_message(span_warning("[batman] gets a slightly too tight hug from [big_guy]!"), span_userdanger("You feel your body break as [big_guy] embraces you!")) - - for(var/zone in GLOB.all_body_zones - BODY_ZONE_HEAD) - batman.apply_damage(15, BRUTE, zone, wound_bonus = 35) - - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/break_spine/finish_action(datum/ai_controller/controller, succeeded, target_key) - if(succeeded) - var/mob/living/bane = controller.pawn - if(QDELETED(bane)) // pawn can be null at this point - return ..() - bane.stop_pulling() - controller.clear_blackboard_key(target_key) - return ..() - -/// Use in hand the currently held item -/datum/ai_behavior/use_in_hand - behavior_flags = AI_BEHAVIOR_MOVE_AND_PERFORM - - -/datum/ai_behavior/use_in_hand/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/pawn = controller.pawn - var/obj/item/held = pawn.get_active_held_item() - if(!held) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - pawn.activate_hand() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/// Use the currently held item, or unarmed, on a weakref to an object in the world -/datum/ai_behavior/use_on_object - required_distance = 1 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/use_on_object/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - if(target == controller.pawn) // this can sometimes end up as ourselves, in which case there is no reason to move - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/use_on_object/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target = target, combat_mode = FALSE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/give - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - - -/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key) - . = ..() - set_movement_target(controller, controller.blackboard[target_key]) - -/datum/ai_behavior/give/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/pawn = controller.pawn - var/obj/item/held_item = pawn.get_active_held_item() - var/atom/target = controller.blackboard[target_key] - - if(!held_item) //if held_item is null, we pretend that action was successful - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - if(QDELETED(target) || !target.IsReachableBy(pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/living_target = target - if(!isliving(living_target)) // target should reasonably only ever be set to a living mob - stack_trace("Tried to give an item to a non-living target!") - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/perform_flags = try_to_give_item(controller, living_target, held_item) - if(perform_flags & AI_BEHAVIOR_FAILED) - return perform_flags - controller.PauseAi(1.5 SECONDS) - living_target.visible_message( - span_info("[pawn] starts trying to give [held_item] to [living_target]!"), - span_warning("[pawn] tries to give you [held_item]!") - ) - if(!do_after(pawn, 1 SECONDS, living_target)) - return AI_BEHAVIOR_DELAY | perform_flags - - perform_flags |= try_to_give_item(controller, living_target, held_item, actually_give = TRUE) - return AI_BEHAVIOR_DELAY | perform_flags - -/datum/ai_behavior/give/proc/try_to_give_item(datum/ai_controller/controller, mob/living/target, obj/item/held_item, actually_give) - if(QDELETED(held_item) || QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/has_left_pocket = target.can_equip(held_item, ITEM_SLOT_LPOCKET) - var/has_right_pocket = target.can_equip(held_item, ITEM_SLOT_RPOCKET) - var/has_valid_hand - - for(var/hand_index in target.get_empty_held_indexes()) - if(target.can_put_in_hand(held_item, hand_index)) - has_valid_hand = TRUE - break - - if(!has_left_pocket && !has_right_pocket && !has_valid_hand) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(!actually_give) - return AI_BEHAVIOR_DELAY - - if(!has_valid_hand || prob(50)) - target.equip_to_slot_if_possible(held_item, (!has_left_pocket ? ITEM_SLOT_RPOCKET : (prob(50) ? ITEM_SLOT_LPOCKET : ITEM_SLOT_RPOCKET))) - else - target.put_in_hands(held_item) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/give/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/consume - action_cooldown = 2 SECONDS - -/datum/ai_behavior/consume/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hunger_timer_key) - var/mob/living/living_pawn = controller.pawn - var/obj/item/target = controller.blackboard[target_key] - if(QDELETED(target) || !living_pawn.is_holding(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target = living_pawn, combat_mode = FALSE) - - return AI_BEHAVIOR_DELAY | (is_content(living_pawn, target) ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) - -/datum/ai_behavior/consume/finish_action(datum/ai_controller/controller, succeeded, target_key, hunger_timer_key) - . = ..() - if(!succeeded) - return - controller.set_blackboard_key(hunger_timer_key, world.time + rand(12 SECONDS, 60 SECONDS)) - - var/mob/living/living_pawn = controller.pawn - var/obj/item/target = controller.blackboard[target_key] - if(!QDELETED(target) && !DOING_INTERACTION_WITH_TARGET(living_pawn, target)) - controller.clear_blackboard_key(target_key) - living_pawn.dropItemToGround(target) // drops empty drink glasses - for(var/obj/item/trash/trash in living_pawn.held_items) - living_pawn.dropItemToGround(trash) // drops spawned trash items - -/// Check if the target is fully consumed, or being actively consumed, or if we're just bored of eating it -/datum/ai_behavior/consume/proc/is_content(mob/living/living_pawm, obj/item/target) - if(QDELETED(target)) - return TRUE - if(DOING_INTERACTION_WITH_TARGET(living_pawm, target)) - return TRUE - if(target.reagents?.total_volume <= 0) - return TRUE - // Even if we don't finish it all we can randomly decide to be done - return prob(10) - -// navigate to target item and pick it up if we can -/datum/ai_behavior/navigate_to_and_pick_up - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 2 SECONDS - -/datum/ai_behavior/navigate_to_and_pick_up/setup(datum/ai_controller/controller, target_key, drop_held = TRUE) - . = ..() - set_movement_target(controller, controller.blackboard[target_key]) - -/datum/ai_behavior/navigate_to_and_pick_up/setup(datum/ai_controller/controller, target_key, drop_held = TRUE) - var/mob/living/living_pawn = controller.pawn - var/obj/item/target = controller.blackboard[target_key] - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(living_pawn.is_holding(target)) // already in hands - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(!target.IsReachableBy(living_pawn)) // can't reach it, despite being adjacent - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(living_pawn.get_active_held_item()) // something is in our hands already - if(!drop_held || !living_pawn.dropItemToGround(living_pawn.get_active_held_item())) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target, combat_mode = FALSE) - return AI_BEHAVIOR_DELAY | (target.loc == living_pawn ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) - -/** - * Drops items in hands, very important for future behaviors that require the pawn to grab stuff - */ -/datum/ai_behavior/drop_item - -/datum/ai_behavior/drop_item/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - var/list/my_held_items = living_pawn.held_items - GetBestWeapon(controller, null, living_pawn.held_items) - if(!length(my_held_items)) - return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY - living_pawn.dropItemToGround(pick(my_held_items)) - return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY - -/// This behavior involves attacking a target. -/datum/ai_behavior/attack - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/attack/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn) || !isturf(living_pawn.loc)) - return AI_BEHAVIOR_DELAY - - var/atom/movable/attack_target = controller.blackboard[BB_ATTACK_TARGET] - if(!attack_target || !can_see(living_pawn, attack_target, length = controller.blackboard[BB_VISION_RANGE])) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/living_target = attack_target - if(istype(living_target) && (living_target.stat == DEAD)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - set_movement_target(controller, living_target) - attack(controller, living_target) - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/attack/finish_action(datum/ai_controller/controller, succeeded) - . = ..() - controller.clear_blackboard_key(BB_ATTACK_TARGET) - -/// A proc representing when the mob is pushed to actually attack the target. Again, subtypes can be used to represent different attacks from different animals, or it can be some other generic behavior -/datum/ai_behavior/attack/proc/attack(datum/ai_controller/controller, mob/living/living_target) - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn)) - return - living_pawn.ClickOn(living_target, list()) - -/// This behavior involves attacking a target. -/datum/ai_behavior/follow - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - -/datum/ai_behavior/follow/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn) || !isturf(living_pawn.loc)) - return AI_BEHAVIOR_DELAY - - var/atom/movable/follow_target = controller.blackboard[BB_FOLLOW_TARGET] - if(!follow_target || get_dist(living_pawn, follow_target) > controller.blackboard[BB_VISION_RANGE]) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/living_target = follow_target - if(istype(living_target) && (living_target.stat == DEAD)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - set_movement_target(controller, living_target) - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/follow/finish_action(datum/ai_controller/controller, succeeded) - . = ..() - controller.clear_blackboard_key(BB_FOLLOW_TARGET) - -/datum/ai_behavior/perform_emote - -/datum/ai_behavior/perform_emote/perform(seconds_per_tick, datum/ai_controller/controller, emote, speech_sound) - . = ..() - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn)) - return AI_BEHAVIOR_INSTANT - living_pawn.manual_emote(emote) - if(speech_sound) // Only audible emotes will pass in a sound - playsound(living_pawn, speech_sound, 80, vary = TRUE, pressure_affected =TRUE, ignore_walls = FALSE) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/perform_speech - -/datum/ai_behavior/perform_speech/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound) - . = ..() - - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn)) - return AI_BEHAVIOR_INSTANT - living_pawn.say(speech, forced = "AI Controller") - if(speech_sound) - playsound(living_pawn, speech_sound, 80, vary = TRUE) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/perform_speech_radio - -/datum/ai_behavior/perform_speech_radio/perform(seconds_per_tick, datum/ai_controller/controller, speech, obj/item/radio/speech_radio, list/try_channels = list(RADIO_CHANNEL_COMMON)) - var/mob/living/living_pawn = controller.pawn - if(!istype(living_pawn) || !istype(speech_radio) || QDELETED(speech_radio) || !length(try_channels)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - speech_radio.talk_into(living_pawn, speech, pick(try_channels)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - -//song behaviors - -/datum/ai_behavior/setup_instrument - -/datum/ai_behavior/setup_instrument/perform(seconds_per_tick, datum/ai_controller/controller, song_instrument_key, song_lines_key) - var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key] - var/datum/song/song = song_instrument.song - var/song_lines = controller.blackboard[song_lines_key] - - //just in case- it won't do anything if the instrument isn't playing - song.stop_playing() - song.ParseSong(new_song = song_lines) - song.repeat = 10 - song.volume = song.max_volume - 10 - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/play_instrument - -/datum/ai_behavior/play_instrument/perform(seconds_per_tick, datum/ai_controller/controller, song_instrument_key) - var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key] - var/datum/song/song = song_instrument.song - - song.start_playing(controller.pawn) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_nearby - -/datum/ai_behavior/find_nearby/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/list/possible_targets = list() - for(var/atom/thing in view(2, controller.pawn)) - if(!thing.mouse_opacity) - continue - if(thing.IsObscured()) - continue - if(isitem(thing)) - var/obj/item/item = thing - if(item.item_flags & ABSTRACT) - continue - possible_targets += thing - if(!possible_targets.len) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(target_key, pick(possible_targets)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic/generic_subtrees.dm b/code/datums/ai/generic/generic_subtrees.dm deleted file mode 100644 index 6fe92668f916..000000000000 --- a/code/datums/ai/generic/generic_subtrees.dm +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Generic Instrument Subtree, For your pawn playing instruments - * - * Requires at least a living mob that can hold items. - * - * relevant blackboards: - * * BB_SONG_INSTRUMENT - set by this subtree, is the song datum the pawn plays music from. - * * BB_SONG_LINES - not set by this subtree, is the song loaded into the song datum. - */ -/datum/ai_planning_subtree/generic_play_instrument/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/obj/item/instrument/song_player = controller.blackboard[BB_SONG_INSTRUMENT] - - if(!song_player) - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_hands, BB_SONG_INSTRUMENT, /obj/item/instrument) - return //we can't play a song since we do not have an instrument - - var/list/parsed_song_lines = splittext(controller.blackboard[BB_SONG_LINES], "\n") - popleft(parsed_song_lines) //remove BPM as it is parsed out - if(!compare_list(song_player.song.lines, parsed_song_lines) || !song_player.song.repeat) - controller.queue_behavior(/datum/ai_behavior/setup_instrument, BB_SONG_INSTRUMENT, BB_SONG_LINES) - - if(!song_player.song.playing) //we may stop playing if we weren't playing before, were setting up dk theme, or ran out of repeats (also causing setup behavior) - controller.queue_behavior(/datum/ai_behavior/play_instrument, BB_SONG_INSTRUMENT) - -/datum/ai_planning_subtree/generic_play_instrument/end_planning - -/datum/ai_planning_subtree/generic_play_instrument/end_planning/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if (controller.blackboard_key_exists(BB_SONG_INSTRUMENT)) - return SUBTREE_RETURN_FINISH_PLANNING // Don't plan anything else if we're playing an instrument - - -/** - * Generic Resist Subtree, resist if it makes sense to! - * - * Requires nothing beyond a living pawn, makes sense on a good amount of mobs since anything can get buckled. - * - * relevant blackboards: - * * None! - */ -/datum/ai_planning_subtree/generic_resist/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - if(SHOULD_RESIST(living_pawn) && SPT_PROB(RESIST_SUBTREE_PROB, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/resist) //BRO IM ON FUCKING FIRE BRO - return SUBTREE_RETURN_FINISH_PLANNING //IM NOT DOING ANYTHING ELSE BUT EXTINGUISH MYSELF, GOOD GOD HAVE MERCY. - -/** - * Generic Hunger Subtree, - * - * Requires at least a living mob that can hold items. - * - * relevant blackboards: - * * BB_NEXT_HUNGRY - set by this subtree, is when the controller is next hungry - */ -/datum/ai_planning_subtree/generic_hunger - -/datum/ai_planning_subtree/generic_hunger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.nutrition > NUTRITION_LEVEL_HUNGRY) - return - - var/next_eat = controller.blackboard[BB_NEXT_HUNGRY] - if(!next_eat) - //inits the blackboard timer - next_eat = world.time + rand(0, 30 SECONDS) - controller.set_blackboard_key(BB_NEXT_HUNGRY, next_eat) - - if(world.time < next_eat) - return - - // find food - var/atom/food_target = controller.blackboard[BB_FOOD_TARGET] - if(isnull(food_target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/food_or_drink/to_eat, BB_FOOD_TARGET, /obj/item, 2) - return SUBTREE_RETURN_FINISH_PLANNING - - if(living_pawn.is_holding(food_target)) - controller.queue_behavior(/datum/ai_behavior/consume, BB_FOOD_TARGET, BB_NEXT_HUNGRY) - // it's been moved since we found it - else if(!isturf(food_target.loc)) - // someone took it. we will fight over it! - if(isliving(food_target.loc) && will_fight_for_food(food_target.loc, living_pawn, controller)) - controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, food_target.loc, MONKEY_FOOD_HATRED_AMOUNT) - // eh, find something else - else - controller.clear_blackboard_key(BB_FOOD_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - else - controller.queue_behavior(/datum/ai_behavior/navigate_to_and_pick_up, BB_FOOD_TARGET, TRUE) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_planning_subtree/generic_hunger/proc/will_fight_for_food(mob/living/thief, mob/living/monkey, datum/ai_controller/controller) - if(controller.blackboard[BB_MONKEY_AGGRESSIVE]) - return TRUE - if(controller.blackboard[BB_MONKEY_TAMED]) - return FALSE - return prob(100 * ((NUTRITION_LEVEL_HUNGRY - monkey.nutrition) / NUTRITION_LEVEL_HUNGRY)) diff --git a/code/datums/ai/generic_behaviors/acquire_injured_target.dm b/code/datums/ai/generic_behaviors/acquire_injured_target.dm new file mode 100644 index 000000000000..50bd0090da43 --- /dev/null +++ b/code/datums/ai/generic_behaviors/acquire_injured_target.dm @@ -0,0 +1,15 @@ +/datum/bt_node/ai_behavior/retrieve_injured_rider + ///where do we save our target + var/target_key + +/datum/bt_node/ai_behavior/retrieve_injured_rider/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_mob = controller.pawn + if (!length(living_mob.buckled_mobs) || !isliving(living_mob.buckled_mobs[1])) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/rider = living_mob.buckled_mobs[1] + if (rider.stat == STABLE || rider.stat == DEAD || rider.health >= rider.maxHealth) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(target_key, rider) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/acquire_target.dm b/code/datums/ai/generic_behaviors/acquire_target.dm new file mode 100644 index 000000000000..6311d82fe2c0 --- /dev/null +++ b/code/datums/ai/generic_behaviors/acquire_target.dm @@ -0,0 +1,198 @@ +///Baseline for target picking behaviors +/datum/bt_node/ai_behavior/acquire_target + /// Blackboard key to write the found target into. + var/target_key + /// Either a /datum/targeting_strategy typepath (resolved directly) or a blackboard key string + var/targeting_strategy = BB_TARGETING_STRATEGY + /// Typepath of the /datum/target_source type used to gather candidates. turned into the singleton instance when used + var/target_source = /datum/target_source/oview + /// How far to scan for candidates (passed to the target source). Can be a key too. + var/vision_range = 7 + /// How to behave when a target is already set. See TARGET_* defines in ai.dm. + var/revalidation_mode = TARGET_REVALIDATE + /// Extended range for retaining an existing target when candidates run dry. 0 = disabled. + var/target_loss_distance = 12 + /// Optional blackboard key holding a lazylist of atoms to skip while filtering candidates. + var/ignore_list_key + /// If TRUE, only candidates the controller can path to are eligible; unreachable ones are reported via note_unreachable_target. + var/must_be_reachable = FALSE + /// Pathfinding distance limit used when must_be_reachable is set. + var/reach_distance = 10 + /// How close the reachability path must get to the target (0 = onto/adjacent). Passed to can_reach_target when must_be_reachable is set. + var/minimum_distance = 0 + /// Strategy/range snapshot from the perform() that kicked off the current async search. + VAR_PRIVATE/datum/targeting_strategy/search_strategy + /// Range snapshot from the perform() that kicked off the current async search. + VAR_PRIVATE/search_range + +/datum/bt_node/ai_behavior/acquire_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + if(!can_search(controller)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/datum/targeting_strategy/strategy = get_targeting_strategy(controller) + var/atom/current_target = controller.blackboard[target_key] + + if(should_keep_target(controller, strategy, current_target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + if(!isnum(vision_range)) //If there's a blackboard override, use that + vision_range = controller.blackboard[vision_range] || vision_range + + if(!must_be_reachable) + return find_and_set_target(controller, strategy, vision_range) + + // Kick off async reachability search. + search_strategy = strategy + search_range = vision_range + return start_async() + +/datum/bt_node/ai_behavior/acquire_target/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + search_strategy = null + search_range = null + +/// Returns TRUE to abort the search before it starts (e.g. a detection field is already active). +/datum/bt_node/ai_behavior/acquire_target/proc/can_search(datum/ai_controller/controller) + return TRUE + +/// Returns TRUE if the current target is still good and we should skip the search. +/datum/bt_node/ai_behavior/acquire_target/proc/should_keep_target(datum/ai_controller/controller, datum/targeting_strategy/strategy, atom/current_target) + switch(revalidation_mode) + if(TARGET_KEEP_IF_SET) + return !isnull(current_target) + if(TARGET_REVALIDATE) + return !isnull(current_target) && strategy.is_valid_target(controller.pawn, current_target, vision_range, controller) + return FALSE + +///Resolves the targeting strategy for this behavior +/datum/bt_node/ai_behavior/acquire_target/proc/get_targeting_strategy(datum/ai_controller/controller) + if(!targeting_strategy) + return GET_TARGETING_STRATEGY(/datum/targeting_strategy/anything) + if(ispath(targeting_strategy)) + return GET_TARGETING_STRATEGY(targeting_strategy) + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy]) + if(!strategy) + CRASH("No targeting strategy was supplied in the blackboard for [controller.pawn]") + return strategy + + +///Actual behavior for collecting and filtering targets +/datum/bt_node/ai_behavior/acquire_target/proc/find_and_set_target(datum/ai_controller/controller, datum/targeting_strategy/targeting_strategy, range) + var/mob/living/living_mob = controller.pawn + var/datum/target_source/source = GET_TARGET_SOURCE(target_source) + if(!source) + CRASH("No target source found for type [target_source] on [controller.pawn]") + + var/atom/current_target = controller.blackboard[target_key] + var/list/candidates = source.collect_candidates(living_mob, controller, range) + + if(!length(candidates)) + candidates = on_no_candidates(controller, current_target, targeting_strategy, range) + if(!length(candidates)) + clear_stale_target(controller, current_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/list/filtered = filter_candidates(controller, candidates, targeting_strategy, current_target) + + if(!length(filtered)) + on_no_valid_candidates(controller, current_target) + clear_stale_target(controller, current_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/atom/target = pick_final_target(controller, filtered) + + if(isnull(target)) + on_no_valid_candidates(controller, current_target) + clear_stale_target(controller, current_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [target] as a target for blackboard key [target_key]! Behavior: [src]", get_turf(target), "Target: [target]") + EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(target)) + + if(target != current_target) + controller.set_blackboard_key(target_key, target) + + on_target_found(controller, target, targeting_strategy) + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/// Clears the target key when revalidating and the search turned up nothing, so a stale target doesn't linger. +/datum/bt_node/ai_behavior/acquire_target/proc/clear_stale_target(datum/ai_controller/controller, atom/current_target) + if(revalidation_mode != TARGET_REVALIDATE || isnull(current_target)) + return + controller.clear_blackboard_key(target_key) + +///Fallback for no targets found. +/datum/bt_node/ai_behavior/acquire_target/proc/on_no_candidates(datum/ai_controller/controller, atom/current_target, datum/targeting_strategy/strategy, range) + if(!target_loss_distance || !current_target) + return list() + if(strategy.can_keep_target(controller.pawn, current_target, target_loss_distance, controller)) + return list(current_target) + return list() + +/// Filters the candidate list to valid targets. Override to add priority filtering or other per-candidate criteria. +/datum/bt_node/ai_behavior/acquire_target/proc/filter_candidates(datum/ai_controller/controller, list/candidates, datum/targeting_strategy/strategy, atom/current_target) + var/mob/living/pawn = controller.pawn + var/list/ignore_list = ignore_list_key ? controller.blackboard[ignore_list_key] : null + var/list/filtered = list() + for(var/atom/candidate as anything in candidates) + if(LAZYACCESS(ignore_list, candidate)) + continue + if(!strategy.is_valid_target(pawn, candidate, vision_range, controller)) + continue + filtered += candidate + return filtered + +/// Called when filter_candidates produces nothing. Override to trigger side effects (e.g. spawning a detection field). +/datum/bt_node/ai_behavior/acquire_target/proc/on_no_valid_candidates(datum/ai_controller/controller, atom/current_target) + return + +/// Called after a target is selected and written to the blackboard. Override for post-selection side effects. +/datum/bt_node/ai_behavior/acquire_target/proc/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + return + +/// Picks the final target from filtered candidates. Only valid for the non-reachable path; the reachable path goes async. +/datum/bt_node/ai_behavior/acquire_target/proc/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + return pick(filtered_targets) + +/// perform_async(): walks filtered candidates checking reachability (may sleep), then commits the result via finish_async(). +/datum/bt_node/ai_behavior/acquire_target/perform_async(datum/ai_controller/controller) + var/datum/targeting_strategy/strategy = search_strategy + var/range = search_range + var/mob/living/living_mob = controller.pawn + var/datum/target_source/source = GET_TARGET_SOURCE(target_source) + var/atom/current_target = controller.blackboard[target_key] + var/list/candidates = source.collect_candidates(living_mob, controller, range) + if(!length(candidates)) + candidates = on_no_candidates(controller, current_target, strategy, range) + var/list/filtered = filter_candidates(controller, candidates, strategy, current_target) + var/atom/target + for(var/atom/candidate as anything in filtered) + // get_path_to may sleep here check abort flag after it returns. + if(controller.can_reach_target(candidate, reach_distance, minimum_distance)) + target = candidate + break + if(!async_still_valid()) + return + controller.note_unreachable_target(candidate) + // If finish_action fired while we were sleeping, bail without touching anything. + if(!async_still_valid()) + return + if(!isnull(target)) + if(target != controller.blackboard[target_key]) + controller.set_blackboard_key(target_key, target) + on_target_found(controller, target, strategy) + else + on_no_valid_candidates(controller, current_target) + clear_stale_target(controller, current_target) + finish_async(isnull(target) ? AI_BEHAVIOR_FAILED : AI_BEHAVIOR_SUCCEEDED) + +///Finds a nearby target to interact with, used as a baseline for behaviors that need to interact with something nearby. +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target + target_source = /datum/target_source/oview + revalidation_mode = TARGET_REVALIDATE + time_between_perform = 2 SECONDS diff --git a/code/datums/ai/generic_behaviors/agile_melee_movement.dm b/code/datums/ai/generic_behaviors/agile_melee_movement.dm new file mode 100644 index 000000000000..60e80e8956d9 --- /dev/null +++ b/code/datums/ai/generic_behaviors/agile_melee_movement.dm @@ -0,0 +1,157 @@ +/// Not moving under our own direction yet. +#define AGILE_MELEE_MODE_NONE 0 +/// Beelining into melee range, min distance 1. +#define AGILE_MELEE_MODE_CLOSING 1 +/// Backstepping out of melee range, min distance 0. +#define AGILE_MELEE_MODE_RETREATING 2 +/// Shuffling around inside the standoff band, min distance 0. +#define AGILE_MELEE_MODE_JITTERING 3 +/// Standing still for a beat after landing a hit. +#define AGILE_MELEE_MODE_HOLDING 4 + +///Yakety sax type melee movement; If you can attack (next_move off cooldown), then get in range, otherwise, move inbetween standdoff_min and max, if within the band; randomly jitter in the band. +/datum/bt_node/ai_behavior/agile_melee_movement + /// Blackboard key holding the atom to fight. + var/target_key + /// Distance we back off to once we've swung. + var/standoff_min = 2 + /// Distance past which we close in again even while on cooldown, so we never lose the target. + var/standoff_max = 3 + /// Percent chance per tick to take another step while loitering inside the standoff band. + var/jitter_chance = 100 + /// time to stay planted until after a hit. Makes it a bit more fair to fight against. + var/wait_after_attack = 0.3 SECONDS + /// Set by on_movement_failed() when the movement system gives up pathing. + VAR_FINAL/movement_failed = FALSE + /// Which AGILE_MELEE_MODE_ we drove movement with last tick, so we only tear the moveloop down when it flips. + VAR_PRIVATE/current_mode = AGILE_MELEE_MODE_NONE + /// Last next_move we saw on the pawn. A jump means a hit landed, which is what starts the post attack pause. + VAR_PRIVATE/last_next_move = 0 + /// When we can move post attack + VAR_PRIVATE/hold_until = 0 + +/datum/bt_node/ai_behavior/agile_melee_movement/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + if(isliving(controller.pawn)) + var/mob/living/living_pawn = controller.pawn + last_next_move = living_pawn.next_move + RegisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED, PROC_REF(on_movement_failed)) + return TRUE + +/datum/bt_node/ai_behavior/agile_melee_movement/proc/on_movement_failed(atom/source) + SIGNAL_HANDLER + movement_failed = TRUE + +/datum/bt_node/ai_behavior/agile_melee_movement/perform(seconds_per_tick, datum/ai_controller/controller) + if(movement_failed) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/mob/pawn = controller.pawn + var/distance = get_dist(pawn, target) + + if(wait_after_attack && isliving(pawn)) + var/mob/living/living_pawn = pawn + if(living_pawn.next_move > last_next_move) + hold_until = world.time + wait_after_attack + last_next_move = living_pawn.next_move + + if(hold_until > world.time) + set_movement_mode(controller, AGILE_MELEE_MODE_HOLDING, initial(controller.ai_movement)) + return AI_BEHAVIOR_INSTANT + + if(can_swing(pawn) || distance > standoff_max) + close_in(controller, target) + return AI_BEHAVIOR_INSTANT + + if(distance < standoff_min) + retreat(controller, target) + return AI_BEHAVIOR_INSTANT + + jitter(controller, target) + return AI_BEHAVIOR_INSTANT + +/datum/bt_node/ai_behavior/agile_melee_movement/finish_action(datum/ai_controller/controller, succeeded) + UnregisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED) + movement_failed = FALSE + current_mode = AGILE_MELEE_MODE_NONE + last_next_move = 0 + hold_until = 0 + controller.ai_movement.stop_moving_towards(controller) + controller.change_ai_movement_type(initial(controller.ai_movement)) + return ..() + +/// Whether our melee attack is off cooldown, meaning it's worth being adjacent right now. +/datum/bt_node/ai_behavior/agile_melee_movement/proc/can_swing(mob/pawn) + if(!isliving(pawn)) + return TRUE + var/mob/living/living_pawn = pawn + return world.time >= living_pawn.next_move + + +///Switches which mode is driving our movement, tearing the current moveloop down first. +/datum/bt_node/ai_behavior/agile_melee_movement/proc/set_movement_mode(datum/ai_controller/controller, mode, datum/ai_movement/movement_type) + if(mode == current_mode) + return + controller.ai_movement.stop_moving_towards(controller) + controller.change_ai_movement_type(movement_type) + current_mode = mode + +/// Beelines into melee range. Retargeting to a new target is handled inside start_moving_towards(). +/datum/bt_node/ai_behavior/agile_melee_movement/proc/close_in(datum/ai_controller/controller, atom/target) + set_movement_mode(controller, AGILE_MELEE_MODE_CLOSING, initial(controller.ai_movement)) + controller.ai_movement.start_moving_towards(controller, target, 1) + +/// Steps one tile away from target using backstep avoidance, falling back to shuffled directions if blocked. +/datum/bt_node/ai_behavior/agile_melee_movement/proc/retreat(datum/ai_controller/controller, atom/target) + set_movement_mode(controller, AGILE_MELEE_MODE_RETREATING, /datum/ai_movement/basic_avoidance/backstep) + var/mob/pawn = controller.pawn + pawn.face_atom(target) + var/turf/next_step = get_step_away(pawn, target) + if(!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) + controller.ai_movement.start_moving_towards(controller, next_step, 0, controller.movement_delay) + return TRUE + var/list/all_dirs = GLOB.alldirs.Copy() + all_dirs -= get_dir(pawn, next_step) + all_dirs -= get_dir(pawn, target) + shuffle_inplace(all_dirs) + for(var/dir in all_dirs) + next_step = get_step(pawn, dir) + if(!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) + controller.ai_movement.start_moving_towards(controller, next_step, 0, controller.movement_delay) + return TRUE + return FALSE + +/// Takes one step to a random adjacent turf, anywhere that keeps us inside the standoff band. +/datum/bt_node/ai_behavior/agile_melee_movement/proc/jitter(datum/ai_controller/controller, atom/target) + if(!prob(jitter_chance)) + return + + var/mob/pawn = controller.pawn + var/list/candidates = list() + for(var/direction in GLOB.alldirs) + var/turf/step_turf = get_step(pawn, direction) + if(isnull(step_turf) || step_turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + var/step_distance = get_dist(step_turf, target) + if(step_distance < standoff_min || step_distance > standoff_max) + continue + candidates += step_turf + + if(!length(candidates)) + return + + set_movement_mode(controller, AGILE_MELEE_MODE_JITTERING, initial(controller.ai_movement)) + pawn.face_atom(target) + controller.ai_movement.start_moving_towards(controller, pick(candidates), 0, controller.movement_delay) + +#undef AGILE_MELEE_MODE_NONE +#undef AGILE_MELEE_MODE_CLOSING +#undef AGILE_MELEE_MODE_RETREATING +#undef AGILE_MELEE_MODE_JITTERING +#undef AGILE_MELEE_MODE_HOLDING diff --git a/code/datums/ai/generic_behaviors/ai_interact.dm b/code/datums/ai/generic_behaviors/ai_interact.dm new file mode 100644 index 000000000000..29e54e626e2d --- /dev/null +++ b/code/datums/ai/generic_behaviors/ai_interact.dm @@ -0,0 +1,17 @@ +/// Interacts with the atom at target_key once, with optional combat_mode (default FALSE). +/datum/bt_node/ai_behavior/ai_interact + /// Blackboard key holding the atom to interact with. + var/target_key + /// Whether to interact in combat mode. + var/combat_mode = FALSE + +/datum/bt_node/ai_behavior/ai_interact/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/ai_interact/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, combat_mode) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/attack_obstacles.dm b/code/datums/ai/generic_behaviors/attack_obstacles.dm new file mode 100644 index 000000000000..4e15357f5167 --- /dev/null +++ b/code/datums/ai/generic_behaviors/attack_obstacles.dm @@ -0,0 +1,72 @@ + +///Destroy shit int he way +/datum/bt_node/ai_behavior/attack_obstructions + var/target_key + time_between_perform = 2 SECONDS + /// If we should attack walls, be prepared for complaints about breaches + var/can_attack_turfs = FALSE + /// For if you want your mob to be able to attack dense objects + var/can_attack_dense_objects = FALSE + /// Set this if using JPS movement, since if we use basic avoidance we should check get_step_to as well + var/can_ignore_step = FALSE + +/datum/bt_node/ai_behavior/attack_obstructions/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/basic_mob = controller.pawn + var/atom/target = controller.blackboard[target_key] + + if(QDELETED(target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/turf/next_step = get_step_towards(basic_mob, target) + + if(!next_step.is_blocked_turf(exclude_mobs = TRUE, source_atom = controller.pawn)) + if(can_ignore_step) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED // Path clear let selector fall through to melee + if(get_step_to(controller.pawn, next_step)) //pathing could still fail because byond sucks. + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED // Path clear let selector fall through to melee + + var/dir_to_next_step = get_dir(basic_mob, next_step) + var/list/dirs_to_move = list() + if(ISDIAGONALDIR(dir_to_next_step)) + for(var/direction in GLOB.cardinals) + if(direction & dir_to_next_step) + dirs_to_move += direction + else + dirs_to_move += dir_to_next_step + + for(var/direction in dirs_to_move) + if(attack_in_direction(controller, basic_mob, direction)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED // Nothing smashable let selector fall through + +/datum/bt_node/ai_behavior/attack_obstructions/proc/attack_in_direction(datum/ai_controller/controller, mob/living/basic/basic_mob, direction) + var/turf/next_step = get_step(basic_mob, direction) + if(!next_step.is_blocked_turf(exclude_mobs = TRUE, source_atom = controller.pawn)) + if(get_step_to(controller.pawn, next_step)) //pathing could still fail because byond sucks. + return FALSE + + for(var/obj/object as anything in next_step.contents) + if(!can_smash_object(basic_mob, object)) + continue + basic_mob.melee_attack(object) + return TRUE + + if(can_attack_turfs) + basic_mob.melee_attack(next_step) + return TRUE + return FALSE + +/datum/bt_node/ai_behavior/attack_obstructions/proc/can_smash_object(mob/living/basic/basic_mob, obj/object) + if(!object.density && !can_attack_dense_objects) + return FALSE + if(object.IsObscured()) + return FALSE + if(basic_mob.see_invisible < object.invisibility) + return FALSE + var/list/whitelist = basic_mob.ai_controller.blackboard[BB_OBSTACLE_TARGETING_WHITELIST] + if(whitelist && !is_type_in_typecache(object, whitelist)) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/attack_obstructions/attack_turfs + can_attack_turfs = TRUE diff --git a/code/datums/ai/generic_behaviors/battle_screech.dm b/code/datums/ai/generic_behaviors/battle_screech.dm new file mode 100644 index 000000000000..eee357e942ef --- /dev/null +++ b/code/datums/ai/generic_behaviors/battle_screech.dm @@ -0,0 +1,10 @@ +/// Emotes a random screech from a list of screeches defined on the subtype. +/datum/bt_node/ai_behavior/battle_screech + /// List of possible screeches the behavior has + var/list/screeches + +/datum/bt_node/ai_behavior/battle_screech/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(screeches)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + diff --git a/code/datums/ai/generic_behaviors/break_out_of_object.dm b/code/datums/ai/generic_behaviors/break_out_of_object.dm new file mode 100644 index 000000000000..239222e55f5b --- /dev/null +++ b/code/datums/ai/generic_behaviors/break_out_of_object.dm @@ -0,0 +1,40 @@ +/// Attacks an escape target until the pawn is no longer buckled to or contained by it. +/datum/bt_node/ai_behavior/break_out_of_object + time_between_perform = 0.2 SECONDS + /// The object to break out of by attacking it. + var/atom/target_atom + +/datum/bt_node/ai_behavior/break_out_of_object/setup(datum/ai_controller/controller) + if (!should_attack_target(controller, target_atom)) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/break_out_of_object/perform(seconds_per_tick, datum/ai_controller/controller) + if (!should_attack_target(controller, target_atom)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target_atom, TRUE) + return AI_BEHAVIOR_DELAY + +/datum/bt_node/ai_behavior/break_out_of_object/proc/should_attack_target(datum/ai_controller/controller, atom/target) + if (QDELETED(target)) + return FALSE + var/mob/living/pawn = controller.pawn + if (!target.IsReachableBy(pawn)) + return FALSE + return pawn.loc == target || pawn.buckled == target + +/// Variant that reads the escape target from a blackboard key instead of a direct reference. +/datum/bt_node/ai_behavior/break_out_of_object/from_bb + /// Blackboard key holding the object to break out of. + var/target_key + +/datum/bt_node/ai_behavior/break_out_of_object/from_bb/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + return should_attack_target(controller, target) + +/datum/bt_node/ai_behavior/break_out_of_object/from_bb/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(!should_attack_target(controller, target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, TRUE) + return AI_BEHAVIOR_DELAY diff --git a/code/datums/ai/generic_behaviors/break_spine.dm b/code/datums/ai/generic_behaviors/break_spine.dm new file mode 100644 index 000000000000..a99e93b120c2 --- /dev/null +++ b/code/datums/ai/generic_behaviors/break_spine.dm @@ -0,0 +1,42 @@ +/// Applies full-body brute damage to a target while pulling them. Used with a move_to_target sequence. +/datum/bt_node/ai_behavior/break_spine + var/target_key + var/give_up_distance = 10 + +/datum/bt_node/ai_behavior/break_spine/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/batman = controller.blackboard[target_key] + var/mob/living/big_guy = controller.pawn + + if(QDELETED(batman) || get_dist(batman, big_guy) >= give_up_distance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(IS_UNCONSCIOUS_OR_CRIT(batman)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + INVOKE_ASYNC(big_guy, TYPE_PROC_REF(/atom/movable, start_pulling), batman) + big_guy.face_atom(batman) + batman.visible_message(span_warning("[batman] gets a slightly too tight hug from [big_guy]!"), span_userdanger("You feel your body break as [big_guy] embraces you!")) + for(var/zone in GLOB.all_body_zones - BODY_ZONE_HEAD) + batman.apply_damage(15, BRUTE, zone, wound_bonus = 35) + + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/break_spine/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + if(succeeded) + var/mob/living/attacker = controller.pawn + if(!QDELETED(attacker)) + attacker.stop_pulling() + controller.clear_blackboard_key(target_key) + +/// Bane variant: says a quote from bane.json on successful spine-breaking. +/datum/bt_node/ai_behavior/break_spine/bane + +/datum/bt_node/ai_behavior/break_spine/bane/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + if(succeeded) + var/mob/living/bane = controller.pawn + if(QDELETED(bane)) + return + var/list/bane_quotes = strings("bane.json", "bane") + INVOKE_ASYNC(bane, TYPE_PROC_REF(/atom/movable, say), pick(bane_quotes)) diff --git a/code/datums/ai/generic_behaviors/cancel_current_plan.dm b/code/datums/ai/generic_behaviors/cancel_current_plan.dm new file mode 100644 index 000000000000..74fe54b83e7e --- /dev/null +++ b/code/datums/ai/generic_behaviors/cancel_current_plan.dm @@ -0,0 +1,6 @@ +/// Cancels the controller's current plan, causing the BT to re-evaluate from the root next tick. +/datum/bt_node/ai_behavior/cancel_current_plan + +/datum/bt_node/ai_behavior/cancel_current_plan/perform(seconds_per_tick, datum/ai_controller/controller) + controller.cancel_current_plan() + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/clear_key.dm b/code/datums/ai/generic_behaviors/clear_key.dm new file mode 100644 index 000000000000..fffbc70e9511 --- /dev/null +++ b/code/datums/ai/generic_behaviors/clear_key.dm @@ -0,0 +1,8 @@ +/// BT-native version: clears a single blackboard key. Returns INSTANT SUCCESS. +/datum/bt_node/ai_behavior/clear_key + /// Blackboard key to clear. + var/key + +/datum/bt_node/ai_behavior/clear_key/perform(seconds_per_tick, datum/ai_controller/controller) + controller.clear_blackboard_key(key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/consume.dm b/code/datums/ai/generic_behaviors/consume.dm new file mode 100644 index 000000000000..f8562ce6b197 --- /dev/null +++ b/code/datums/ai/generic_behaviors/consume.dm @@ -0,0 +1,43 @@ +/// Uses the pawn's held food/drink item on themselves until consumed. +/datum/bt_node/ai_behavior/consume + time_between_perform = 2 SECONDS + /// Blackboard key holding the food/drink item to consume. + var/target_key + /// Blackboard key used to store the next-hunger time once finished. + var/hunger_timer_key + +/datum/bt_node/ai_behavior/consume/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/obj/item/target = controller.blackboard[target_key] + if(QDELETED(target) || !living_pawn.is_holding(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), living_pawn, FALSE) + + return AI_BEHAVIOR_DELAY | (is_content(living_pawn, target) ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/datum/bt_node/ai_behavior/consume/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + if(!succeeded) + return + controller.set_blackboard_key(hunger_timer_key, world.time + rand(12 SECONDS, 60 SECONDS)) + + var/mob/living/living_pawn = controller.pawn + var/obj/item/target = controller.blackboard[target_key] + if(!QDELETED(target) && !DOING_INTERACTION_WITH_TARGET(living_pawn, target)) + controller.clear_blackboard_key(target_key) + living_pawn.dropItemToGround(target) // drops empty drink glasses + for(var/obj/item/trash/trash in living_pawn.held_items) + living_pawn.dropItemToGround(trash) // drops spawned trash items + +/// Check if the target is fully consumed, or being actively consumed, or if we're just bored of eating it +/datum/bt_node/ai_behavior/consume/proc/is_content(mob/living/living_pawm, obj/item/target) + if(QDELETED(target)) + return TRUE + if(DOING_INTERACTION_WITH_TARGET(living_pawm, target)) + return TRUE + if(target.reagents?.total_volume <= 0) + return TRUE + // Even if we don't finish it all we can randomly decide to be done + return prob(10) + diff --git a/code/datums/ai/generic_behaviors/copy_bb_key.dm b/code/datums/ai/generic_behaviors/copy_bb_key.dm new file mode 100644 index 000000000000..19cd6e4bd471 --- /dev/null +++ b/code/datums/ai/generic_behaviors/copy_bb_key.dm @@ -0,0 +1,8 @@ +/// Copies the value of source_key to dest_key. +/datum/bt_node/ai_behavior/copy_bb_key + var/dest_key + var/source_key + +/datum/bt_node/ai_behavior/copy_bb_key/perform(seconds_per_tick, datum/ai_controller/controller) + controller.set_blackboard_key(dest_key, controller.blackboard[source_key]) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/drag_target.dm b/code/datums/ai/generic_behaviors/drag_target.dm new file mode 100644 index 000000000000..2ef882e2c87e --- /dev/null +++ b/code/datums/ai/generic_behaviors/drag_target.dm @@ -0,0 +1,21 @@ +/** + * BT-native drag behavior. Moves to the target and starts pulling it. + * If already pulling the target, returns SUCCESS immediately (idempotent). + * Does NOT clear the target key on finish callers must clear it when done. + * Use move_to_target after this to drag the pulled mob/item to a destination. + */ +/datum/bt_node/ai_behavior/drag_target + /// Blackboard key holding the atom to drag. + var/target_key + +/datum/bt_node/ai_behavior/drag_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/our_mob = controller.pawn + var/atom/movable/target = controller.blackboard[target_key] + if(QDELETED(target) || target.anchored || target.pulledby) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(our_mob.pulling == target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + if(!our_mob.Adjacent(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(our_mob, TYPE_PROC_REF(/atom/movable, start_pulling), target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/drop_all_held_items.dm b/code/datums/ai/generic_behaviors/drop_all_held_items.dm new file mode 100644 index 000000000000..5c4653062aa2 --- /dev/null +++ b/code/datums/ai/generic_behaviors/drop_all_held_items.dm @@ -0,0 +1,9 @@ +/// Drops everything the pawn is currently holding in its hands onto its current turf. +/datum/bt_node/ai_behavior/drop_all_held_items + +/datum/bt_node/ai_behavior/drop_all_held_items/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!living_pawn.get_num_held_items()) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + living_pawn.drop_all_held_items() + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/express_happiness.dm b/code/datums/ai/generic_behaviors/express_happiness.dm new file mode 100644 index 000000000000..eaf181ca6965 --- /dev/null +++ b/code/datums/ai/generic_behaviors/express_happiness.dm @@ -0,0 +1,53 @@ +/// Occasionally emotes based on the pawn's current happiness level. +/datum/bt_node/ai_behavior/express_happiness + /// Probability (%) of emoting per per second + var/emote_probability = 5 + /// Happiness >= this threshold -> happy emotions + var/high_happiness_threshold = 0.7 + /// Happiness >= this threshold -> moderate emotions + var/moderate_happiness_threshold = 0.5 + /// Blackboard key holding the happiness value + var/happiness_key = BB_BASIC_HAPPINESS + /// Blackboard key holding a custom happy emotions list + var/happy_key = BB_HAPPY_EMOTIONS + /// Blackboard key holding a custom moderate emotions list + var/moderate_key = BB_MODERATE_EMOTIONS + /// Blackboard key holding a custom sad emotions list + var/sad_key = BB_SAD_EMOTIONS + + var/static/list/default_happy_emotions = list( + "celebrates happily!", + "dances around in excitement!", + ) + var/static/list/default_moderate_emotions = list( + "looks satisfied.", + "trots around.", + ) + var/static/list/default_depressed_emotions = list( + "looks depressed...", + "turns its back and sulks...", + "looks towards the floor in dissapointment...", + ) + +/datum/bt_node/ai_behavior/express_happiness/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(emote_probability, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + var/happiness = controller.blackboard[happiness_key] + if(isnull(happiness)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + var/list/emotion_list + if(happiness >= high_happiness_threshold) + emotion_list = controller.blackboard[happy_key] || default_happy_emotions + else if(happiness >= moderate_happiness_threshold) + emotion_list = controller.blackboard[moderate_key] || default_moderate_emotions + else + emotion_list = controller.blackboard[sad_key] || default_depressed_emotions + + if(!length(emotion_list)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + var/mob/living/living_pawn = controller.pawn + living_pawn.manual_emote(pick(emotion_list)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/face_target_or_face_initial.dm b/code/datums/ai/generic_behaviors/face_target_or_face_initial.dm new file mode 100644 index 000000000000..14f216d42025 --- /dev/null +++ b/code/datums/ai/generic_behaviors/face_target_or_face_initial.dm @@ -0,0 +1,24 @@ +/// Faces the target each tick while it's within range. When the target is absent or out of range, resets to BB_STARTING_DIRECTION. +/// Captures BB_STARTING_DIRECTION once on setup from the pawn's current facing direction. +/datum/bt_node/ai_behavior/face_target_or_face_initial + var/target_key = BB_CURRENT_TARGET + +/datum/bt_node/ai_behavior/face_target_or_face_initial/setup(datum/ai_controller/controller) + . = ..() + var/mob/living/we = controller.pawn + if(!istype(we)) + return FALSE + var/atom/movable/target = controller.blackboard[target_key] + if(!ismovable(target) || !isturf(target.loc)) + return FALSE + controller.set_blackboard_key(BB_STARTING_DIRECTION, we.dir) + return TRUE + +/datum/bt_node/ai_behavior/face_target_or_face_initial/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/we = controller.pawn + var/atom/movable/target = controller.blackboard[target_key] + if(isnull(target) || get_dist(we, target) > 8) + we.dir = controller.blackboard[BB_STARTING_DIRECTION] + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + we.face_atom(target) + return AI_BEHAVIOR_DELAY diff --git a/code/datums/ai/generic_behaviors/fail.dm b/code/datums/ai/generic_behaviors/fail.dm new file mode 100644 index 000000000000..6a0e6fe583fc --- /dev/null +++ b/code/datums/ai/generic_behaviors/fail.dm @@ -0,0 +1,4 @@ +/datum/bt_node/ai_behavior/fail + +/datum/bt_node/ai_behavior/fail/perform(seconds_per_tick, datum/ai_controller/controller) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED diff --git a/code/datums/ai/generic_behaviors/find_furthest_turf_from_target.dm b/code/datums/ai/generic_behaviors/find_furthest_turf_from_target.dm new file mode 100644 index 000000000000..015d63c8e9e7 --- /dev/null +++ b/code/datums/ai/generic_behaviors/find_furthest_turf_from_target.dm @@ -0,0 +1,32 @@ +/// Finds the open turf furthest from the keyed target and stores it, used to pick an escape destination. +/// Returns INSTANT SUCCESS if a turf is found, INSTANT FAILURE if none are available. +/datum/bt_node/ai_behavior/find_furthest_turf_from_target + /// Blackboard key holding the atom to flee from. + var/target_key + /// Blackboard key to store the chosen turf in. + var/set_key + /// How many tiles outward from the target to scan. + var/range = 2 + +/datum/bt_node/ai_behavior/find_furthest_turf_from_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/best_distance = 0 + var/turf/chosen_turf + for(var/turf/open/potential_destination in oview(range, living_target)) + if(potential_destination.is_blocked_turf()) + continue + var/new_distance = get_dist(potential_destination, living_target) + if(new_distance > best_distance) + chosen_turf = potential_destination + best_distance = new_distance + if(best_distance == range) + break // already at the furthest possible distance + + if(isnull(chosen_turf)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(set_key, chosen_turf) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/find_nearby.dm b/code/datums/ai/generic_behaviors/find_nearby.dm new file mode 100644 index 000000000000..427b7ea79bd7 --- /dev/null +++ b/code/datums/ai/generic_behaviors/find_nearby.dm @@ -0,0 +1,21 @@ +/// Picks a random visible, non-abstract atom within range 2 and stores it in a blackboard key. +/datum/bt_node/ai_behavior/find_nearby + /// Blackboard key to store the found atom in. + var/target_key + +/datum/bt_node/ai_behavior/find_nearby/perform(seconds_per_tick, datum/ai_controller/controller) + var/list/possible_targets = list() + for(var/atom/thing in view(2, controller.pawn)) + if(!thing.mouse_opacity) + continue + if(thing.IsObscured()) + continue + if(isitem(thing)) + var/obj/item/item = thing + if(item.item_flags & ABSTRACT) + continue + possible_targets += thing + if(!possible_targets.len) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + controller.set_blackboard_key(target_key, pick(possible_targets)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/find_target_facing_turf.dm b/code/datums/ai/generic_behaviors/find_target_facing_turf.dm new file mode 100644 index 000000000000..5c968ad0d91a --- /dev/null +++ b/code/datums/ai/generic_behaviors/find_target_facing_turf.dm @@ -0,0 +1,19 @@ +/// Finds the turf directly in front of the keyed target (the tile it is facing) and stores it, +/// so we can line up an action on its front. Fails if the turf is missing or blocked. +/datum/bt_node/ai_behavior/find_target_facing_turf + /// Blackboard key holding the target we want to line up against. + var/target_key + /// Blackboard key to write the found turf into. + var/set_key + +/datum/bt_node/ai_behavior/find_target_facing_turf/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/turf/facing_turf = get_step(target, target.dir) + if(isnull(facing_turf) || facing_turf.is_blocked_turf(ignore_atoms = list(controller.pawn))) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(set_key, facing_turf) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/find_unwebbed_turf.dm b/code/datums/ai/generic_behaviors/find_unwebbed_turf.dm new file mode 100644 index 000000000000..5232fe829f9c --- /dev/null +++ b/code/datums/ai/generic_behaviors/find_unwebbed_turf.dm @@ -0,0 +1,46 @@ +/// Find a nearby unwebbed turf to spin webs on and store it in a blackboard key. +/// Returns INSTANT SUCCESS if we already have a valid target, or if we find a new one. +/// Returns INSTANT FAILURE if no valid turf is nearby. +/datum/bt_node/ai_behavior/find_unwebbed_turf + /// How many tiles outward to scan for valid turfs. + var/scan_range = 3 + /// Blackboard key holding/storing the unwebbed turf. + var/target_key + +/datum/bt_node/ai_behavior/find_unwebbed_turf/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/spider = controller.pawn + var/atom/current_target = controller.blackboard[target_key] + if(current_target && !(locate(/obj/structure/spider/stickyweb) in current_target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + controller.clear_blackboard_key(target_key) + var/turf/our_turf = get_turf(spider) + if(is_valid_web_turf(our_turf, spider)) + controller.set_blackboard_key(target_key, our_turf) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + var/list/turfs_by_range = list() + for(var/i in 1 to scan_range) + turfs_by_range["[i]"] = list() + for(var/turf/turf_in_view in oview(scan_range, our_turf)) + if(!is_valid_web_turf(turf_in_view, spider)) + continue + turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view + + var/list/final_turfs + for(var/list/turf_list as anything in turfs_by_range) + if(length(turfs_by_range[turf_list])) + final_turfs = turfs_by_range[turf_list] + break + if(!length(final_turfs)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(target_key, pick(final_turfs)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/find_unwebbed_turf/proc/is_valid_web_turf(turf/target_turf, mob/living/spider) + if(locate(/obj/structure/spider/stickyweb) in target_turf) + return FALSE + if(HAS_TRAIT(target_turf, TRAIT_SPINNING_WEB_TURF)) + return FALSE + return !target_turf.is_blocked_turf(source_atom = spider) diff --git a/code/datums/ai/generic_behaviors/find_valid_teleport_location.dm b/code/datums/ai/generic_behaviors/find_valid_teleport_location.dm new file mode 100644 index 000000000000..4b0eeeb0529f --- /dev/null +++ b/code/datums/ai/generic_behaviors/find_valid_teleport_location.dm @@ -0,0 +1,28 @@ +/// Finds a random open turf near the keyed target that the target can see, used to pick an ambush teleport destination. +/// Returns SUCCESS if a turf is found, FAILURE if none are available. +/datum/bt_node/ai_behavior/find_valid_teleport_location + /// Blackboard key holding the atom to teleport next to. + var/target_key = BB_CURRENT_TARGET + /// Blackboard key to store the chosen turf in. + var/set_key + /// How many tiles outward from the target to scan. + var/range = 3 + +/datum/bt_node/ai_behavior/find_valid_teleport_location/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/list/possible_turfs = list() + for(var/turf/open/potential_turf in oview(range, target)) + if(potential_turf.is_blocked_turf()) + continue + if(!can_see(target, potential_turf, range)) + continue + possible_turfs += potential_turf + + if(!length(possible_turfs)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(set_key, pick(possible_turfs)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/give.dm b/code/datums/ai/generic_behaviors/give.dm new file mode 100644 index 000000000000..7ff038f1d3a7 --- /dev/null +++ b/code/datums/ai/generic_behaviors/give.dm @@ -0,0 +1,86 @@ +/// gives the pawn's active held item to a blackboard-keyed target. +/datum/bt_node/ai_behavior/give + /// Blackboard key holding the mob to give the held item to. + var/target_key + /// Target snapshotted in perform(), for perform_async() to read. + VAR_PRIVATE/mob/living/give_target + /// Held item snapshotted in perform(), for perform_async() to read. + VAR_PRIVATE/obj/item/give_held_item + +/datum/bt_node/ai_behavior/give/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/mob/living/pawn = controller.pawn + var/obj/item/held_item = pawn.get_active_held_item() + var/atom/target = controller.blackboard[target_key] + + if(!held_item) //if held_item is null, we pretend that action was successful + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + if(QDELETED(target) || !target.IsReachableBy(pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/living_target = target + if(!isliving(living_target)) // target should reasonably only ever be set to a living mob + stack_trace("Tried to give an item to a non-living target!") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if(!can_give_item(living_target, held_item)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + living_target.visible_message( + span_info("[pawn] starts trying to give [held_item] to [living_target]!"), + span_warning("[pawn] tries to give you [held_item]!") + ) + give_target = living_target + give_held_item = held_item + return start_async() + +/datum/bt_node/ai_behavior/give/perform_async(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + var/mob/living/living_target = give_target + var/obj/item/held_item = give_held_item + var/result_flags = AI_BEHAVIOR_FAILED + if(do_after(pawn, 1 SECONDS, living_target)) + result_flags = try_to_give_item(living_target, held_item) + finish_async(result_flags) + +/// Returns a list(has_left_pocket, has_right_pocket, has_valid_hand) if the item can be given, null otherwise. +/datum/bt_node/ai_behavior/give/proc/can_give_item(mob/living/target, obj/item/held_item) + if(QDELETED(held_item) || QDELETED(target)) + return null + + var/has_left_pocket = target.can_equip(held_item, ITEM_SLOT_LPOCKET) + var/has_right_pocket = target.can_equip(held_item, ITEM_SLOT_RPOCKET) + var/has_valid_hand + + for(var/hand_index in target.get_empty_held_indexes()) + if(target.can_put_in_hand(held_item, hand_index)) + has_valid_hand = TRUE + break + + if(!has_left_pocket && !has_right_pocket && !has_valid_hand) + return null + + return list(has_left_pocket, has_right_pocket, has_valid_hand) + +/datum/bt_node/ai_behavior/give/proc/try_to_give_item(mob/living/target, obj/item/held_item) + var/list/give_slots = can_give_item(target, held_item) + if(!give_slots) + return AI_BEHAVIOR_FAILED + + var/has_left_pocket = give_slots[1] + var/has_valid_hand = give_slots[3] + + if(!has_valid_hand || prob(50)) + target.equip_to_slot_if_possible(held_item, (!has_left_pocket ? ITEM_SLOT_RPOCKET : (prob(50) ? ITEM_SLOT_LPOCKET : ITEM_SLOT_RPOCKET))) + else + INVOKE_ASYNC(target, TYPE_PROC_REF(/mob, put_in_hands), held_item) + return AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/give/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + give_target = null + give_held_item = null + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/generic_behaviors/grab_target.dm b/code/datums/ai/generic_behaviors/grab_target.dm new file mode 100644 index 000000000000..8698199de1dd --- /dev/null +++ b/code/datums/ai/generic_behaviors/grab_target.dm @@ -0,0 +1,33 @@ +/** + * Grabs (starts pulling) the atom at the given blackboard key. + * Succeeds immediately if already pulling the target. + * Fails if the target is anchored + */ +/datum/bt_node/ai_behavior/grab_target + /// Blackboard key holding the atom to grab. + var/target_key + +/datum/bt_node/ai_behavior/grab_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/atom/movable/target = controller.blackboard[target_key] + if(QDELETED(target) || target.anchored) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] grab_target: can't grab [target] (deleted=[QDELETED(target)], anchored=[target?.anchored])") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/our_mob = controller.pawn + if(our_mob.pulling == target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return start_async() + +/datum/bt_node/ai_behavior/grab_target/perform_async(datum/ai_controller/controller) + var/mob/living/our_mob = controller.pawn + var/atom/movable/target = controller.blackboard[target_key] + var/result = our_mob.start_pulling(target) + if(!async_still_valid()) + return + if(result) + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[our_mob] grabbing [target]", get_turf(target), "Grab") + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) diff --git a/code/datums/ai/generic_behaviors/heal_eye_damage.dm b/code/datums/ai/generic_behaviors/heal_eye_damage.dm new file mode 100644 index 000000000000..61f61c75bf5a --- /dev/null +++ b/code/datums/ai/generic_behaviors/heal_eye_damage.dm @@ -0,0 +1,22 @@ +/// Heals the eye damage of the keyed target. Movement to the target is handled externally. +/datum/bt_node/ai_behavior/heal_eye_damage + /// Blackboard key holding the target whose eyes we heal. + var/target_key + +/datum/bt_node/ai_behavior/heal_eye_damage/setup(datum/ai_controller/controller) + var/mob/living/carbon/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/heal_eye_damage/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/carbon/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/basic/eyeball/eye = controller.pawn + var/obj/item/organ/eyes/eyes = target.get_organ_slot(ORGAN_SLOT_EYES) + eye.heal_eye_damage(target, eyes) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/heal_eye_damage/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/generic_behaviors/hunt_target.dm b/code/datums/ai/generic_behaviors/hunt_target.dm new file mode 100644 index 000000000000..5eb684e79dbf --- /dev/null +++ b/code/datums/ai/generic_behaviors/hunt_target.dm @@ -0,0 +1,184 @@ +/// Performs an action on a blackboard-keyed hunt target once in range. +/// Movement to the target is handled externally via a move_to_target leaf. +/datum/bt_node/ai_behavior/hunt_target + /// Blackboard key holding the atom to hunt + var/target_key + /// Blackboard key to record the cooldown timestamp on success; if null, no cooldown is set + var/cooldown_key + /// Duration of the post-hunt cooldown + var/hunt_cooldown = 5 SECONDS + /// If TRUE, clears target_key after every hunt regardless of success + var/always_reset_target = FALSE + /// Target snapshotted in perform() for async subtypes, for perform_async() to read. + VAR_PRIVATE/atom/hunt_async_target + +/datum/bt_node/ai_behavior/hunt_target/setup(datum/ai_controller/controller) + var/atom/hunted = controller.blackboard[target_key] + return !QDELETED(hunted) + +/datum/bt_node/ai_behavior/hunt_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/hunted = controller.blackboard[target_key] + if(QDELETED(hunted)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + target_caught(controller.pawn, hunted) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/hunt_target/proc/target_caught(mob/living/hunter, atom/hunted) + if(isliving(hunted)) + var/mob/living/living_target = hunted + hunter.manual_emote("chomps [living_target]!") + living_target.investigate_log("has been killed by [key_name(hunter)].", INVESTIGATE_DEATHS) + living_target.death() + else if(IS_EDIBLE(hunted)) + hunted.attack_animal(hunter) + else + hunter.manual_emote("chomps [hunted]!") + qdel(hunted) + +/datum/bt_node/ai_behavior/hunt_target/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + hunt_async_target = null + if(succeeded && cooldown_key) + controller.set_blackboard_key(cooldown_key, world.time + hunt_cooldown) + else if(target_key) + controller.clear_blackboard_key(target_key) + if(always_reset_target && target_key) + controller.clear_blackboard_key(target_key) + +/// Uses ai_interact() on the target instead of default kill/eat/qdel. +/// behavior_combat_mode, always_reset_target, and hunt_cooldown are all configurable +/datum/bt_node/ai_behavior/hunt_target/interact_with_target + /// Combat mode to use when interacting with the target + var/behavior_combat_mode = TRUE + +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/atom/hunted = controller.blackboard[target_key] + if(QDELETED(hunted)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + hunt_async_target = hunted + return start_async() + +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/perform_async(datum/ai_controller/controller) + target_caught(controller.pawn, hunt_async_target) + if(!async_still_valid()) + return + finish_async(AI_BEHAVIOR_SUCCEEDED) + +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/target_caught(mob/living/hunter, atom/hunted) + hunter.ai_controller.ai_interact(target = hunted, combat_mode = behavior_combat_mode) + +/// Uses a cooldown ability from ability_key on the target. +/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target + always_reset_target = TRUE + /// Blackboard key holding the /datum/action/cooldown ability to use + var/ability_key + /// Ability snapshotted in perform(), for perform_async() to read. + VAR_PRIVATE/datum/action/cooldown/hunt_async_ability + +/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/datum/action/cooldown/ability = controller.blackboard[ability_key] + if(!ability?.IsAvailable()) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/atom/hunted = controller.blackboard[target_key] + if(QDELETED(hunted)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + hunt_async_target = hunted + hunt_async_ability = ability + return start_async() + +/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target/perform_async(datum/ai_controller/controller) + target_caught(controller.pawn, hunt_async_target) + if(!async_still_valid()) + return + finish_async(AI_BEHAVIOR_SUCCEEDED) + +/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + hunt_async_ability = null + +/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target/target_caught(mob/living/hunter, atom/hunted) + hunt_async_ability.InterceptClickOn(hunter, null, hunted) + +/// Celebrates around the target with a spin animation. +/datum/bt_node/ai_behavior/hunt_target/snail_people + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/snail_people/target_caught(mob/living/hunter, atom/hunted) + hunter.manual_emote("Celebrates around [hunted]!") + hunter.SpinAnimation(speed = 1, loops = 3) + +/// Starts pulling the target item toward the hunter. +/datum/bt_node/ai_behavior/hunt_target/pull_target + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/pull_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/obj/item/hunted = controller.blackboard[target_key] + if(QDELETED(hunted)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + hunt_async_target = hunted + return start_async() + +/datum/bt_node/ai_behavior/hunt_target/pull_target/perform_async(datum/ai_controller/controller) + target_caught(controller.pawn, hunt_async_target) + if(!async_still_valid()) + return + finish_async(AI_BEHAVIOR_SUCCEEDED) + +/datum/bt_node/ai_behavior/hunt_target/pull_target/target_caught(mob/living/hunter, obj/item/hunted) + hunter.start_pulling(hunted) + +/// Emotes enjoyment of the target's scent. +/datum/bt_node/ai_behavior/hunt_target/sniff_flora + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/sniff_flora/target_caught(mob/living/hunter, atom/hunted) + hunter.manual_emote("Enjoys the sweet scent eminating from [hunted::name]!") + +/// Playfully headbutts the target's legs. +/datum/bt_node/ai_behavior/hunt_target/headbutt_leg + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/headbutt_leg/target_caught(mob/living/hunter, atom/hunted) + hunter.manual_emote("playfully headbutts [hunted]'s legs!") + +/// Attempts to buckle (latch onto) the target mob. +/datum/bt_node/ai_behavior/hunt_target/latch_onto + +/datum/bt_node/ai_behavior/hunt_target/latch_onto/setup(datum/ai_controller/controller) + if(!..()) + return FALSE + var/mob/living/living_pawn = controller.pawn + return !living_pawn.buckled + +/datum/bt_node/ai_behavior/hunt_target/latch_onto/target_caught(mob/living/hunter, obj/hunted) + if(hunter.buckled) + return FALSE + if(!hunted.buckle_mob(hunter, force = TRUE)) + return FALSE + hunted.visible_message(span_notice("[hunted] has been latched onto by [hunter]!")) + return TRUE + + +/datum/bt_node/ai_behavior/hunt_target/play_with_owner + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/play_with_owner/target_caught(mob/living/hunter, atom/hunted) + var/list/interactions_list = hunter.ai_controller.blackboard[BB_INTERACTIONS_WITH_OWNER] + var/interaction_message = length(interactions_list) ? pick(interactions_list) : "Plays with" + hunter.manual_emote("[interaction_message] [hunted]!") diff --git a/code/datums/ai/generic_behaviors/issue_pet_command.dm b/code/datums/ai/generic_behaviors/issue_pet_command.dm new file mode 100644 index 000000000000..92aaea16e221 --- /dev/null +++ b/code/datums/ai/generic_behaviors/issue_pet_command.dm @@ -0,0 +1,41 @@ +/** + * Issues a spoken pet command and points at the target. + * Requires a blackboard key holding a list of /datum/pet_command instances. + * Use a cooldown decorator in the tree time_between_perform has no effect on one-shot behaviors. + */ +/datum/bt_node/ai_behavior/issue_pet_command + /// Blackboard key holding a list of /datum/pet_command instances to search. + var/command_list_key + /// Typepath of the /datum/pet_command to locate in the list. + var/command_type + /// Blackboard key holding the target atom to point at. + var/target_key + /// If set, setup() requires at least one mob of this type within command_distance. + var/commandable_mob_type + /// Range to search for commandable mobs. + var/command_distance = 5 + +/datum/bt_node/ai_behavior/issue_pet_command/setup(datum/ai_controller/controller) + . = ..() + if(!.) + return FALSE + if(commandable_mob_type) + if(!locate(commandable_mob_type) in oview(command_distance, controller.pawn)) + return FALSE + var/atom/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/issue_pet_command/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/list/commands = controller.blackboard[command_list_key] + if(!length(commands)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/datum/pet_command/cmd = locate(command_type) in commands + if(isnull(cmd)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), pick(cmd.speech_commands), forced = "controller") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, _pointed), target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/keep_playing_instrument.dm b/code/datums/ai/generic_behaviors/keep_playing_instrument.dm new file mode 100644 index 000000000000..e0d9c28f27a6 --- /dev/null +++ b/code/datums/ai/generic_behaviors/keep_playing_instrument.dm @@ -0,0 +1,19 @@ +/// Checks that a song instrument's song is still properly configured and playing. +/// Returns SUCCEEDED while the song is playing correctly, FAILED when it needs setup or restart. +/datum/bt_node/ai_behavior/keep_playing_instrument + time_between_perform = 1 SECONDS + /// Blackboard key holding the instrument being played. + var/song_instrument_key + +/datum/bt_node/ai_behavior/keep_playing_instrument/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/instrument/song_player = controller.blackboard[song_instrument_key] + if(QDELETED(song_player)) + controller.clear_blackboard_key(song_instrument_key) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/list/parsed_song_lines = splittext(controller.blackboard[BB_SONG_LINES], "\n") + popleft(parsed_song_lines) // remove BPM as it is parsed out + if(!compare_list(song_player.song.lines, parsed_song_lines) || !song_player.song.repeat) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!song_player.song.playing) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/maintain_distance.dm b/code/datums/ai/generic_behaviors/maintain_distance.dm new file mode 100644 index 000000000000..0f48cc359cb4 --- /dev/null +++ b/code/datums/ai/generic_behaviors/maintain_distance.dm @@ -0,0 +1,94 @@ +/// Moves away from a target if too close, or toward it if too far, to stay within the blackboard-keyed distance band. +/datum/bt_node/ai_behavior/maintain_distance + /// Set by on_movement_failed() when the movement system gives up pathing. + var/movement_failed = FALSE + /// Blackboard key holding the atom to keep distance from. + var/target_key + /// Blackboard key holding the minimum desired distance. + var/min_dist_key = BB_RANGED_SKIRMISH_MIN_DISTANCE + /// Blackboard key holding the maximum desired distance. + var/max_dist_key = BB_RANGED_SKIRMISH_MAX_DISTANCE + /// Movement type to use while approaching a target that's too far. Null falls back to the controller's default movement type. Reset on finish. + var/approach_movement_type = null + +/datum/bt_node/ai_behavior/maintain_distance/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(!isliving(target) || !can_see(controller.pawn, target, 10)) + return FALSE + RegisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED, PROC_REF(on_movement_failed)) + return TRUE + +/datum/bt_node/ai_behavior/maintain_distance/proc/on_movement_failed(atom/source) + SIGNAL_HANDLER + movement_failed = TRUE + +/datum/bt_node/ai_behavior/maintain_distance/perform(seconds_per_tick, datum/ai_controller/controller) + if(movement_failed) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_FAILED + + + var/minimum_distance = controller.blackboard[min_dist_key] || 4 + var/maximum_distance = controller.blackboard[max_dist_key] || 6 + var/range = get_dist(controller.pawn, target) + + if(range >= minimum_distance && range <= maximum_distance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + if(range < minimum_distance) + if(!retreat(controller, target, minimum_distance)) + return AI_BEHAVIOR_FAILED + else + controller.change_ai_movement_type(approach_movement_type || initial(controller.ai_movement)) + controller.ai_movement.start_moving_towards(controller, target, maximum_distance) + + return AI_BEHAVIOR_INSTANT + +/datum/bt_node/ai_behavior/maintain_distance/finish_action(datum/ai_controller/controller, succeeded) + UnregisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED) + movement_failed = FALSE + controller.ai_movement.stop_moving_towards(controller) + controller.change_ai_movement_type(initial(controller.ai_movement)) + return ..() + +/// Steps one tile away from target using backstep avoidance, falling back to shuffled directions if blocked. +/datum/bt_node/ai_behavior/maintain_distance/proc/retreat(datum/ai_controller/controller, atom/target, minimum_distance) + controller.change_ai_movement_type(/datum/ai_movement/basic_avoidance/backstep) + var/mob/pawn = controller.pawn + pawn.face_atom(target) + var/turf/next_step = get_step_away(pawn, target) + if(!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) + controller.ai_movement.start_moving_towards(controller, next_step, 0, controller.movement_delay * 2) + return TRUE + var/list/all_dirs = GLOB.alldirs.Copy() + all_dirs -= get_dir(pawn, next_step) + all_dirs -= get_dir(pawn, target) + shuffle_inplace(all_dirs) + for(var/dir in all_dirs) + next_step = get_step(pawn, dir) + if(!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) + controller.ai_movement.start_moving_towards(controller, next_step, 0, controller.movement_delay * 2) + return TRUE + return FALSE + +/// Retreats to the furthest available open turf within reach rather than a single step. +/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance + +/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance/retreat(datum/ai_controller/controller, atom/target, minimum_distance) + var/atom/movable/pawn = controller.pawn + var/required_distance = minimum_distance - get_dist(pawn, target) + var/best_distance = 0 + var/turf/chosen_turf + for(var/turf/open/potential_turf in oview(required_distance, pawn)) + if(potential_turf.is_blocked_turf()) + continue + var/new_distance = get_dist(potential_turf, target) + if(new_distance > best_distance) + chosen_turf = potential_turf + best_distance = new_distance + if(isnull(chosen_turf)) + return FALSE + controller.ai_movement.start_moving_towards(controller, chosen_turf, 0) + return TRUE diff --git a/code/datums/ai/generic_behaviors/mine_walls.dm b/code/datums/ai/generic_behaviors/mine_walls.dm new file mode 100644 index 000000000000..aa6f95061b2d --- /dev/null +++ b/code/datums/ai/generic_behaviors/mine_walls.dm @@ -0,0 +1,53 @@ +/// Searches for a nearby mineral wall the pawn can mine and sets the target key. +/datum/bt_node/ai_behavior/find_mineral_wall + time_between_perform = 2 SECONDS + /// Blackboard key to store the found mineral wall in. + var/target_key + +/datum/bt_node/ai_behavior/find_mineral_wall/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living_pawn = controller.pawn + for(var/turf/closed/mineral/potential_wall in oview(9, living_pawn)) + if(!check_if_mineable(controller, potential_wall)) + continue + controller.set_blackboard_key(target_key, potential_wall) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/// Returns TRUE if the given wall can be approached and mined. +/datum/bt_node/ai_behavior/find_mineral_wall/proc/check_if_mineable(datum/ai_controller/controller, turf/target_wall) + var/mob/living/source = controller.pawn + var/direction_to_turf = get_dir(target_wall, source) + if(!ISDIAGONALDIR(direction_to_turf)) + return TRUE + for(var/direction_check in GLOB.cardinals) + if(!(direction_check & direction_to_turf)) + continue + var/turf/test_turf = get_step(target_wall, direction_check) + if(isnull(test_turf)) + continue + if(!test_turf.is_blocked_turf(ignore_atoms = list(source))) + return TRUE + return FALSE + +/// Mines the mineral wall at target_key when adjacent. Clears the target key on finish. +/datum/bt_node/ai_behavior/mine_wall + /// Blackboard key holding the mineral wall to mine. + var/target_key + +/datum/bt_node/ai_behavior/mine_wall/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/living_pawn = controller.pawn + var/turf/closed/mineral/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!living_pawn.Adjacent(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!controller.ai_can_interact(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target) + if(istype(target, /turf/closed/mineral/gibtonite)) + living_pawn.manual_emote("sighs...") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/mine_wall/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/generic_behaviors/move_to_target.dm b/code/datums/ai/generic_behaviors/move_to_target.dm new file mode 100644 index 000000000000..076012a59172 --- /dev/null +++ b/code/datums/ai/generic_behaviors/move_to_target.dm @@ -0,0 +1,55 @@ +/// Moves toward a blackboard-keyed target each tick. Succeeds when within required_dist by default. +/// Pass finish_on_arrival = FALSE to keep running indefinitely (used in combat parallels). +/// Pass movement_type to temporarily override the controller's ai_movement; resets to the initial type on finish. +/datum/bt_node/ai_behavior/move_to_target + /// Set by on_movement_failed() when the movement system gives up pathing. + VAR_FINAL/movement_failed = FALSE + /// Blackboard key holding the atom to move toward. + var/target_key + /// Distance at which arrival is considered reached. + var/required_dist = 1 + /// Whether to succeed once within required_dist; FALSE keeps running indefinitely. + var/finish_on_arrival = TRUE + /// Optional ai_movement type override; resets to the initial type on finish. + var/movement_type = null + /// Tracks the last atom we started moving toward so we can retarget when the key changes. + VAR_PRIVATE/atom/tracked_target + +/datum/bt_node/ai_behavior/move_to_target/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + if(movement_type) + controller.change_ai_movement_type(movement_type) + RegisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED, PROC_REF(on_movement_failed)) + controller.ai_movement.start_moving_towards(controller, target, required_dist) + tracked_target = target + return TRUE + +/datum/bt_node/ai_behavior/move_to_target/proc/on_movement_failed(atom/source) + SIGNAL_HANDLER + movement_failed = TRUE + +/datum/bt_node/ai_behavior/move_to_target/perform(seconds_per_tick, datum/ai_controller/controller) + if(movement_failed) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_FAILED + if(target != tracked_target) + controller.ai_movement.start_moving_towards(controller, target, required_dist) + tracked_target = target + else if(!controller.ai_movement.moving_controllers[controller]) + controller.ai_movement.start_moving_towards(controller, target, required_dist) + if(finish_on_arrival && get_dist(controller.pawn, target) <= required_dist) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT + +/datum/bt_node/ai_behavior/move_to_target/finish_action(datum/ai_controller/controller, succeeded) + UnregisterSignal(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED) + movement_failed = FALSE + tracked_target = null + controller.ai_movement.stop_moving_towards(controller) + if(movement_type) + controller.change_ai_movement_type(initial(controller.ai_movement)) + return ..() diff --git a/code/datums/ai/generic_behaviors/perform_emote.dm b/code/datums/ai/generic_behaviors/perform_emote.dm new file mode 100644 index 000000000000..c0f49f05066f --- /dev/null +++ b/code/datums/ai/generic_behaviors/perform_emote.dm @@ -0,0 +1,11 @@ +/// Performs a named emote on the pawn (BT-native). Use for standard emote types like "flip", "wave", etc. +/datum/bt_node/ai_behavior/perform_emote + /// Name of the emote to perform. + var/emote + +/datum/bt_node/ai_behavior/perform_emote/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!istype(living_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), emote) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/pick_random_ability.dm b/code/datums/ai/generic_behaviors/pick_random_ability.dm new file mode 100644 index 000000000000..3f81dbfe096d --- /dev/null +++ b/code/datums/ai/generic_behaviors/pick_random_ability.dm @@ -0,0 +1,27 @@ +/// Picks a random available ability from a pool of blackboard keys, excluding the last-used one. +/// Writes the selected action object to result_key for a targeted_mob_ability leaf to fire. +/// Returns INSTANT FAILURE if no ability in the pool is currently available. +/datum/bt_node/ai_behavior/pick_random_ability + /// List of blackboard key name strings to pick from. + var/list/ability_keys = null + /// Blackboard key storing the last-picked key name string (anti-repeat). Can be null. + var/last_used_key = null + /// Blackboard key to write the selected action object into. + var/result_key = BB_GENERIC_ACTION + +/datum/bt_node/ai_behavior/pick_random_ability/perform(seconds_per_tick, datum/ai_controller/controller) + var/list/possible = ability_keys.Copy() + var/last_used = last_used_key ? controller.blackboard[last_used_key] : null + if(last_used) + possible -= last_used + for(var/bb_key in possible) + var/datum/action/ability = controller.blackboard[bb_key] + if(QDELETED(ability) || !ability.IsAvailable()) + possible -= bb_key + if(!length(possible)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/picked_key = pick(possible) + if(last_used_key) + controller.set_blackboard_key(last_used_key, picked_key) + controller.set_blackboard_key(result_key, controller.blackboard[picked_key]) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/pick_up.dm b/code/datums/ai/generic_behaviors/pick_up.dm new file mode 100644 index 000000000000..fba4ffbacd13 --- /dev/null +++ b/code/datums/ai/generic_behaviors/pick_up.dm @@ -0,0 +1,28 @@ +/// Picks up a blackboard-keyed item. Pair with move_to_target in the BT tree for navigation. +/datum/bt_node/ai_behavior/pick_up + time_between_perform = 2 SECONDS + /// Blackboard key holding the item to pick up. + var/target_key + /// Whether to drop a currently-held item to free a hand. + var/drop_held = TRUE + +/datum/bt_node/ai_behavior/pick_up/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/pick_up/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/obj/item/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if(living_pawn.is_holding(target)) // already in hands + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + if(!target.IsReachableBy(living_pawn)) // cant reach! + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(living_pawn.get_active_held_item()) // something is in our hands already + if(!drop_held || !living_pawn.dropItemToGround(living_pawn.get_active_held_item())) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, FALSE) + return AI_BEHAVIOR_DELAY | (target.loc == living_pawn ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) diff --git a/code/datums/ai/generic_behaviors/play_instrument.dm b/code/datums/ai/generic_behaviors/play_instrument.dm new file mode 100644 index 000000000000..a2ba4a08fde5 --- /dev/null +++ b/code/datums/ai/generic_behaviors/play_instrument.dm @@ -0,0 +1,13 @@ +/// Starts playing the song loaded into a blackboard-keyed instrument. +/datum/bt_node/ai_behavior/play_instrument + var/volume = 50 + /// Blackboard key holding the instrument to play. + var/song_instrument_key + +/datum/bt_node/ai_behavior/play_instrument/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key] + var/datum/song/song = song_instrument.song + song.volume = volume + + song.start_playing(controller.pawn) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/random_walk.dm b/code/datums/ai/generic_behaviors/random_walk.dm new file mode 100644 index 000000000000..4cbda837ba26 --- /dev/null +++ b/code/datums/ai/generic_behaviors/random_walk.dm @@ -0,0 +1,111 @@ +// BT-native idle wander leaves. +/datum/bt_node/ai_behavior/idle_random_walk + /// Chance that the mob random walks per second. + var/walk_chance = 25 + +/datum/bt_node/ai_behavior/idle_random_walk/proc/can_move(mob/living/living_pawn) + return living_pawn && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby + +/datum/bt_node/ai_behavior/idle_random_walk/proc/try_random_step(mob/living/living_pawn, seconds_per_tick, step_walk_chance) + if(LAZYLEN(living_pawn.do_afters)) + return FALSE + + if(!SPT_PROB(step_walk_chance, seconds_per_tick) || !can_move(living_pawn)) + return FALSE + + var/move_dir = pick(GLOB.alldirs) + var/turf/destination_turf = get_step(living_pawn, move_dir) + if(!destination_turf?.can_cross_safely(living_pawn)) + return FALSE + + living_pawn.Move(destination_turf, move_dir) + return TRUE + +/datum/bt_node/ai_behavior/idle_random_walk/proc/try_near_target_step(mob/living/living_pawn, atom/target, seconds_per_tick, step_walk_chance, minimum_distance) + if(isnull(target) || !can_move(living_pawn) || LAZYLEN(living_pawn.do_afters)) + return FALSE + + if(!SPT_PROB(step_walk_chance, seconds_per_tick)) + return FALSE + + var/distance = get_dist(target, living_pawn) + if(distance > minimum_distance) + return try_random_step(living_pawn, seconds_per_tick, step_walk_chance) + + var/list/possible_turfs = list() + for(var/direction in GLOB.alldirs) + var/turf/possible_step = get_step(living_pawn, direction) + if(get_dist(possible_step, target) > minimum_distance) + continue + if(possible_step.is_blocked_turf() || !possible_step.can_cross_safely(living_pawn)) + continue + possible_turfs += possible_step + + if(!length(possible_turfs)) + return FALSE + + var/turf/picked_turf = pick(possible_turfs) + living_pawn.Move(picked_turf, get_dir(living_pawn, picked_turf)) + return TRUE + +/datum/bt_node/ai_behavior/idle_random_walk/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + try_random_step(living_pawn, seconds_per_tick, walk_chance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +// Only walk if we don't have a target. +/datum/bt_node/ai_behavior/idle_random_walk/no_target + /// Where do we look for a target? + var/target_key = BB_CURRENT_TARGET + +/datum/bt_node/ai_behavior/idle_random_walk/no_target/perform(seconds_per_tick, datum/ai_controller/controller) + if(controller.blackboard_key_exists(target_key)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return ..() + +// Only walk if we are not on the target's location. +/datum/bt_node/ai_behavior/idle_random_walk/not_while_on_target + /// What is the spot we have to stand on? + var/target_key + +/datum/bt_node/ai_behavior/idle_random_walk/not_while_on_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(!QDELETED(target) && ((isturf(target) && controller.pawn.loc == target) || (target.loc == controller.pawn.loc))) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return ..() + +// Walk more when healthy and on rust (explore for more), or damaged and off rust (seek healing rust). +/datum/bt_node/ai_behavior/idle_random_walk/rust + +/datum/bt_node/ai_behavior/idle_random_walk/rust/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/turf/my_turf = get_turf(living_pawn) + var/on_rust = HAS_TRAIT(my_turf, TRAIT_RUSTY) + var/damaged = living_pawn.health < living_pawn.maxHealth + try_random_step(living_pawn, seconds_per_tick, (on_rust == damaged) ? 10 : 50) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +// Walk randomly, but stay near a target when possible. +/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target + /// Distance we are allowed to stay from the target. + var/minimum_distance = 20 + /// Key that holds target. + var/target_key + +/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + target = null + + if(!can_move(living_pawn) || LAZYLEN(living_pawn.do_afters) || !SPT_PROB(walk_chance, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + if(isnull(target) || get_dist(target, living_pawn) > minimum_distance) + try_random_step(living_pawn, seconds_per_tick, walk_chance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + if(!try_near_target_step(living_pawn, target, seconds_per_tick, walk_chance, minimum_distance)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/resist.dm b/code/datums/ai/generic_behaviors/resist.dm new file mode 100644 index 000000000000..aa5a43111984 --- /dev/null +++ b/code/datums/ai/generic_behaviors/resist.dm @@ -0,0 +1,14 @@ +/datum/bt_node/ai_behavior/resist/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + return start_async() + +/datum/bt_node/ai_behavior/resist/perform_async(datum/ai_controller/controller) + + if(!async_still_valid()) + return + var/mob/living/living_pawn = controller.pawn + living_pawn.execute_resist() + finish_async(AI_BEHAVIOR_SUCCEEDED) diff --git a/code/datums/ai/generic_behaviors/run_emote.dm b/code/datums/ai/generic_behaviors/run_emote.dm new file mode 100644 index 000000000000..985cb18a3b0d --- /dev/null +++ b/code/datums/ai/generic_behaviors/run_emote.dm @@ -0,0 +1,21 @@ +/datum/bt_node/ai_behavior/run_emote + /// Blackboard key holding the emote (or list of emotes) to perform. + var/emote_key = BB_EMOTE_KEY + +/datum/bt_node/ai_behavior/run_emote/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!isliving(living_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/list/emote_list = controller.blackboard[emote_key] + var/emote + if(islist(emote_list)) + emote = length(emote_list) ? pick(emote_list) : null + else + emote = emote_list + + if(isnull(emote)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), emote) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/set_bb_cooldown.dm b/code/datums/ai/generic_behaviors/set_bb_cooldown.dm new file mode 100644 index 000000000000..36e03e266e47 --- /dev/null +++ b/code/datums/ai/generic_behaviors/set_bb_cooldown.dm @@ -0,0 +1,10 @@ +/// Sets the given blackboard key to a future timestamp, blocking key_off_cooldown until that time. +/datum/bt_node/ai_behavior/set_bb_cooldown + /// Blackboard key to write the cooldown timestamp into. + var/cooldown_key + /// Cooldown duration, in seconds. + var/cooldown_duration + +/datum/bt_node/ai_behavior/set_bb_cooldown/perform(seconds_per_tick, datum/ai_controller/controller) + controller.set_blackboard_key(cooldown_key, world.time + cooldown_duration) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/set_bb_key.dm b/code/datums/ai/generic_behaviors/set_bb_key.dm new file mode 100644 index 000000000000..0470bf116222 --- /dev/null +++ b/code/datums/ai/generic_behaviors/set_bb_key.dm @@ -0,0 +1,10 @@ +/// Sets the given blackboard key to a value. +/datum/bt_node/ai_behavior/set_bb_key + /// Blackboard key to set. + var/target_key + /// Value to store in the key. + var/value + +/datum/bt_node/ai_behavior/set_bb_key/perform(seconds_per_tick, datum/ai_controller/controller) + controller.set_blackboard_key(target_key, value) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/setup_instrument.dm b/code/datums/ai/generic_behaviors/setup_instrument.dm new file mode 100644 index 000000000000..e26bea9a5ca3 --- /dev/null +++ b/code/datums/ai/generic_behaviors/setup_instrument.dm @@ -0,0 +1,18 @@ +/// Parses and loads a song into a blackboard-keyed instrument, preparing it for playback. +/datum/bt_node/ai_behavior/setup_instrument + /// Blackboard key holding the instrument to load the song into. + var/song_instrument_key + /// Blackboard key holding the song lines to parse. + var/song_lines_key + +/datum/bt_node/ai_behavior/setup_instrument/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key] + var/datum/song/song = song_instrument.song + var/song_lines = controller.blackboard[song_lines_key] + + //just in case- it won't do anything if the instrument isn't playing + song.stop_playing() + song.ParseSong(new_song = song_lines) + song.repeat = 10 + song.volume = song.max_volume - 10 + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/shove_target.dm b/code/datums/ai/generic_behaviors/shove_target.dm new file mode 100644 index 000000000000..55bb77478af0 --- /dev/null +++ b/code/datums/ai/generic_behaviors/shove_target.dm @@ -0,0 +1,45 @@ + +//Shoves the blackboard target +/datum/bt_node/ai_behavior/shove_target + /// Blackboard key holding the mob to shove. + var/target_key + /// Percent chance to actually go for the shove. + var/shove_chance = 100 + +/datum/bt_node/ai_behavior/shove_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/mob/living/living_target = controller.blackboard[target_key] + + if(!isliving(living_pawn) || !isliving(living_target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(world.time < living_pawn.next_move) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(!prob(shove_chance)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(!living_target.IsReachableBy(living_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + // Standing up, not ourselves, not sharing a turf. + if(!living_pawn.can_disarm(living_target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + // Right click only shoves off an empty hand, and the click chain uses whichever hand is active, + // so an occupied active hand has to be swapped out of first or we'd swing our weapon instead. + if(!free_up_hand(living_pawn)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), living_target, TRUE, list(RIGHT_CLICK = TRUE)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Makes sure the active hand is empty, swapping to a free one if it isn't. FALSE when every hand is full. +/datum/bt_node/ai_behavior/shove_target/proc/free_up_hand(mob/living/living_pawn) + if(isnull(living_pawn.get_active_held_item())) + return TRUE + var/list/empty_hands = living_pawn.get_empty_held_indexes() + if(!length(empty_hands)) + return FALSE + living_pawn.swap_hand(empty_hands[1]) + return isnull(living_pawn.get_active_held_item()) diff --git a/code/datums/ai/generic_behaviors/speech.dm b/code/datums/ai/generic_behaviors/speech.dm new file mode 100644 index 000000000000..6a5acfc02d4e --- /dev/null +++ b/code/datums/ai/generic_behaviors/speech.dm @@ -0,0 +1,184 @@ +///Random speech behavior, for speech thats random +/datum/bt_node/ai_behavior/random_speech + time_between_perform = 1 SECONDS + /// Chance that the mob will speak. + var/speech_chance = 1 + /// Hearable emotes (e.g. "barks.") played with sound if sound list is populated. + var/list/emote_hear + /// Visible-only emotes (e.g. "wags tail.") no sound. + var/list/emote_see + /// Spoken lines. + var/list/speak + /// Sound files to play alongside emote_hear or speak lines. + var/list/sound + +/datum/bt_node/ai_behavior/random_speech/perform(seconds_per_tick, datum/ai_controller/controller) + if(!prob(speech_chance)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/audible = length(emote_hear) + var/visible = length(emote_see) + var/spoken = length(speak) + var/total = audible + visible + spoken + if(!total) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/pawn = controller.pawn + if(!istype(pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/roll = rand(1, total) + + if(roll <= audible) + pawn.manual_emote(pick(emote_hear)) + if(length(sound)) + playsound(pawn, pick(sound), 80, vary = TRUE, pressure_affected = TRUE, ignore_walls = FALSE) + else if(roll <= audible + visible) + pawn.manual_emote(pick(emote_see)) + else + INVOKE_ASYNC(src, PROC_REF(speak), pawn, controller) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/random_speech/proc/speak(mob/living/pawn, datum/ai_controller/controller) + pawn.say(pick(speak), forced = "AI Controller") + if(length(sound)) + playsound(pawn, pick(sound), 80, vary = TRUE) + +/datum/bt_node/ai_behavior/random_speech/mothroach + speech_chance = 15 + emote_hear = list("flutters.") + +/datum/bt_node/ai_behavior/random_speech/mouse + speech_chance = 1 + speak = list("Squeak!", "SQUEAK!", "Squeak?") + sound = list('sound/mobs/non-humanoids/mouse/mousesqueek.ogg') + emote_hear = list("squeaks.") + emote_see = list("runs in a circle.", "shakes.") + +/datum/bt_node/ai_behavior/random_speech/frog + speech_chance = 3 + emote_see = list("jumps in a circle.", "shakes.") + +/datum/bt_node/ai_behavior/random_speech/lizard + speech_chance = 3 + emote_hear = list("stamps around some.", "hisses a bit.") + emote_see = list("blehs the tongue.", "tilts the head.", "does a spin.") + +/datum/bt_node/ai_behavior/random_speech/faithless + speech_chance = 1 + emote_see = list("wails.") + +/datum/bt_node/ai_behavior/random_speech/garden_gnome + speech_chance = 5 + speak = list("Gnot a gnelf!", "Gnot a gnoblin!", "Howdy chum!") + emote_hear = list("snores.", "burps.") + emote_see = list("blinks.") + +/datum/bt_node/ai_behavior/random_speech/killer_tomato + speech_chance = 3 + emote_hear = list("gnashes.", "growls lowly.", "snarls.") + emote_see = list("salivates.") + +/datum/bt_node/ai_behavior/random_speech/ant + speech_chance = 1 + speak = list("BZZZZT!", "CHTCHTCHT!", "Bzzz", "ChtChtCht") + sound = list('sound/mobs/non-humanoids/insect/chitter.ogg') + emote_hear = list("buzzes.", "clacks.") + emote_see = list("shakes their head.", "twitches their antennae.") + +/datum/bt_node/ai_behavior/random_speech/fox + speech_chance = 1 + speak = list("Ack-Ack", "Ack-Ack-Ack-Ackawoooo", "Geckers", "Awoo", "Tchoff") + emote_hear = list("howls.", "barks.", "screams.") + emote_see = list("shakes their head.", "shivers.") + +/datum/bt_node/ai_behavior/random_speech/crab + speech_chance = 1 + sound = list('sound/mobs/non-humanoids/crab/claw_click.ogg') + emote_hear = list("clicks.") + emote_see = list("clacks.") + +/datum/bt_node/ai_behavior/random_speech/penguin + speech_chance = 5 + speak = list("Gah Gah!", "NOOT NOOT!", "NOOT!", "Noot", "noot", "Prah!", "Grah!") + emote_hear = list("squawks", "gakkers") + +/datum/bt_node/ai_behavior/random_speech/bear + speech_chance = 5 + emote_hear = list("rawrs.", "grumbles.", "grawls.", "stomps!") + emote_see = list("stares ferociously.") + +/datum/bt_node/ai_behavior/random_speech/cats + speech_chance = 10 + sound = list(SFX_CAT_MEOW) + emote_hear = list("meows.") + emote_see = list("meows.") + +/// Make spooky sounds, if we have a corpse inside then impersonate them +/datum/bt_node/ai_behavior/random_speech/legion + speech_chance = 1 + speak = list("Come...", "Legion...", "Why...?") + emote_hear = list("groans.", "wails.", "whimpers.") + emote_see = list("twitches.", "shudders.") + /// Stuff to specifically say into a radio + var/list/radio_speech = list("Come...", "Why...?") + +/datum/bt_node/ai_behavior/random_speech/legion/speak(mob/living/pawn, datum/ai_controller/controller) + var/mob/living/carbon/human/victim = controller.blackboard[BB_LEGION_CORPSE] + if (QDELETED(victim) || prob(30)) + return ..() + + if (HAS_MIND_TRAIT(victim, TRAIT_MIMING)) // mimes cant talk + return + + var/list/remembered_speech = controller.blackboard[BB_LEGION_RECENT_LINES] || list() + + if (length(remembered_speech) && prob(50)) // Don't spam the radio + pawn.say(pick(remembered_speech), forced = "AI Controller") + return + + var/obj/item/radio/mob_radio = locate() in victim + if (QDELETED(mob_radio)) + return ..() // No radio, just talk funny + mob_radio.talk_into(pawn, pick(radio_speech + remembered_speech), pick(RADIO_CHANNEL_SUPPLY, RADIO_CHANNEL_COMMON)) + +///Speech behavior that reads from a blackboard to pick what to say. Useful for things with dynamic speech behaviors +/datum/bt_node/ai_behavior/random_speech_blackboard + +/datum/bt_node/ai_behavior/random_speech_blackboard/perform(seconds_per_tick, datum/ai_controller/controller) + var/list/speech_lines = controller.blackboard[BB_BASIC_MOB_SPEAK_LINES] + if(isnull(speech_lines)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/speech_chance = speech_lines[BB_SPEAK_CHANCE] || 1 + if(!prob(speech_chance)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/list/emote_hear = speech_lines[BB_EMOTE_HEAR] || list() + var/list/emote_see = speech_lines[BB_EMOTE_SEE] || list() + var/list/speak = speech_lines[BB_EMOTE_SAY] || list() + var/list/sounds = speech_lines[BB_EMOTE_SOUND] || list() + + var/total = length(emote_hear) + length(emote_see) + length(speak) + if(!total) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/pawn = controller.pawn + if(!istype(pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/sound_to_play = length(sounds) ? pick(sounds) : null + var/roll = rand(1, total) + + if(roll <= length(emote_hear)) + pawn.manual_emote(pick(emote_hear)) + if(sound_to_play) + playsound(pawn, sound_to_play, 80, vary = TRUE, pressure_affected = TRUE, ignore_walls = FALSE) + else if(roll <= length(emote_hear) + length(emote_see)) + pawn.manual_emote(pick(emote_see)) + else + INVOKE_ASYNC(pawn, TYPE_PROC_REF(/atom/movable, say), pick(speak), forced = "AI Controller") + if(sound_to_play) + playsound(pawn, sound_to_play, 80, vary = TRUE) + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/spin_web.dm b/code/datums/ai/generic_behaviors/spin_web.dm new file mode 100644 index 000000000000..91f7b684ca8c --- /dev/null +++ b/code/datums/ai/generic_behaviors/spin_web.dm @@ -0,0 +1,35 @@ +/// Trigger a web-spinning action at the current web target turf. +/// Expects move_to_target to have positioned the spider first. +/// Clears BB_SPIDER_WEB_TARGET on finish. +/datum/bt_node/ai_behavior/spin_web + time_between_perform = 15 SECONDS + /// Blackboard key holding the web-spinning action. + var/action_key + /// Blackboard key holding the target turf to web. + var/target_key + +/datum/bt_node/ai_behavior/spin_web/setup(datum/ai_controller/controller) + if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key)) + return FALSE + return ..() + +/datum/bt_node/ai_behavior/spin_web/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/datum/action/cooldown/web_action = controller.blackboard[action_key] + if(!web_action) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return start_async() + +/datum/bt_node/ai_behavior/spin_web/perform_async(datum/ai_controller/controller) + var/datum/action/cooldown/web_action = controller.blackboard[action_key] + var/result = web_action.Trigger() + if(!async_still_valid()) + return + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/datum/bt_node/ai_behavior/spin_web/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/generic_behaviors/stop_dragging.dm b/code/datums/ai/generic_behaviors/stop_dragging.dm new file mode 100644 index 000000000000..24c28fea42d7 --- /dev/null +++ b/code/datums/ai/generic_behaviors/stop_dragging.dm @@ -0,0 +1,7 @@ +/// Stops the pawn from pulling whatever it is currently dragging. +/datum/bt_node/ai_behavior/stop_dragging + +/datum/bt_node/ai_behavior/stop_dragging/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + living_pawn.stop_pulling() + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/stuff_in_disposal.dm b/code/datums/ai/generic_behaviors/stuff_in_disposal.dm new file mode 100644 index 000000000000..07b85ad95d95 --- /dev/null +++ b/code/datums/ai/generic_behaviors/stuff_in_disposal.dm @@ -0,0 +1,37 @@ +/// Grabs a downed target mob and stuffs them into a nearby disposal unit. +/datum/bt_node/ai_behavior/stuff_in_disposal + time_between_perform = 2 SECONDS + /// Blackboard key holding the mob to stuff. + var/attack_target_key + /// Blackboard key holding the disposal unit. + var/disposal_target_key + +/datum/bt_node/ai_behavior/stuff_in_disposal/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/mob/living/target = controller.blackboard[attack_target_key] + var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key] + var/mob/living/living_pawn = controller.pawn + if(QDELETED(target) || QDELETED(disposal)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!living_pawn.Adjacent(disposal)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return start_async() + +/datum/bt_node/ai_behavior/stuff_in_disposal/perform_async(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[attack_target_key] + var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key] + var/mob/living/living_pawn = controller.pawn + var/stuffed = disposal.stuff_mob_in(target, living_pawn) + if(!async_still_valid()) + return + if(stuffed && !QDELETED(disposal)) + disposal.flush() + finish_async(AI_BEHAVIOR_SUCCEEDED) + +/datum/bt_node/ai_behavior/stuff_in_disposal/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(attack_target_key) + controller.clear_blackboard_key(disposal_target_key) diff --git a/code/datums/ai/generic_behaviors/succeed.dm b/code/datums/ai/generic_behaviors/succeed.dm new file mode 100644 index 000000000000..19d31d8d406b --- /dev/null +++ b/code/datums/ai/generic_behaviors/succeed.dm @@ -0,0 +1,4 @@ +/datum/bt_node/ai_behavior/succeed + +/datum/bt_node/ai_behavior/succeed/perform(seconds_per_tick, datum/ai_controller/controller) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/target_retaliate.dm b/code/datums/ai/generic_behaviors/target_retaliate.dm new file mode 100644 index 000000000000..dff7da1d5bab --- /dev/null +++ b/code/datums/ai/generic_behaviors/target_retaliate.dm @@ -0,0 +1,53 @@ + +///Pick a target from our retaliate list +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list + target_source = /datum/target_source/from_bb_list/retaliate_list + revalidation_mode = TARGET_REVALIDATE + time_between_perform = 2 SECONDS + vision_range = 9 + /// Blackboard key in which to store the target's hiding location. + var/hiding_location_key + /// If FALSE, temporarily ignores faction during the search. + var/check_faction = FALSE + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/perform(seconds_per_tick, datum/ai_controller/controller) + if(!check_faction) // This is lame, but comeon man the polar bears kept killing each other + controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE) + . = ..() + var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE + controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction) + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/filter_candidates(datum/ai_controller/controller, list/candidates, datum/targeting_strategy/strategy, atom/current_target) + var/mob/living/pawn = controller.pawn + var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY]) + var/current_priority = priority_strategy ? priority_strategy.get_target_priority(controller, current_target) : 0 + var/list/filtered = list() + for(var/atom/candidate as anything in candidates) + if(!strategy.is_valid_target(pawn, candidate, vision_range, controller)) + continue + if(priority_strategy && priority_strategy.get_target_priority(controller, candidate) < current_priority) + continue + filtered += candidate + return filtered + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + var/atom/hiding = strategy.find_hidden_mobs(controller.pawn, target) + if(hiding) + controller.set_blackboard_key(hiding_location_key, hiding) + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/on_no_valid_candidates(datum/ai_controller/controller, atom/current_target) + if(current_target) + controller.clear_blackboard_key(target_key) + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/datum/target_priority_strategy/priority_strategy = GET_TARGET_PRIORITY_STRATEGY(controller.blackboard[BB_TARGET_PRIORITY_STRATEGY]) + if(!priority_strategy) + return pick(filtered_targets) + return priority_strategy.select_target(controller, filtered_targets) + +/// Nearest-attacker variant +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest + +/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/turf/our_position = get_turf(controller.pawn) + return get_closest_atom(/atom/, filtered_targets, our_position) diff --git a/code/datums/ai/generic_behaviors/unlock_do_once.dm b/code/datums/ai/generic_behaviors/unlock_do_once.dm new file mode 100644 index 000000000000..2adb2776f16e --- /dev/null +++ b/code/datums/ai/generic_behaviors/unlock_do_once.dm @@ -0,0 +1,8 @@ +/// Clears a do-once being locked +/datum/bt_node/ai_behavior/unlock_do_once + /// Blackboard key configured on the corresponding do_once decorator. + var/lock_key + +/datum/bt_node/ai_behavior/unlock_do_once/perform(seconds_per_tick, datum/ai_controller/controller) + UNLOCK_DO_ONCE(controller, lock_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/use_in_hand.dm b/code/datums/ai/generic_behaviors/use_in_hand.dm new file mode 100644 index 000000000000..01fc28d5a4c3 --- /dev/null +++ b/code/datums/ai/generic_behaviors/use_in_hand.dm @@ -0,0 +1,10 @@ +/// Uses the pawn's currently active held item in-hand. +/datum/bt_node/ai_behavior/use_in_hand + +/datum/bt_node/ai_behavior/use_in_hand/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + var/obj/item/held = pawn.get_active_held_item() + if(!held) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(pawn, TYPE_PROC_REF(/mob, activate_hand)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/use_on_object.dm b/code/datums/ai/generic_behaviors/use_on_object.dm new file mode 100644 index 000000000000..5661221406c0 --- /dev/null +++ b/code/datums/ai/generic_behaviors/use_on_object.dm @@ -0,0 +1,12 @@ +/// Uses the pawn's held item (or unarmed) on a blackboard-keyed target. +/datum/bt_node/ai_behavior/use_on_object + /// Blackboard key holding the atom to use the held item on. + var/target_key + +/datum/bt_node/ai_behavior/use_on_object/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, FALSE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/virtual_pick_up_item.dm b/code/datums/ai/generic_behaviors/virtual_pick_up_item.dm new file mode 100644 index 000000000000..b22367d0b63d --- /dev/null +++ b/code/datums/ai/generic_behaviors/virtual_pick_up_item.dm @@ -0,0 +1,59 @@ +/// Moves the item at target_key onto the pawn and records it in storage_key. Does not use hands storage_key is the virtual carry slot. Clears target_key on finish. +/datum/bt_node/ai_behavior/pick_up_item_virtual + /// Blackboard key holding the item to pick up. + var/target_key + /// Blackboard key acting as the virtual carry slot. + var/storage_key + +/datum/bt_node/ai_behavior/pick_up_item_virtual/setup(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[target_key] + return isitem(target) && isturf(target.loc) && !target.anchored + +/datum/bt_node/ai_behavior/pick_up_item_virtual/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[target_key] + if(QDELETED(target) || !isturf(target.loc)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!controller.pawn.Adjacent(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + _pickup(controller, target, storage_key) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/pick_up_item_virtual/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + +/datum/bt_node/ai_behavior/pick_up_item_virtual/proc/_pickup(datum/ai_controller/controller, obj/item/target, storage_key) + var/atom/pawn = controller.pawn + var/obj/item/held = controller.blackboard[storage_key] + if(held?.loc == pawn) + pawn.visible_message(span_notice("[pawn] drops [held].")) + held.forceMove(get_turf(pawn)) + controller.clear_blackboard_key(storage_key) + pawn.visible_message(span_notice("[pawn] picks up [target].")) + target.forceMove(pawn) + controller.set_blackboard_key(storage_key, target) + +/// Passes the item at storage_key to whoever is at delivery_key (must already be adjacent). +/datum/bt_node/ai_behavior/pass_item_virtual + /// Blackboard key holding the recipient to deliver to. + var/delivery_key + /// Blackboard key holding the virtually-carried item. + var/storage_key + +/datum/bt_node/ai_behavior/pass_item_virtual/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[delivery_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/pass_item_virtual/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[delivery_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/pawn = controller.pawn + var/obj/item/item = controller.blackboard[storage_key] + if(QDELETED(item) || item.loc != pawn) + pawn.visible_message(span_notice("[pawn] looks around as if [pawn.p_they()] [pawn.p_have()] lost something.")) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + pawn.visible_message(span_notice("[pawn] delivers [item] to [target].")) + item.forceMove(get_turf(target)) + controller.clear_blackboard_key(storage_key) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/generic_behaviors/wait.dm b/code/datums/ai/generic_behaviors/wait.dm new file mode 100644 index 000000000000..b609803c89f9 --- /dev/null +++ b/code/datums/ai/generic_behaviors/wait.dm @@ -0,0 +1,19 @@ +/// Does nothing for a given duration (deciseconds, so use the SECONDS define pls), then succeeds. duration = 0 waits forever. +/datum/bt_node/ai_behavior/wait + /// world.time when the wait ends. 0 when waiting forever + VAR_PRIVATE/end_time = 0 + /// How long to wait, in deciseconds. 0 waits forever. + var/duration = 0 + +/datum/bt_node/ai_behavior/wait/setup(datum/ai_controller/controller) + end_time = duration > 0 ? world.time + duration : 0 + return TRUE + +/datum/bt_node/ai_behavior/wait/perform(seconds_per_tick, datum/ai_controller/controller) + if(!end_time || world.time < end_time) + return AI_BEHAVIOR_INSTANT + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/wait/reset_tick_state() + end_time = 0 + ..() diff --git a/code/datums/ai/generic_decorators/ability_available.dm b/code/datums/ai/generic_decorators/ability_available.dm new file mode 100644 index 000000000000..99050ba44497 --- /dev/null +++ b/code/datums/ai/generic_decorators/ability_available.dm @@ -0,0 +1,8 @@ +/// Gates on whether a mob ability stored in a blackboard key is currently available. Needs to poll since we dont have nice signals to register too : ( +/datum/bt_node/decorator/ability_available + /// Blackboard key holding the ability datum + var/ability_key = BB_GENERIC_ACTION + +/datum/bt_node/decorator/ability_available/check_condition(datum/ai_controller/controller) + var/datum/action/action = controller.blackboard[ability_key] + return !QDELETED(action) && action.IsAvailable() diff --git a/code/datums/ai/generic_decorators/bb_key_at_least.dm b/code/datums/ai/generic_decorators/bb_key_at_least.dm new file mode 100644 index 000000000000..b7e9c5e3c60d --- /dev/null +++ b/code/datums/ai/generic_decorators/bb_key_at_least.dm @@ -0,0 +1,8 @@ +/// Passes if the given blackboard key holds a number >= minimum. Returns FAILURE if the key is unset or below minimum. +/datum/bt_node/decorator/bb_key_at_least + var/key + var/minimum + +/datum/bt_node/decorator/bb_key_at_least/check_condition(datum/ai_controller/controller) + var/value = controller.blackboard[key] + return !isnull(value) && value >= minimum diff --git a/code/datums/ai/generic_decorators/bb_key_equals.dm b/code/datums/ai/generic_decorators/bb_key_equals.dm new file mode 100644 index 000000000000..dfabc05ffa90 --- /dev/null +++ b/code/datums/ai/generic_decorators/bb_key_equals.dm @@ -0,0 +1,7 @@ +/// Passes if the given blackboard key equals the expected value. +/datum/bt_node/decorator/bb_key_equals + var/key + var/value + +/datum/bt_node/decorator/bb_key_equals/check_condition(datum/ai_controller/controller) + return bb_key_equals(controller, key, value) diff --git a/code/datums/ai/generic_decorators/bb_key_list_min_count.dm b/code/datums/ai/generic_decorators/bb_key_list_min_count.dm new file mode 100644 index 000000000000..27792bc4af6a --- /dev/null +++ b/code/datums/ai/generic_decorators/bb_key_list_min_count.dm @@ -0,0 +1,14 @@ +/// Passes if the blackboard key holds a list with at least min_count entries. +/datum/bt_node/decorator/bb_key_list_min_count + var/key + var/min_count = 1 + +/datum/bt_node/decorator/bb_key_list_min_count/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/bb_key_list_min_count/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/datum/bt_node/decorator/bb_key_list_min_count/check_condition(datum/ai_controller/controller) + return LAZYLEN(controller.blackboard[key]) >= min_count diff --git a/code/datums/ai/generic_decorators/bb_key_set.dm b/code/datums/ai/generic_decorators/bb_key_set.dm new file mode 100644 index 000000000000..68edc5971370 --- /dev/null +++ b/code/datums/ai/generic_decorators/bb_key_set.dm @@ -0,0 +1,13 @@ +/// Is the key set to a non-null value +/datum/bt_node/decorator/bb_key_set + var/key = null + +/datum/bt_node/decorator/bb_key_set/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/bb_key_set/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/datum/bt_node/decorator/bb_key_set/check_condition(datum/ai_controller/controller) + return controller.blackboard_key_exists(key) diff --git a/code/datums/ai/generic_decorators/bb_key_true.dm b/code/datums/ai/generic_decorators/bb_key_true.dm new file mode 100644 index 000000000000..680ac0afb008 --- /dev/null +++ b/code/datums/ai/generic_decorators/bb_key_true.dm @@ -0,0 +1,7 @@ +/// Passes if the given blackboard key equals a true-ey value +/datum/bt_node/decorator/bb_key_true + var/key + +/datum/bt_node/decorator/bb_key_true/check_condition(datum/ai_controller/controller) + var/result = controller.blackboard[key] + return !!result diff --git a/code/datums/ai/generic_decorators/buckle_target_dangerous.dm b/code/datums/ai/generic_decorators/buckle_target_dangerous.dm new file mode 100644 index 000000000000..115f415236a9 --- /dev/null +++ b/code/datums/ai/generic_decorators/buckle_target_dangerous.dm @@ -0,0 +1,10 @@ +/// Gates on the buckle target having TRAIT_DANGEROUS_BUCKLE; fails if pacifist = TRUE. +/datum/bt_node/decorator/buckle_target_dangerous + var/pacifist = FALSE + var/target_key = BB_BASIC_MOB_ESCAPE_TARGET + +/datum/bt_node/decorator/buckle_target_dangerous/check_condition(datum/ai_controller/controller) + if(pacifist) + return FALSE + var/atom/target = controller.blackboard[target_key] + return !isnull(target) && HAS_TRAIT(target, TRAIT_DANGEROUS_BUCKLE) diff --git a/code/datums/ai/generic_decorators/can_see_target.dm b/code/datums/ai/generic_decorators/can_see_target.dm new file mode 100644 index 000000000000..9a2ea5b7fe24 --- /dev/null +++ b/code/datums/ai/generic_decorators/can_see_target.dm @@ -0,0 +1,17 @@ +/** + * Gates child on a blackboard key holding a visible, non-deleted atom. + * If the target is null, deleted, or not visible to the pawn, clears the key and returns BT_FAILURE. + */ +/datum/bt_node/decorator/can_see_target + /// The blackboard key whose value is the atom to validate. + var/key + /// Visibility range passed to can_see(). Default matches typical bot search radius. + var/range = 7 + +/datum/bt_node/decorator/can_see_target/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target) || !can_see(controller.pawn, target, range)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_DECISIONMAKING, "[controller.pawn] can_see_target([key]): target lost") + controller.clear_blackboard_key(key) + return FALSE + return TRUE diff --git a/code/datums/ai/generic_decorators/check_cooldown.dm b/code/datums/ai/generic_decorators/check_cooldown.dm new file mode 100644 index 000000000000..721179dd9e11 --- /dev/null +++ b/code/datums/ai/generic_decorators/check_cooldown.dm @@ -0,0 +1,28 @@ +/// Check if the specified blackboard key is off cooldown. +/datum/bt_node/decorator/key_off_cooldown + var/cooldown_key + +/datum/bt_node/decorator/key_off_cooldown/check_condition(datum/ai_controller/controller) + var/cooldown_time = controller.blackboard[cooldown_key] + return isnull(cooldown_time) || cooldown_time <= world.time + +/** + * Checks if a blackboard cooldown key is off cooldown before ticking the child, + * then sets it when the child finishes replacing the key_off_cooldown + * + * cooldown_duration is in deciseconds (e.g. 30 SECONDS). + * lock_on_succeed = TRUE (default): lock after child SUCCESS only. + * lock_on_succeed = FALSE: lock after any completion (SUCCESS or FAILURE). + */ +/datum/bt_node/decorator/cooldown + var/cooldown_key + var/cooldown_duration + var/lock_on_succeed = TRUE + +/datum/bt_node/decorator/cooldown/check_condition(datum/ai_controller/controller) + var/cooldown_time = controller.blackboard[cooldown_key] + return isnull(cooldown_time) || cooldown_time <= world.time + +/datum/bt_node/decorator/cooldown/on_child_complete(datum/ai_controller/controller, result) + if(!lock_on_succeed || result == BT_SUCCESS) + controller.set_blackboard_key(cooldown_key, world.time + cooldown_duration) diff --git a/code/datums/ai/generic_decorators/check_rider_stat.dm b/code/datums/ai/generic_decorators/check_rider_stat.dm new file mode 100644 index 000000000000..4d05ea5a0e65 --- /dev/null +++ b/code/datums/ai/generic_decorators/check_rider_stat.dm @@ -0,0 +1,11 @@ +///Checks the health status of our rider, if any. returns false if we dont have a rider to begin with +/datum/bt_node/decorator/check_rider_stat + ///stat we're interested in + var/target_stat = HARD_CRIT + +/datum/bt_node/decorator/check_rider_stat/check_condition(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!length(living_pawn.buckled_mobs)) + return FALSE + var/mob/living/buckled_to = living_pawn.buckled_mobs[1] + return buckled_to.stat == target_stat diff --git a/code/datums/ai/generic_decorators/container_attackable.dm b/code/datums/ai/generic_decorators/container_attackable.dm new file mode 100644 index 000000000000..8e4303e74e44 --- /dev/null +++ b/code/datums/ai/generic_decorators/container_attackable.dm @@ -0,0 +1,15 @@ +/// Gates on the container being worth attacking (pawn.obj_damage > damage_deflection); fails if pacifist = TRUE. +/datum/bt_node/decorator/container_attackable + var/pacifist = FALSE + var/target_key = BB_BASIC_MOB_ESCAPE_TARGET + +/datum/bt_node/decorator/container_attackable/check_condition(datum/ai_controller/controller) + if(pacifist) + return FALSE + if(!isbasicmob(controller.pawn)) + return FALSE + var/atom/target = controller.blackboard[target_key] + if(isnull(target)) + return FALSE + var/mob/living/basic/basic_pawn = controller.pawn + return basic_pawn.obj_damage > target.damage_deflection diff --git a/code/datums/ai/generic_decorators/do_once.dm b/code/datums/ai/generic_decorators/do_once.dm new file mode 100644 index 000000000000..d2d97776b0ee --- /dev/null +++ b/code/datums/ai/generic_decorators/do_once.dm @@ -0,0 +1,20 @@ +///Only returns TRUE the first time. Optionally only locks if the child succeeds. Can be unlocked again +/datum/bt_node/decorator/do_once + /// Blackboard key storing the completion latch. + var/lock_key + /// Whether a child FAILURE should leave this decorator available for another attempt. + var/only_lock_if_succeeded = FALSE + +/datum/bt_node/decorator/do_once/check_condition(datum/ai_controller/controller) + return !controller.blackboard_key_exists(lock_key) + +/datum/bt_node/decorator/do_once/on_child_complete(datum/ai_controller/controller, result) + if(!only_lock_if_succeeded || result == BT_SUCCESS) + controller.set_blackboard_key(lock_key, TRUE) + +/datum/bt_node/decorator/do_once/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(lock_key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(lock_key)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/do_once/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(lock_key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(lock_key))) diff --git a/code/datums/ai/generic_decorators/is_at_distance.dm b/code/datums/ai/generic_decorators/is_at_distance.dm new file mode 100644 index 000000000000..d8852e1b8954 --- /dev/null +++ b/code/datums/ai/generic_decorators/is_at_distance.dm @@ -0,0 +1,21 @@ +/// Decorator that requires the controller's pawn to be within range of a blackboard target. +/datum/bt_node/decorator/is_at_distance + /// Blackboard key holding the atom to approach. Must be set on the subtype or via configure(). + var/target_key = null + /// Minimum distance (inclusive) from target. 0 means no lower bound. + var/min_distance = -1 + /// Maximum distance (inclusive) from target + var/maximum_distance = -1 + /// If TRUE, also verifies target.IsReachableBy(pawn) before passing to child. + var/require_reach = FALSE + +/datum/bt_node/decorator/is_at_distance/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + + var/atom/movable/pawn = controller.pawn + var/dist = get_dist(pawn, target) + var/reachable = !require_reach || target.IsReachableBy(pawn) + + return (maximum_distance == -1 || dist <= maximum_distance) && dist >= min_distance && reachable diff --git a/code/datums/ai/generic_decorators/is_dragging.dm b/code/datums/ai/generic_decorators/is_dragging.dm new file mode 100644 index 000000000000..6abe04896af8 --- /dev/null +++ b/code/datums/ai/generic_decorators/is_dragging.dm @@ -0,0 +1,6 @@ +/// Gates child on pawn currently pulling something. Use invert = TRUE for the opposite. Checked each tick. +/datum/bt_node/decorator/is_dragging + +/datum/bt_node/decorator/is_dragging/check_condition(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + return !!living_pawn.pulling diff --git a/code/datums/ai/generic_decorators/is_grabbing_target.dm b/code/datums/ai/generic_decorators/is_grabbing_target.dm new file mode 100644 index 000000000000..9e9dd8fae31a --- /dev/null +++ b/code/datums/ai/generic_decorators/is_grabbing_target.dm @@ -0,0 +1,20 @@ +///Are we grabbing the specified target key? +/datum/bt_node/decorator/is_grabbing_target + /// The blackboard key whose value is the grabbed atom. + var/key + +/datum/bt_node/decorator/is_grabbing_target/check_condition(datum/ai_controller/controller) + var/atom/movable/target = controller.blackboard[key] + var/mob/living/our_mob = controller.pawn + if(QDELETED(target) || our_mob.pulling != target) + controller.clear_blackboard_key(key) + return FALSE + return TRUE + +/datum/bt_node/decorator/is_grabbing_target/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_LIVING_START_PULL, COMSIG_ATOM_NO_LONGER_PULLING, COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/is_grabbing_target/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_LIVING_START_PULL, COMSIG_ATOM_NO_LONGER_PULLING, COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + diff --git a/code/datums/ai/generic_decorators/is_holding_target.dm b/code/datums/ai/generic_decorators/is_holding_target.dm new file mode 100644 index 000000000000..3fe8068b7096 --- /dev/null +++ b/code/datums/ai/generic_decorators/is_holding_target.dm @@ -0,0 +1,24 @@ +/** + * Gates child on the pawn currently holding the item at the given blackboard key in its hands. + * Clears the key and returns BT_FAILURE if the item has been deleted. + * Returns BT_FAILURE without clearing the key if the item exists but is not held the item + * may be in a bag or on the floor and other branches may still need to locate it. + */ +/datum/bt_node/decorator/is_holding_target + /// The blackboard key whose value is the item to check. + var/key + +/datum/bt_node/decorator/is_holding_target/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM, COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/is_holding_target/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM, COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/datum/bt_node/decorator/is_holding_target/check_condition(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[key] + var/mob/mob_pawn = controller.pawn + if(QDELETED(target)) + controller.clear_blackboard_key(key) + return FALSE + return !!mob_pawn.is_holding(target) diff --git a/code/datums/ai/generic_decorators/is_in_vent.dm b/code/datums/ai/generic_decorators/is_in_vent.dm new file mode 100644 index 000000000000..de4e08f500c0 --- /dev/null +++ b/code/datums/ai/generic_decorators/is_in_vent.dm @@ -0,0 +1,13 @@ +/// Gates on the pawn currently being inside a vent (has TRAIT_MOVE_VENTCRAWLING). +/datum/bt_node/decorator/is_in_vent + observer_abort = BT_ABORT_LOWER_PRIORITY + +/datum/bt_node/decorator/is_in_vent/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING), SIGNAL_REMOVETRAIT(TRAIT_MOVE_VENTCRAWLING)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/is_in_vent/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING), SIGNAL_REMOVETRAIT(TRAIT_MOVE_VENTCRAWLING))) + +/datum/bt_node/decorator/is_in_vent/check_condition(datum/ai_controller/controller) + return HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING) diff --git a/code/datums/ai/generic_decorators/is_target_stunned.dm b/code/datums/ai/generic_decorators/is_target_stunned.dm new file mode 100644 index 000000000000..13f11c799d8e --- /dev/null +++ b/code/datums/ai/generic_decorators/is_target_stunned.dm @@ -0,0 +1,11 @@ +/// Gates child on the mob held in a blackboard key being paralyzed (stunned and on the ground). +/// Use "invert": true to gate on the target NOT being stunned. Used by arrest bots like ED209 to close in once the target is incapacitated. +/datum/bt_node/decorator/is_target_stunned + /// Blackboard key holding the mob to check. + var/key = BB_CURRENT_TARGET + +/datum/bt_node/decorator/is_target_stunned/check_condition(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[key] + if(QDELETED(target) || !isliving(target)) + return FALSE + return !!target.IsParalyzed() diff --git a/code/datums/ai/generic_decorators/item_inside_pawn.dm b/code/datums/ai/generic_decorators/item_inside_pawn.dm new file mode 100644 index 000000000000..d4c3d31013dd --- /dev/null +++ b/code/datums/ai/generic_decorators/item_inside_pawn.dm @@ -0,0 +1,46 @@ +/** + * Gates child on the item at the given blackboard key being located anywhere inside the pawn + * (recursive contents check via locate()). Clears the key and returns FALSE if the item is deleted. + * Does NOT clear the key if the item simply isn't inside the pawn other branches may still use it. + */ +/datum/bt_node/decorator/item_inside_pawn + /// Blackboard key holding the item to check. + var/key = null + /// The item currently being observed for location changes. + var/obj/item/observed_item = null + +/datum/bt_node/decorator/item_inside_pawn/register_observe_signals(atom/pawn) + var/obj/item/target = owning_controller?.blackboard[key] + if(target) + observed_item = target + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_item_key_changed)) + return TRUE + +/datum/bt_node/decorator/item_inside_pawn/unregister_observe_signals(atom/pawn) + if(observed_item) + UnregisterSignal(observed_item, COMSIG_MOVABLE_MOVED) + observed_item = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/// Fires when the blackboard key changes. Rebinds the move observer to the new item and re-evaluates. +/datum/bt_node/decorator/item_inside_pawn/proc/on_item_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/obj/item/target = owning_controller?.blackboard[key] + if(target == observed_item) + return + if(observed_item) + UnregisterSignal(observed_item, COMSIG_MOVABLE_MOVED) + observed_item = null + if(target) + observed_item = target + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/item_inside_pawn/check_condition(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[key] + if(QDELETED(target)) + controller.clear_blackboard_key(key) + return FALSE + return !isnull(locate(target) in controller.pawn) diff --git a/code/datums/ai/generic_decorators/key_in_typelist.dm b/code/datums/ai/generic_decorators/key_in_typelist.dm new file mode 100644 index 000000000000..0bb11ecc9d2a --- /dev/null +++ b/code/datums/ai/generic_decorators/key_in_typelist.dm @@ -0,0 +1,30 @@ +/// Gates child on the atom in `key` being an instance of a type in the typelist held at `typelist_key`. Use "invert": true for the opposite. +/datum/bt_node/decorator/key_in_typelist + /// Blackboard key holding the atom to type-check. + var/key = BB_CURRENT_TARGET + /// Blackboard key holding the list of typepaths to check against. + var/typelist_key + +/datum/bt_node/decorator/key_in_typelist/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key), + COMSIG_AI_BLACKBOARD_KEY_SET(typelist_key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(typelist_key), + ), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/key_in_typelist/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key), + COMSIG_AI_BLACKBOARD_KEY_SET(typelist_key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(typelist_key), + )) + +/datum/bt_node/decorator/key_in_typelist/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + var/list/types = controller.blackboard[typelist_key] + if(QDELETED(target) || isnull(types)) + return FALSE + return is_type_in_list(target, types) diff --git a/code/datums/ai/generic_decorators/keys_different_gender.dm b/code/datums/ai/generic_decorators/keys_different_gender.dm new file mode 100644 index 000000000000..fa5335cb2051 --- /dev/null +++ b/code/datums/ai/generic_decorators/keys_different_gender.dm @@ -0,0 +1,30 @@ +///Checks for gender difference, I know, not very 2026 of me but until we add m-preg this'll have to do. +/datum/bt_node/decorator/keys_different_gender + /// Blackboard key holding the first mob. + var/key_a = BB_CURRENT_TARGET + /// Blackboard key holding the second mob. + var/key_b + +/datum/bt_node/decorator/keys_different_gender/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(key_a), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key_a), + COMSIG_AI_BLACKBOARD_KEY_SET(key_b), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key_b), + ), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/keys_different_gender/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(key_a), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key_a), + COMSIG_AI_BLACKBOARD_KEY_SET(key_b), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(key_b), + )) + +/datum/bt_node/decorator/keys_different_gender/check_condition(datum/ai_controller/controller) + var/mob/mob_a = controller.blackboard[key_a] + var/mob/mob_b = controller.blackboard[key_b] + if(isnull(mob_a) || isnull(mob_b)) + return FALSE + return mob_a.gender != mob_b.gender diff --git a/code/datums/ai/generic_decorators/mob_stat_at_least.dm b/code/datums/ai/generic_decorators/mob_stat_at_least.dm new file mode 100644 index 000000000000..ca17ea121594 --- /dev/null +++ b/code/datums/ai/generic_decorators/mob_stat_at_least.dm @@ -0,0 +1,45 @@ +/// Passes when the mob held in a blackboard key has a stat value at that is at least X. Higher is more dead. +/datum/bt_node/decorator/mob_stat_at_least + /// Blackboard key holding the mob to check. + var/key = null + /// Minimum stat value (inclusive) for the condition to pass. Default: CONSCIOUS. + var/min_stat = STABLE + /// The mob currently being observed. Tracked so we can unregister when the key changes or teardown runs. + var/mob/observed_mob = null + +/datum/bt_node/decorator/mob_stat_at_least/register_observe_signals(atom/pawn) + var/mob/target = owning_controller?.blackboard[key] + if(target) + observed_mob = target + RegisterSignal(target, COMSIG_MOB_STATCHANGE, PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_mob_key_changed)) + return TRUE + +/datum/bt_node/decorator/mob_stat_at_least/unregister_observe_signals(atom/pawn) + if(observed_mob) + UnregisterSignal(observed_mob, COMSIG_MOB_STATCHANGE) + observed_mob = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + + +/// Fires when the blackboard key changes. Rebinds the stat observer to the new mob and re-evaluates. +/datum/bt_node/decorator/mob_stat_at_least/proc/on_mob_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/mob/target = owning_controller?.blackboard[key] + if(target == observed_mob) + return + if(observed_mob) + UnregisterSignal(observed_mob, COMSIG_MOB_STATCHANGE) + observed_mob = null + if(target) + observed_mob = target + RegisterSignal(target, COMSIG_MOB_STATCHANGE, PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + + +/datum/bt_node/decorator/mob_stat_at_least/check_condition(datum/ai_controller/controller) + var/mob/target = controller.blackboard[key] + if(!ismob(target)) + return FALSE + return target.stat >= min_stat diff --git a/code/datums/ai/generic_decorators/no_humans_watching.dm b/code/datums/ai/generic_decorators/no_humans_watching.dm new file mode 100644 index 000000000000..6ffadadbc666 --- /dev/null +++ b/code/datums/ai/generic_decorators/no_humans_watching.dm @@ -0,0 +1,11 @@ +/// Passes only when no living, conscious human can see or hear the pawn within range. +/// Used by cowardly mobs that only act when nobody is looking. +/datum/bt_node/decorator/no_humans_watching + /// How far to look for witnesses. + var/range = 7 + +/datum/bt_node/decorator/no_humans_watching/check_condition(datum/ai_controller/controller) + for(var/mob/living/carbon/human/watcher in hearers(range, controller.pawn)) + if(watcher.stat != DEAD) + return FALSE + return TRUE diff --git a/code/datums/ai/generic_decorators/optional.dm b/code/datums/ai/generic_decorators/optional.dm new file mode 100644 index 000000000000..cc04b8547f15 --- /dev/null +++ b/code/datums/ai/generic_decorators/optional.dm @@ -0,0 +1,7 @@ +/// Always reports SUCCESS to the parent, even if the child fails. Lets a sequence continue past a step that's allowed to fail. RUNNING still passes through unchanged. +/datum/bt_node/decorator/optional + +/datum/bt_node/decorator/optional/tick(datum/ai_controller/controller, seconds_per_tick) + . = ..() + if(. == BT_FAILURE) + return BT_SUCCESS diff --git a/code/datums/ai/generic_decorators/pawn_buckled_to_obj.dm b/code/datums/ai/generic_decorators/pawn_buckled_to_obj.dm new file mode 100644 index 000000000000..045145882498 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_buckled_to_obj.dm @@ -0,0 +1,18 @@ +/// Gates on the pawn being buckled to an obj; writes pawn.buckled to the escape target key as a side effect. +/datum/bt_node/decorator/pawn_buckled_to_obj + var/target_key = BB_BASIC_MOB_ESCAPE_TARGET + observer_abort = BT_ABORT_LOWER_PRIORITY + +/datum/bt_node/decorator/pawn_buckled_to_obj/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_MOB_BUCKLED, COMSIG_MOB_UNBUCKLED), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_buckled_to_obj/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_MOB_BUCKLED, COMSIG_MOB_UNBUCKLED)) + +/datum/bt_node/decorator/pawn_buckled_to_obj/check_condition(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + if(!isobj(pawn.buckled)) + return FALSE + controller.blackboard[target_key] = pawn.buckled + return TRUE diff --git a/code/datums/ai/generic_decorators/pawn_contained_in_obj.dm b/code/datums/ai/generic_decorators/pawn_contained_in_obj.dm new file mode 100644 index 000000000000..a49659df49da --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_contained_in_obj.dm @@ -0,0 +1,18 @@ +/// Gates on the pawn being inside an obj (not a turf, mob, or mob_holder); writes pawn.loc to the escape target key. +/datum/bt_node/decorator/pawn_contained_in_obj + var/target_key = BB_BASIC_MOB_ESCAPE_TARGET + observer_abort = BT_ABORT_LOWER_PRIORITY + +/datum/bt_node/decorator/pawn_contained_in_obj/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_MOVABLE_MOVED), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_contained_in_obj/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_MOVABLE_MOVED)) + +/datum/bt_node/decorator/pawn_contained_in_obj/check_condition(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + if(isturf(pawn.loc) || ismob(pawn.loc) || istype(pawn.loc, /obj/item/mob_holder) || HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING)) + return FALSE + controller.blackboard[target_key] = pawn.loc + return TRUE diff --git a/code/datums/ai/generic_decorators/pawn_farther_than_from_key.dm b/code/datums/ai/generic_decorators/pawn_farther_than_from_key.dm new file mode 100644 index 000000000000..997f6f0abcd4 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_farther_than_from_key.dm @@ -0,0 +1,13 @@ +/// Passes if the pawn is farther than the distance stored in distance_key from the atom stored in anchor_key. +/datum/bt_node/decorator/pawn_farther_than_from_key + /// Blackboard key holding the anchor atom. + var/anchor_key + /// Blackboard key whose integer value is the minimum distance threshold (exclusive). + var/distance_key + +/datum/bt_node/decorator/pawn_farther_than_from_key/check_condition(datum/ai_controller/controller) + var/atom/anchor = controller.blackboard[anchor_key] + if(QDELETED(anchor)) + return FALSE + var/min_dist = controller.blackboard[distance_key] + return get_dist(controller.pawn, anchor) > min_dist diff --git a/code/datums/ai/generic_decorators/pawn_grabbed_by_enemy.dm b/code/datums/ai/generic_decorators/pawn_grabbed_by_enemy.dm new file mode 100644 index 000000000000..64e7a0df473f --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_grabbed_by_enemy.dm @@ -0,0 +1,22 @@ +/// Gates on the pawn being grabbed above GRAB_PASSIVE by a mob the targeting strategy considers an enemy. +/datum/bt_node/decorator/pawn_grabbed_by_enemy + var/targeting_strategy_key = BB_TARGETING_STRATEGY + child_typepath = /datum/bt_node/ai_behavior/resist + +/datum/bt_node/decorator/pawn_grabbed_by_enemy/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_LIVING_GET_PULLED, COMSIG_ATOM_NO_LONGER_PULLED, COMSIG_MOVABLE_SET_GRAB_STATE), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_grabbed_by_enemy/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_LIVING_GET_PULLED, COMSIG_ATOM_NO_LONGER_PULLED, COMSIG_MOVABLE_SET_GRAB_STATE)) + +/datum/bt_node/decorator/pawn_grabbed_by_enemy/check_condition(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + var/mob/puller = pawn.pulledby + if(isnull(puller) || puller.grab_state <= GRAB_PASSIVE) + return FALSE + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) + if(!strategy?.is_valid_target(pawn, puller)) + return FALSE + var/list/friends = controller.blackboard[BB_FRIENDS_LIST] || list() + return !(puller in friends) diff --git a/code/datums/ai/generic_decorators/pawn_has_gravity.dm b/code/datums/ai/generic_decorators/pawn_has_gravity.dm new file mode 100644 index 000000000000..78de74825b48 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_has_gravity.dm @@ -0,0 +1,13 @@ +/// Gates child on the pawn having gravity. Use invert = TRUE to gate on weightlessness instead. +/datum/bt_node/decorator/pawn_has_gravity + +/datum/bt_node/decorator/pawn_has_gravity/register_observe_signals(atom/pawn) + RegisterSignal(pawn, COMSIG_LIVING_GRAVITY_CHANGED, PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_has_gravity/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, COMSIG_LIVING_GRAVITY_CHANGED) + +/datum/bt_node/decorator/pawn_has_gravity/check_condition(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + return istype(living_pawn) && living_pawn.has_gravity() diff --git a/code/datums/ai/generic_decorators/pawn_has_trait_from.dm b/code/datums/ai/generic_decorators/pawn_has_trait_from.dm new file mode 100644 index 000000000000..b422f828717f --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_has_trait_from.dm @@ -0,0 +1,16 @@ +/// Gates child on the pawn having a given trait from a specific source. Use "invert": true to gate on the trait being absent. +/datum/bt_node/decorator/pawn_has_trait_from + /// The trait to check for. + var/trait = null + /// The source the trait must originate from. + var/source = null + +/datum/bt_node/decorator/pawn_has_trait_from/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_has_trait_from/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait))) + +/datum/bt_node/decorator/pawn_has_trait_from/check_condition(datum/ai_controller/controller) + return HAS_TRAIT_FROM(controller.pawn, trait, source) diff --git a/code/datums/ai/generic_decorators/pawn_health_below.dm b/code/datums/ai/generic_decorators/pawn_health_below.dm new file mode 100644 index 000000000000..9a7390251bc5 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_health_below.dm @@ -0,0 +1,20 @@ +/// Passes when the pawn's current health is below the configured threshold. +/datum/bt_node/decorator/pawn_health_below + /// Health value that the pawn must be below for this decorator to pass + var/health_threshold = 0 + /// blackboard value holding our threshold, if this is null, health_threshold will be used instead + var/health_blackboard_key + +/datum/bt_node/decorator/pawn_health_below/register_observe_signals(atom/pawn) + RegisterSignal(pawn, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_health_below/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, COMSIG_LIVING_HEALTH_UPDATE) + +/datum/bt_node/decorator/pawn_health_below/check_condition(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!isliving(living_pawn)) + return FALSE + var/final_threshold = controller.blackboard[health_blackboard_key] || health_threshold + return living_pawn.health < final_threshold diff --git a/code/datums/ai/generic_decorators/pawn_inside_mob.dm b/code/datums/ai/generic_decorators/pawn_inside_mob.dm new file mode 100644 index 000000000000..cef895c380a2 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_inside_mob.dm @@ -0,0 +1,13 @@ +/// Gates on the pawn being located inside another mob (e.g. absorbed or shapeshifted). +/datum/bt_node/decorator/pawn_inside_mob + observer_abort = BT_ABORT_LOWER_PRIORITY + +/datum/bt_node/decorator/pawn_inside_mob/register_observe_signals(atom/pawn) + RegisterSignal(pawn, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_inside_mob/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, COMSIG_MOVABLE_MOVED) + +/datum/bt_node/decorator/pawn_inside_mob/check_condition(datum/ai_controller/controller) + return ismob(controller.pawn.loc) diff --git a/code/datums/ai/generic_decorators/pawn_is_restrained.dm b/code/datums/ai/generic_decorators/pawn_is_restrained.dm new file mode 100644 index 000000000000..2a9b7c96f764 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_is_restrained.dm @@ -0,0 +1,14 @@ +/// Gates on the pawn having TRAIT_RESTRAINED; reacts to trait add/remove signals. +/datum/bt_node/decorator/pawn_is_restrained + observer_abort = BT_ABORT_LOWER_PRIORITY + child_typepath = /datum/bt_node/ai_behavior/resist + +/datum/bt_node/decorator/pawn_is_restrained/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(SIGNAL_ADDTRAIT(TRAIT_RESTRAINED), SIGNAL_REMOVETRAIT(TRAIT_RESTRAINED)), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_is_restrained/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(SIGNAL_ADDTRAIT(TRAIT_RESTRAINED), SIGNAL_REMOVETRAIT(TRAIT_RESTRAINED))) + +/datum/bt_node/decorator/pawn_is_restrained/check_condition(datum/ai_controller/controller) + return HAS_TRAIT(controller.pawn, TRAIT_RESTRAINED) diff --git a/code/datums/ai/generic_decorators/pawn_loc_is_type.dm b/code/datums/ai/generic_decorators/pawn_loc_is_type.dm new file mode 100644 index 000000000000..339078ad49c5 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_loc_is_type.dm @@ -0,0 +1,15 @@ +/// Passes when the pawn's loc is of the given type (e.g. the pawn is inside a specific structure). +/datum/bt_node/decorator/pawn_loc_is_type + observer_abort = BT_ABORT_LOWER_PRIORITY + /// Typepath the pawn's loc must match. + var/loc_type + +/datum/bt_node/decorator/pawn_loc_is_type/register_observe_signals(atom/pawn) + RegisterSignal(pawn, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/pawn_loc_is_type/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, COMSIG_MOVABLE_MOVED) + +/datum/bt_node/decorator/pawn_loc_is_type/check_condition(datum/ai_controller/controller) + return istype(controller.pawn.loc, loc_type) diff --git a/code/datums/ai/generic_decorators/pawn_nutrition_below.dm b/code/datums/ai/generic_decorators/pawn_nutrition_below.dm new file mode 100644 index 000000000000..ea559a106a74 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_nutrition_below.dm @@ -0,0 +1,10 @@ +/// Passes if the pawn's nutrition is below the given threshold. +/datum/bt_node/decorator/pawn_nutrition_below + /// Nutrition threshold to check against. + var/nutrition_threshold = NUTRITION_LEVEL_HUNGRY + +/datum/bt_node/decorator/pawn_nutrition_below/check_condition(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + if(!isliving(pawn)) + return FALSE + return pawn.nutrition < nutrition_threshold diff --git a/code/datums/ai/generic_decorators/pawn_same_z_as_key.dm b/code/datums/ai/generic_decorators/pawn_same_z_as_key.dm new file mode 100644 index 000000000000..eadaef069688 --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_same_z_as_key.dm @@ -0,0 +1,9 @@ +/// Passes if the pawn is on the same z-level as the atom stored in the given blackboard key. +/datum/bt_node/decorator/pawn_same_z_as_key + var/key + +/datum/bt_node/decorator/pawn_same_z_as_key/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target)) + return FALSE + return controller.pawn.z == target.z diff --git a/code/datums/ai/generic_decorators/pawn_turf_has_trait.dm b/code/datums/ai/generic_decorators/pawn_turf_has_trait.dm new file mode 100644 index 000000000000..e5febf59704c --- /dev/null +++ b/code/datums/ai/generic_decorators/pawn_turf_has_trait.dm @@ -0,0 +1,7 @@ +/// Gates child on the pawn's current turf having a given trait. Use invert = TRUE to gate on the trait being absent. +/datum/bt_node/decorator/pawn_turf_has_trait + var/trait = null + +/datum/bt_node/decorator/pawn_turf_has_trait/check_condition(datum/ai_controller/controller) + var/turf/my_turf_in_the_middle_of_my_turf = get_turf(controller.pawn) + return HAS_TRAIT(my_turf_in_the_middle_of_my_turf, trait) diff --git a/code/datums/ai/generic_decorators/random_chance.dm b/code/datums/ai/generic_decorators/random_chance.dm new file mode 100644 index 000000000000..f33392904a3d --- /dev/null +++ b/code/datums/ai/generic_decorators/random_chance.dm @@ -0,0 +1,7 @@ +///Decorator with a probability to pass, useful for things that sometimes happen. Slay queen +/datum/bt_node/decorator/random_chance + /// 0.0–1.0 float; converted to percentage for prob(). Configure via BT_DECORATOR. + var/chance = 0.5 + +/datum/bt_node/decorator/random_chance/check_condition(datum/ai_controller/controller) + return prob(chance * 100) diff --git a/code/datums/ai/generic_decorators/random_chance_from_key.dm b/code/datums/ai/generic_decorators/random_chance_from_key.dm new file mode 100644 index 000000000000..9c7d778cd60b --- /dev/null +++ b/code/datums/ai/generic_decorators/random_chance_from_key.dm @@ -0,0 +1,10 @@ +/// Passes with a probability taken from a blackboard key (0-100 integer). +/datum/bt_node/decorator/random_chance_from_key + /// Blackboard key holding the integer probability (0-100) + var/chance_key = null + +/datum/bt_node/decorator/random_chance_from_key/check_condition(datum/ai_controller/controller) + var/chance = controller.blackboard[chance_key] + if(!chance) + return FALSE + return prob(chance) diff --git a/code/datums/ai/generic_decorators/target_can_be_shove_stunned.dm b/code/datums/ai/generic_decorators/target_can_be_shove_stunned.dm new file mode 100644 index 000000000000..618f96380e0d --- /dev/null +++ b/code/datums/ai/generic_decorators/target_can_be_shove_stunned.dm @@ -0,0 +1,48 @@ +/// Passes when shoving the target right now would knock them down rather than just push them back. In the future this could maybe check for any options, right now ti just checks from me to target +/datum/bt_node/decorator/target_can_be_shove_stunned + /// Blackboard key holding the mob we would shove. + var/target_key + +/datum/bt_node/decorator/target_can_be_shove_stunned/check_condition(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/mob/living/living_target = controller.blackboard[target_key] + + if(!isliving(living_pawn) || !isliving(living_target)) + return FALSE + + // Standing up, not ourselves, not sharing a turf. Also guarantees a real shove direction below. + if(!living_pawn.can_disarm(living_target)) + return FALSE + + // Authoritative source for whether this pairing can hit something, is knockdown immune, or gets side kicked. + var/shove_flags = living_target.get_shove_flags(living_pawn, null) + if(!(shove_flags & SHOVE_CAN_HIT_SOMETHING)) + return FALSE + if(shove_flags & (SHOVE_KNOCKDOWN_BLOCKED | SHOVE_CAN_KICK_SIDE)) + return FALSE + + return shove_will_knockdown(living_pawn, living_target) + +/// Whether the target has nowhere to be pushed, which is what turns a shove into a knockdown. (and thus succesful) +/datum/bt_node/decorator/target_can_be_shove_stunned/proc/shove_will_knockdown(mob/living/living_pawn, mob/living/living_target) + var/shove_dir = get_dir(living_pawn.loc, living_target.loc) + var/turf/target_turf = get_turf(living_target) + var/turf/shove_turf = get_step(target_turf, shove_dir) + + // Nothing behind them at all, so they've got nowhere to go. + if(isnull(shove_turf)) + return TRUE + + // Passing the target as source_atom runs CanPass, which covers directional windows on the far turf. + if(shove_turf.is_blocked_turf(source_atom = living_target)) + return TRUE + + // A border object on their own turf facing the way we'd shove stops them leaving it in the first place, + // which is_blocked_turf() on the destination can't see. + if(!(shove_dir in GLOB.cardinals)) + return FALSE + for(var/obj/obj_content in target_turf) + if((obj_content.flags_1 & ON_BORDER_1) && obj_content.dir == shove_dir && obj_content.density) + return TRUE + + return FALSE diff --git a/code/datums/ai/generic_decorators/target_has_reagent.dm b/code/datums/ai/generic_decorators/target_has_reagent.dm new file mode 100644 index 000000000000..85458169bf53 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_has_reagent.dm @@ -0,0 +1,44 @@ +/// Gates child on the atom held in a blackboard key having a specific reagent in its reagent list. Use "invert": true to gate on the reagent being absent. +/datum/bt_node/decorator/target_has_reagent + /// Blackboard key holding the atom to check. + var/key = null + /// Typepath of the reagent to look for. + var/reagent_type = null + /// Tracked so we can rebind signals when the key or its reagents change. + var/datum/reagents/observed_holder = null + +/datum/bt_node/decorator/target_has_reagent/register_observe_signals(atom/pawn) + var/atom/target = owning_controller?.blackboard[key] + if(target?.reagents) + observed_holder = target.reagents + RegisterSignal(observed_holder, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_target_key_changed)) + return TRUE + +/datum/bt_node/decorator/target_has_reagent/unregister_observe_signals(atom/pawn) + if(observed_holder) + UnregisterSignal(observed_holder, COMSIG_REAGENTS_HOLDER_UPDATED) + observed_holder = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/// Fires when the blackboard key changes. Rebinds the reagent holder observer to the new target and re-evaluates. +/datum/bt_node/decorator/target_has_reagent/proc/on_target_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/atom/target = owning_controller?.blackboard[key] + var/datum/reagents/new_holder = target?.reagents + if(new_holder == observed_holder) + return + if(observed_holder) + UnregisterSignal(observed_holder, COMSIG_REAGENTS_HOLDER_UPDATED) + observed_holder = null + if(new_holder) + observed_holder = new_holder + RegisterSignal(observed_holder, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/target_has_reagent/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target) || !target.reagents) + return FALSE + return !!target.reagents.has_reagent(reagent_type) diff --git a/code/datums/ai/generic_decorators/target_has_trait.dm b/code/datums/ai/generic_decorators/target_has_trait.dm new file mode 100644 index 000000000000..8dbd03100041 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_has_trait.dm @@ -0,0 +1,43 @@ +/// Gates child on the atom held in a blackboard key having a given trait. Use "invert": true to gate on the trait being absent. +/datum/bt_node/decorator/target_has_trait + /// Blackboard key holding the atom to check. + var/key = null + /// The trait the target must have for the child to run. + var/trait = null + /// Tracked so we can rebind signals when the key changes. + var/atom/observed_target = null + +/datum/bt_node/decorator/target_has_trait/register_observe_signals(atom/pawn) + var/atom/target = owning_controller?.blackboard[key] + if(target) + observed_target = target + RegisterSignals(target, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait)), PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_target_key_changed)) + return TRUE + +/datum/bt_node/decorator/target_has_trait/unregister_observe_signals(atom/pawn) + if(observed_target) + UnregisterSignal(observed_target, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait))) + observed_target = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/// Fires when the blackboard key changes. Rebinds trait signals to the new target and re-evaluates. +/datum/bt_node/decorator/target_has_trait/proc/on_target_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/atom/target = owning_controller?.blackboard[key] + if(target == observed_target) + return + if(observed_target) + UnregisterSignal(observed_target, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait))) + observed_target = null + if(target) + observed_target = target + RegisterSignals(target, list(SIGNAL_ADDTRAIT(trait), SIGNAL_REMOVETRAIT(trait)), PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/target_has_trait/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target)) + return FALSE + return HAS_TRAIT(target, trait) diff --git a/code/datums/ai/generic_decorators/target_health_below_fraction.dm b/code/datums/ai/generic_decorators/target_health_below_fraction.dm new file mode 100644 index 000000000000..0f2ebb0d5949 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_health_below_fraction.dm @@ -0,0 +1,42 @@ +/// Passes when the living mob held in a blackboard key has health below a fraction of its max health. +/datum/bt_node/decorator/target_health_below_fraction + /// Blackboard key holding the mob to check. + var/key = null + /// Health fraction threshold (0.0–1.0). Passes when health < maxHealth * fraction. + var/fraction = 0.75 + /// The mob currently being observed. + VAR_FINAL/mob/living/observed_mob = null + +/datum/bt_node/decorator/target_health_below_fraction/register_observe_signals(atom/pawn) + var/mob/living/target = owning_controller?.blackboard[key] + if(isliving(target)) + observed_mob = target + RegisterSignal(target, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_mob_key_changed)) + return TRUE + +/datum/bt_node/decorator/target_health_below_fraction/unregister_observe_signals(atom/pawn) + if(observed_mob) + UnregisterSignal(observed_mob, COMSIG_LIVING_HEALTH_UPDATE) + observed_mob = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/datum/bt_node/decorator/target_health_below_fraction/proc/on_mob_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/mob/living/target = owning_controller?.blackboard[key] + if(target == observed_mob) + return + if(observed_mob) + UnregisterSignal(observed_mob, COMSIG_LIVING_HEALTH_UPDATE) + observed_mob = null + if(isliving(target)) + observed_mob = target + RegisterSignal(target, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/target_health_below_fraction/check_condition(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[key] + if(!isliving(target)) + return FALSE + return target.health < target.maxHealth * fraction diff --git a/code/datums/ai/generic_decorators/target_holding_lit_item.dm b/code/datums/ai/generic_decorators/target_holding_lit_item.dm new file mode 100644 index 000000000000..1ef20f50cf52 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_holding_lit_item.dm @@ -0,0 +1,21 @@ +/// Passes when the keyed target is a carbon holding a lit item whose type is in a blackboard-keyed type list. +/// Use "invert": true for the opposite. +/datum/bt_node/decorator/target_holding_lit_item + /// Blackboard key holding the atom to check. + var/key = BB_CURRENT_TARGET + /// Blackboard key holding the list of item typepaths we care about. + var/item_types_key + +/datum/bt_node/decorator/target_holding_lit_item/check_condition(datum/ai_controller/controller) + var/mob/living/carbon/target = controller.blackboard[key] + if(!iscarbon(target)) + return FALSE + var/list/item_types = controller.blackboard[item_types_key] + if(!length(item_types)) + return FALSE + for(var/obj/item/held_item in target.held_items) + if(!is_type_in_list(held_item, item_types)) + continue + if(held_item.light_on) + return TRUE + return FALSE diff --git a/code/datums/ai/generic_decorators/target_is_holding_item.dm b/code/datums/ai/generic_decorators/target_is_holding_item.dm new file mode 100644 index 000000000000..888a327ea37f --- /dev/null +++ b/code/datums/ai/generic_decorators/target_is_holding_item.dm @@ -0,0 +1,42 @@ +/// Gates child on the mob held in a blackboard key currently holding any item in their hands. +/// Use "invert": true to gate on the target holding nothing. +/datum/bt_node/decorator/target_is_holding_item + /// Blackboard key holding the mob to check. + var/key = BB_CURRENT_TARGET + /// Tracked so we can rebind signals when the key changes. + var/mob/observed_target = null + +/datum/bt_node/decorator/target_is_holding_item/register_observe_signals(atom/pawn) + var/mob/target = owning_controller?.blackboard[key] + if(ismob(target)) + observed_target = target + RegisterSignals(target, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM), PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_target_key_changed)) + return TRUE + +/datum/bt_node/decorator/target_is_holding_item/unregister_observe_signals(atom/pawn) + if(observed_target) + UnregisterSignal(observed_target, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM)) + observed_target = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/// Fires when the blackboard key changes. Rebinds equip signals to the new target and re-evaluates. +/datum/bt_node/decorator/target_is_holding_item/proc/on_target_key_changed(atom/source, ...) + SIGNAL_HANDLER + var/mob/target = owning_controller?.blackboard[key] + if(target == observed_target) + return + if(observed_target) + UnregisterSignal(observed_target, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM)) + observed_target = null + if(ismob(target)) + observed_target = target + RegisterSignals(target, list(COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_UNEQUIPPED_ITEM), PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/target_is_holding_item/check_condition(datum/ai_controller/controller) + var/mob/target = controller.blackboard[key] + if(QDELETED(target)) + return FALSE + return target.get_num_held_items() > 0 diff --git a/code/datums/ai/generic_decorators/target_is_type.dm b/code/datums/ai/generic_decorators/target_is_type.dm new file mode 100644 index 000000000000..dba7d6a2618d --- /dev/null +++ b/code/datums/ai/generic_decorators/target_is_type.dm @@ -0,0 +1,12 @@ +/// Gates child on the atom held in a blackboard key being an instance of a given typepath. Use "invert": true for the opposite. +/datum/bt_node/decorator/target_is_type + /// Blackboard key holding the atom to check. + var/key = BB_CURRENT_TARGET + /// Typepath the target must be an instance of. + var/target_type + +/datum/bt_node/decorator/target_is_type/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target) || isnull(target_type)) + return FALSE + return istype(target, target_type) diff --git a/code/datums/ai/generic_decorators/target_legcuffed.dm b/code/datums/ai/generic_decorators/target_legcuffed.dm new file mode 100644 index 000000000000..7eabdb699b36 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_legcuffed.dm @@ -0,0 +1,10 @@ +/// Gates child on the carbon held in a blackboard key being legcuffed. Use "invert": true to gate on not being legcuffed. +/datum/bt_node/decorator/target_legcuffed + /// Blackboard key holding the mob to check. + var/key = BB_CURRENT_TARGET + +/datum/bt_node/decorator/target_legcuffed/check_condition(datum/ai_controller/controller) + var/mob/living/carbon/target = controller.blackboard[key] + if(!iscarbon(target)) + return FALSE + return !isnull(target.legcuffed) diff --git a/code/datums/ai/generic_decorators/target_on_ground.dm b/code/datums/ai/generic_decorators/target_on_ground.dm new file mode 100644 index 000000000000..bba87d5d5318 --- /dev/null +++ b/code/datums/ai/generic_decorators/target_on_ground.dm @@ -0,0 +1,45 @@ +///Check fi target is still on a turf +/datum/bt_node/decorator/validate_target_on_turf + var/key + + var/atom/observed_target = null + +/datum/bt_node/decorator/validate_target_on_turf/register_observe_signals(atom/pawn) + var/atom/target = owning_controller?.blackboard[key] + if(target) + observed_target = target + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed), override = TRUE) + RegisterSignals(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key)), PROC_REF(on_key_changed)) + return TRUE + +/datum/bt_node/decorator/validate_target_on_turf/unregister_observe_signals(atom/pawn) + if(observed_target) + UnregisterSignal(observed_target, COMSIG_MOVABLE_MOVED) + observed_target = null + UnregisterSignal(pawn, list(COMSIG_AI_BLACKBOARD_KEY_SET(key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(key))) + +/datum/bt_node/decorator/validate_target_on_turf/proc/on_key_changed(atom/pawn, ...) + SIGNAL_HANDLER + var/atom/target = owning_controller?.blackboard[key] + if(target == observed_target) + return + if(observed_target) + UnregisterSignal(observed_target, COMSIG_MOVABLE_MOVED) + observed_target = null + if(target) + observed_target = target + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_signal_changed), override = TRUE) + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/validate_target_on_turf/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + if(QDELETED(target) || !isturf(target.loc)) + controller.clear_blackboard_key(key) + return FALSE + return TRUE + +/// Check without side effects for observer path. +/datum/bt_node/decorator/validate_target_on_turf/evaluate_for_observer(datum/ai_controller/controller) + var/atom/target = controller.blackboard[key] + return !QDELETED(target) && isturf(target.loc) diff --git a/code/datums/ai/generic_decorators/true_for_time.dm b/code/datums/ai/generic_decorators/true_for_time.dm new file mode 100644 index 000000000000..e70126b6a70f --- /dev/null +++ b/code/datums/ai/generic_decorators/true_for_time.dm @@ -0,0 +1,43 @@ +/** + * Passes while its duration has not elapsed. + * Starts timing on the first tick; resets when the tree resets. + * + * duration: how long to pass, in deciseconds (e.g. "10 SECONDS"). + * succeed_on_timeout FALSE (default): counts as succeed and lets lower nodes pass +*/ +/datum/bt_node/decorator/true_for_time + observer_abort = BT_ABORT_SELF + var/duration = 0 + var/succeed_on_timeout = FALSE + VAR_PRIVATE/timer_id = null + VAR_PRIVATE/timed_out = FALSE + +/datum/bt_node/decorator/true_for_time/register_observe_signals(atom/pawn) + timer_id = addtimer(CALLBACK(src, PROC_REF(on_timeout)), duration, TIMER_STOPPABLE|TIMER_DELETE_ME) + return TRUE + +/datum/bt_node/decorator/true_for_time/unregister_observe_signals(atom/pawn) + if(timer_id) + deltimer(timer_id) + timer_id = null + timed_out = FALSE + +/datum/bt_node/decorator/true_for_time/proc/on_timeout() + SIGNAL_HANDLER + timer_id = null + timed_out = TRUE + if(succeed_on_timeout) + return // handled locally by tick(), no plan-wide cancel needed + if(owning_controller) + on_observed_change(owning_controller, null) + +/datum/bt_node/decorator/true_for_time/check_condition(datum/ai_controller/controller) + return !timed_out + +/datum/bt_node/decorator/true_for_time/tick(datum/ai_controller/controller, seconds_per_tick) + if(succeed_on_timeout && timed_out) + if(child_active && child) + child.reset_subtree_tick_states() + child_active = FALSE + return BT_SUCCESS + return ..() diff --git a/code/datums/ai/generic_hunger.bt.json b/code/datums/ai/generic_hunger.bt.json new file mode 100644 index 000000000000..905de0d57b3f --- /dev/null +++ b/code/datums/ai/generic_hunger.bt.json @@ -0,0 +1,43 @@ +{ + "dm_type": "/datum/bt_node/subtree/generic_hunger", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "bb_food_target" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/consume", + "vars": { + "target_key": "bb_food_target", + "hunger_timer_key": "BB_NEXT_HUNGRY" + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "bb_food_target", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up", + "vars": { + "target_key": "bb_food_target", + "drop_held": true + } + } + ] + } + ] + } +} diff --git a/code/datums/ai/generic_play_instrument.bt.json b/code/datums/ai/generic_play_instrument.bt.json new file mode 100644 index 000000000000..b1adb16beee9 --- /dev/null +++ b/code/datums/ai/generic_play_instrument.bt.json @@ -0,0 +1,83 @@ +{ + "dm_type": "/datum/bt_node/subtree/generic_play_instrument", + "bindings": { + "bulu13xf": { + "label": "volume", + "default": "50" + } + }, + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SONG_INSTRUMENT" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_INSTRUMENT_UP_ASS" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_holding_target", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "key": "BB_SONG_INSTRUMENT" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SONG_INSTRUMENT" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up", + "vars": { + "target_key": "BB_SONG_INSTRUMENT" + } + } + ] + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/keep_playing_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT" + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/setup_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT", + "song_lines_key": "BB_SONG_LINES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/play_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT", + "volume": "$bulu13xf" + } + } + ] + } + ] + } +} diff --git a/code/datums/ai/generic_subtrees/basic_find_target.bt.json b/code/datums/ai/generic_subtrees/basic_find_target.bt.json new file mode 100644 index 000000000000..e6dfedebcbb4 --- /dev/null +++ b/code/datums/ai/generic_subtrees/basic_find_target.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/bt_node/subtree/basic_find_target", + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } +} diff --git a/code/datums/ai/generic_subtrees/basic_find_target.dm b/code/datums/ai/generic_subtrees/basic_find_target.dm new file mode 100644 index 000000000000..989688e5a06f --- /dev/null +++ b/code/datums/ai/generic_subtrees/basic_find_target.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/basic_find_target + behavior_tree_json = "code/datums/ai/generic_subtrees/basic_find_target.bt.json" diff --git a/code/datums/ai/generic_subtrees/capricious_pick_target.bt.json b/code/datums/ai/generic_subtrees/capricious_pick_target.bt.json new file mode 100644 index 000000000000..33549460fbb8 --- /dev/null +++ b/code/datums/ai/generic_subtrees/capricious_pick_target.bt.json @@ -0,0 +1,18 @@ +{ + "dm_type": "/datum/bt_node/subtree/capricious_pick_target", + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/capricious_retaliate", + "vars": { + "targeting_strategy": "BB_TARGETING_STRATEGY", + "ignore_faction": true + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pick_retaliate_target" + } + ] +} diff --git a/code/datums/ai/generic_subtrees/capricious_pick_target.dm b/code/datums/ai/generic_subtrees/capricious_pick_target.dm new file mode 100644 index 000000000000..91cd88264b63 --- /dev/null +++ b/code/datums/ai/generic_subtrees/capricious_pick_target.dm @@ -0,0 +1,4 @@ +/// Like [/datum/bt_node/subtree/pick_retaliate_target], but first rolls to randomly aggro on or +/// forgive a nearby mob. Used by animals that pick fights for no reason. +/datum/bt_node/subtree/capricious_pick_target + behavior_tree_json = "code/datums/ai/generic_subtrees/capricious_pick_target.bt.json" diff --git a/code/datums/ai/generic_subtrees/climb_tree.bt.json b/code/datums/ai/generic_subtrees/climb_tree.bt.json new file mode 100644 index 000000000000..0f11ef37f357 --- /dev/null +++ b/code/datums/ai/generic_subtrees/climb_tree.bt.json @@ -0,0 +1,50 @@ +{ + "dm_type": "/datum/bt_node/subtree/climb_tree", + "bindings": { + "bb0lcfol": { + "label": "tree_climbing_cooldown", + "default": "30 SECONDS" + } + }, + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_TREE_CLIMBING_COOLDOWN", + "cooldown_duration": "$bb0lcfol" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CLIMBED_TREE" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CLIMBED_TREE" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_CLIMBED_TREE" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_CLIMBED_TREE" + } + } + ] + } + } +} diff --git a/code/datums/ai/generic_subtrees/climb_tree.dm b/code/datums/ai/generic_subtrees/climb_tree.dm new file mode 100644 index 000000000000..7160d455982d --- /dev/null +++ b/code/datums/ai/generic_subtrees/climb_tree.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/climb_tree + behavior_tree_json = "code/datums/ai/generic_subtrees/climb_tree.bt.json" diff --git a/code/datums/ai/generic_subtrees/find_food.bt.json b/code/datums/ai/generic_subtrees/find_food.bt.json new file mode 100644 index 000000000000..a67032a285fd --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_food.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/bt_node/subtree/find_food", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "cooldown_key": "BB_NEXT_FOOD_EAT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_TARGET_FOOD", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_typed/from_bb_key/basic_foods", + "revalidation_mode": "TARGET_REVALIDATE" + } + } +} diff --git a/code/datums/ai/generic_subtrees/find_food.dm b/code/datums/ai/generic_subtrees/find_food.dm new file mode 100644 index 000000000000..74b2d386f77b --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_food.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/find_food + behavior_tree_json = "code/datums/ai/generic_subtrees/find_food.bt.json" diff --git a/code/datums/ai/generic_subtrees/find_partner.bt.json b/code/datums/ai/generic_subtrees/find_partner.bt.json new file mode 100644 index 000000000000..d48387851e17 --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_partner.bt.json @@ -0,0 +1,32 @@ +{ + "dm_type": "/datum/bt_node/subtree/find_partner", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_FUCKS" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BREED_READY" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_PARTNER_SEARCH_TIMEOUT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_partner", + "vars": { + "target_key": "BB_BABIES_TARGET", + "partner_types_key": "BB_BABIES_PARTNER_TYPES", + "child_types_key": "BB_BABIES_CHILD_TYPES" + } + } + } + } +} diff --git a/code/datums/ai/generic_subtrees/find_partner.dm b/code/datums/ai/generic_subtrees/find_partner.dm new file mode 100644 index 000000000000..a808c199fded --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_partner.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/find_partner + behavior_tree_json = "code/datums/ai/generic_subtrees/find_partner.bt.json" diff --git a/code/datums/ai/generic_subtrees/find_stealable_object.bt.json b/code/datums/ai/generic_subtrees/find_stealable_object.bt.json new file mode 100644 index 000000000000..0176eedb0eb6 --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_stealable_object.bt.json @@ -0,0 +1,88 @@ +{ + "dm_type": "/datum/bt_node/subtree/find_stealable_object", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_dragging", + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance_from_key", + "vars": { + "chance_key": "BB_GUILTY_CONSCIOUS_CHANCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/stop_dragging" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_WANTS_TO_COMMIT_THEFT" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance_from_key", + "vars": { + "chance_key": "BB_STEAL_CHANCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_WANTS_TO_COMMIT_THEFT", + "value": true + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_WANTS_TO_COMMIT_THEFT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_ITEM_TO_STEAL", + "targeting_strategy": "/datum/targeting_strategy/pickup_item/stealable_item", + "target_source": "/datum/target_source/oview", + "must_be_reachable": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_WANTS_TO_COMMIT_THEFT", + "value": false + } + } + ] + } + ] +} diff --git a/code/datums/ai/generic_subtrees/find_stealable_object.dm b/code/datums/ai/generic_subtrees/find_stealable_object.dm new file mode 100644 index 000000000000..4811a5207df2 --- /dev/null +++ b/code/datums/ai/generic_subtrees/find_stealable_object.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/find_stealable_object + behavior_tree_json = "code/datums/ai/generic_subtrees/find_stealable_object.bt.json" diff --git a/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.json b/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.json new file mode 100644 index 000000000000..17a52c4d35d2 --- /dev/null +++ b/code/datums/ai/generic_subtrees/forage_and_retaliate.bt.json @@ -0,0 +1,92 @@ +{ + "dm_type": "/datum/bt_node/subtree/forage_and_retaliate", + "bindings": { + "q8w3rtv1": { + "label": "Food Finder", + "default": "/datum/bt_node/subtree/find_food" + }, + "z4n9bk7p": { + "label": "Retaliate Manager", + "default": "/datum/bt_node/subtree/capricious_pick_target" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_RETALIATE_LIST", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_DISABLE_IDLE", + "invert": true + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + } + ] + }, + { + "type": "subtree", + "subtype": "$z4n9bk7p" + }, + { + "type": "subtree", + "subtype": "$q8w3rtv1" + } + ] +} diff --git a/code/datums/ai/generic_subtrees/forage_and_retaliate.dm b/code/datums/ai/generic_subtrees/forage_and_retaliate.dm new file mode 100644 index 000000000000..291fa033090c --- /dev/null +++ b/code/datums/ai/generic_subtrees/forage_and_retaliate.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/forage_and_retaliate + behavior_tree_json = "code/datums/ai/generic_subtrees/forage_and_retaliate.bt.json" diff --git a/code/datums/ai/generic_subtrees/make_babies.dm b/code/datums/ai/generic_subtrees/make_babies.dm new file mode 100644 index 000000000000..9ed043fb2977 --- /dev/null +++ b/code/datums/ai/generic_subtrees/make_babies.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/make_babies + behavior_tree_json = "code/datums/ai/babies/make_babies.bt.json" diff --git a/code/datums/ai/generic_subtrees/move_to_and_eat.bt.json b/code/datums/ai/generic_subtrees/move_to_and_eat.bt.json new file mode 100644 index 000000000000..652b9f8d376d --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_and_eat.bt.json @@ -0,0 +1,36 @@ +{ + "dm_type": "/datum/bt_node/subtree/move_to_and_eat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_TARGET_FOOD" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_FOOD", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "eats up happily!" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_TARGET_FOOD" + } + } + ] + } +} diff --git a/code/datums/ai/generic_subtrees/move_to_and_eat.dm b/code/datums/ai/generic_subtrees/move_to_and_eat.dm new file mode 100644 index 000000000000..d3b051bd1e03 --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_and_eat.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/move_to_and_eat + behavior_tree_json = "code/datums/ai/generic_subtrees/move_to_and_eat.bt.json" diff --git a/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.json b/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.json new file mode 100644 index 000000000000..b1e027d0dee0 --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_and_hunt.bt.json @@ -0,0 +1,63 @@ +{ + "dm_type": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bvtz06kb": { + "label": "hunting_target", + "default": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "b3y599q4": { + "label": "hunting_target", + "default": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "brrasnah": { + "label": "hunting_target", + "default": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "bqwjf4id": { + "label": "hunt_cooldown", + "default": "5 SECONDS" + }, + "bm3y5m55": { + "label": "cooldown_key", + "default": "" + }, + "bd1towgc": { + "label": "behavior_combat_mode", + "default": "TRUE" + }, + "b3cnse9r": { + "label": "always_reset_target", + "default": "TRUE" + } + }, + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "$bvtz06kb" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "$b3y599q4", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "$brrasnah", + "always_reset_target": "$b3cnse9r", + "hunt_cooldown": "$bqwjf4id", + "cooldown_key": "$bm3y5m55", + "behavior_combat_mode": "$bd1towgc" + } + } + ] + } +} diff --git a/code/datums/ai/generic_subtrees/move_to_and_hunt.dm b/code/datums/ai/generic_subtrees/move_to_and_hunt.dm new file mode 100644 index 000000000000..428ca9db2357 --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_and_hunt.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/move_to_and_hunt + behavior_tree_json = "code/datums/ai/generic_subtrees/move_to_and_hunt.bt.json" diff --git a/code/datums/ai/generic_subtrees/move_to_reinforce.bt.json b/code/datums/ai/generic_subtrees/move_to_reinforce.bt.json new file mode 100644 index 000000000000..08bad36b3f4b --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_reinforce.bt.json @@ -0,0 +1,30 @@ +{ + "dm_type": "/datum/bt_node/subtree/move_to_reinforce", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_BASIC_MOB_REINFORCEMENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BASIC_MOB_REINFORCEMENT_TARGET", + "required_dist": 0, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_BASIC_MOB_REINFORCEMENT_TARGET" + } + } + ] + } +} diff --git a/code/datums/ai/generic_subtrees/move_to_reinforce.dm b/code/datums/ai/generic_subtrees/move_to_reinforce.dm new file mode 100644 index 000000000000..3a3a95c47eff --- /dev/null +++ b/code/datums/ai/generic_subtrees/move_to_reinforce.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/move_to_reinforce + behavior_tree_json = "code/datums/ai/generic_subtrees/move_to_reinforce.bt.json" diff --git a/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.json b/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.json new file mode 100644 index 000000000000..c1bef09b791f --- /dev/null +++ b/code/datums/ai/generic_subtrees/pick_retaliate_target.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/bt_node/subtree/pick_retaliate_target", + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } +} diff --git a/code/datums/ai/generic_subtrees/pick_retaliate_target.dm b/code/datums/ai/generic_subtrees/pick_retaliate_target.dm new file mode 100644 index 000000000000..e4e52f3d10ca --- /dev/null +++ b/code/datums/ai/generic_subtrees/pick_retaliate_target.dm @@ -0,0 +1,3 @@ +/// Picks a target from our retaliate list (real attackers only). +/datum/bt_node/subtree/pick_retaliate_target + behavior_tree_json = "code/datums/ai/generic_subtrees/pick_retaliate_target.bt.json" diff --git a/code/datums/ai/generic_subtrees/random_walk.bt.json b/code/datums/ai/generic_subtrees/random_walk.bt.json new file mode 100644 index 000000000000..1ff4af0b089e --- /dev/null +++ b/code/datums/ai/generic_subtrees/random_walk.bt.json @@ -0,0 +1,22 @@ +{ + "dm_type": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": { + "label": "walk_chance", + "default": "25" + } + }, + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk", + "vars": { + "walk_chance": "$bf07i8ep" + } + } + ] +} diff --git a/code/datums/ai/generic_subtrees/random_walk.dm b/code/datums/ai/generic_subtrees/random_walk.dm new file mode 100644 index 000000000000..f4160f87ed0c --- /dev/null +++ b/code/datums/ai/generic_subtrees/random_walk.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/random_walk + behavior_tree_json = "code/datums/ai/generic_subtrees/random_walk.bt.json" diff --git a/code/datums/ai/generic_subtrees/skittish_and_speak.bt.json b/code/datums/ai/generic_subtrees/skittish_and_speak.bt.json new file mode 100644 index 000000000000..9899a48b230a --- /dev/null +++ b/code/datums/ai/generic_subtrees/skittish_and_speak.bt.json @@ -0,0 +1,32 @@ +{ + "dm_type": "/datum/bt_node/subtree/skittish_and_speak", + "bindings": { + "f17iiafz": { + "label": "Speech Behavior", + "default": "/datum/bt_node/ai_behavior/random_speech_blackboard" + } + }, + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_skittish_combat" + } + ] + }, + { + "type": "leaf", + "behavior": "$f17iiafz" + } + ] +} diff --git a/code/datums/ai/generic_subtrees/skittish_and_speak.dm b/code/datums/ai/generic_subtrees/skittish_and_speak.dm new file mode 100644 index 000000000000..6bef54057e63 --- /dev/null +++ b/code/datums/ai/generic_subtrees/skittish_and_speak.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/skittish_and_speak + behavior_tree_json = "code/datums/ai/generic_subtrees/skittish_and_speak.bt.json" diff --git a/code/datums/ai/generic_subtrees/steal_and_flee.bt.json b/code/datums/ai/generic_subtrees/steal_and_flee.bt.json new file mode 100644 index 000000000000..922f09d1d090 --- /dev/null +++ b/code/datums/ai/generic_subtrees/steal_and_flee.bt.json @@ -0,0 +1,43 @@ +{ + "dm_type": "/datum/bt_node/subtree/steal_and_flee", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_ITEM_TO_STEAL" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ITEM_TO_STEAL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/drag_target", + "vars": { + "target_key": "BB_ITEM_TO_STEAL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/copy_bb_key", + "vars": { + "dest_key": "BB_LAST_STOLEN_ITEM", + "source_key": "BB_ITEM_TO_STEAL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_ITEM_TO_STEAL" + } + } + ] + } +} diff --git a/code/datums/ai/generic_subtrees/steal_and_flee.dm b/code/datums/ai/generic_subtrees/steal_and_flee.dm new file mode 100644 index 000000000000..e20201f1fdd4 --- /dev/null +++ b/code/datums/ai/generic_subtrees/steal_and_flee.dm @@ -0,0 +1,2 @@ +/datum/bt_node/subtree/steal_and_flee + behavior_tree_json = "code/datums/ai/generic_subtrees/steal_and_flee.bt.json" diff --git a/code/datums/ai/hauntium/haunted.bt.json b/code/datums/ai/hauntium/haunted.bt.json new file mode 100644 index 000000000000..88cad7cc215a --- /dev/null +++ b/code/datums/ai/hauntium/haunted.bt.json @@ -0,0 +1,107 @@ +{ + "dm_type": "/datum/ai_controller/haunted", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/item_being_held", + "vars": { + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LIKES_EQUIPPER", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": 0 + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/item_escape_grasp" + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_HAUNT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "2 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/throw_attack/haunted", + "vars": { + "target_key": "BB_HAUNT_TARGET", + "throw_count_key": "BB_HAUNTED_THROW_ATTEMPT_COUNT", + "haunt_list_key": "BB_TO_HAUNT_LIST" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HAUNT_TARGET", + "required_dist": 3, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_ghost_item" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_haunt_target", + "vars": { + "target_key": "BB_HAUNT_TARGET", + "haunt_list_key": "BB_TO_HAUNT_LIST" + } + } + ] + } + ] +} diff --git a/code/datums/ai/hauntium/haunted_bt_nodes.dm b/code/datums/ai/hauntium/haunted_bt_nodes.dm new file mode 100644 index 000000000000..576f37b8f386 --- /dev/null +++ b/code/datums/ai/hauntium/haunted_bt_nodes.dm @@ -0,0 +1,83 @@ +/// Gates on whether the haunted item pawn is currently inside a mob's inventory. +/datum/bt_node/decaorator/item_being_held + +/datum/bt_node/decorator/item_being_held/check_condition(datum/ai_controller/controller) + return ismob(controller.pawn.loc) + +/datum/bt_node/decorator/item_being_held/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(COMSIG_ITEM_ENTERED_HANDS, COMSIG_ITEM_DROPPED), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/item_being_held/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(COMSIG_ITEM_ENTERED_HANDS, COMSIG_ITEM_DROPPED)) + +/** + * Attempts to slip out of the holder's hands with a per-tick probability. + * Returns RUNNING while waiting for the chance to fire; SUCCEEDED after escaping. + */ +/datum/bt_node/ai_behavior/item_escape_grasp + +/datum/bt_node/ai_behavior/item_escape_grasp/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(HAUNTED_ITEM_ESCAPE_GRASP_CHANCE, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT + var/obj/item/item_pawn = controller.pawn + var/mob/item_holder = item_pawn.loc + if(!ismob(item_holder)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + item_pawn.visible_message(span_warning("[item_pawn] slips out of the hands of [item_holder]!")) + item_holder.dropItemToGround(item_pawn, TRUE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +///Find someone to haunt +/datum/bt_node/ai_behavior/pick_haunt_target + var/target_key + var/haunt_list_key + +/datum/bt_node/ai_behavior/pick_haunt_target/perform(seconds_per_tick, datum/ai_controller/controller) + if(!prob(HAUNTED_ITEM_ATTACK_HAUNT_CHANCE)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + var/obj/item/item_pawn = controller.pawn + var/list/to_haunt = controller.blackboard[haunt_list_key] + for(var/mob/living/candidate as anything in to_haunt) + if(QDELETED(candidate) || to_haunt[candidate] <= 0) + controller.remove_thing_from_blackboard_key(haunt_list_key, candidate) + continue + if(get_dist(candidate, item_pawn) <= CURSED_VIEW_RANGE) + controller.set_blackboard_key(target_key, candidate) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + +/// Haunted variant: decrements haunt list aggro when throws are exhausted. +/// BT args: target_key, throw_count_key, haunt_list_key +/datum/bt_node/ai_behavior/throw_attack/haunted + var/haunt_list_key + max_attempts = HAUNTED_MAX_THROW_ATTEMPTS + /// Cached from perform args so on_throws_exhausted can access haunt_list_key. + var/active_haunt_list_key + +/datum/bt_node/ai_behavior/throw_attack/haunted/perform(seconds_per_tick, datum/ai_controller/controller) + active_haunt_list_key = haunt_list_key + return ..() + +/datum/bt_node/ai_behavior/throw_attack/haunted/on_throws_exhausted(datum/ai_controller/controller, atom/throw_target, target_key, throw_count_key) + controller.add_blackboard_key_assoc(active_haunt_list_key, throw_target, -1) + return ..() + + +///Teleport every now and then +/datum/bt_node/ai_behavior/idle_ghost_item + ///Chance for item to teleport somewhere else + var/teleport_chance = 4 + time_between_perform = 1 SECONDS + +/datum/bt_node/ai_behavior/idle_ghost_item/perform(seconds_per_tick, datum/ai_controller/controller) + . = ..() + var/obj/item/item_pawn = controller.pawn + if(ismob(item_pawn.loc)) //Being held. dont teleport + return AI_BEHAVIOR_INSTANT + if(SPT_PROB(teleport_chance, seconds_per_tick)) + playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) + #ifndef UNIT_TESTS // hauntium teleports can cause mapping nearstation tests to fail if it teleports outside an area + do_teleport(item_pawn, get_turf(item_pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) + #endif + return AI_BEHAVIOR_INSTANT diff --git a/code/datums/ai/hauntium/haunted_controller.dm b/code/datums/ai/hauntium/haunted_controller.dm index b2863a310c16..45388702fc74 100644 --- a/code/datums/ai/hauntium/haunted_controller.dm +++ b/code/datums/ai/hauntium/haunted_controller.dm @@ -1,5 +1,6 @@ /datum/ai_controller/haunted + behavior_tree_json = "code/datums/ai/hauntium/haunted.bt.json" movement_delay = 0.4 SECONDS blackboard = list( BB_TO_HAUNT_LIST = list(), @@ -7,8 +8,6 @@ BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT, ) - planning_subtrees = list(/datum/ai_planning_subtree/haunted) - idle_behavior = /datum/idle_behavior/idle_ghost_item /datum/ai_controller/haunted/TryPossessPawn(atom/new_pawn) if(!isitem(new_pawn)) diff --git a/code/datums/ai/hauntium/hauntium_subtrees.dm b/code/datums/ai/hauntium/hauntium_subtrees.dm deleted file mode 100644 index 57b5d2ae7e07..000000000000 --- a/code/datums/ai/hauntium/hauntium_subtrees.dm +++ /dev/null @@ -1,24 +0,0 @@ -/datum/ai_planning_subtree/haunted/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/obj/item/item_pawn = controller.pawn - - if(ismob(item_pawn.loc)) //We're being held, maybe escape? - if(controller.blackboard[BB_LIKES_EQUIPPER])//don't unequip from people it's okay with - return - if(SPT_PROB(HAUNTED_ITEM_ESCAPE_GRASP_CHANCE, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/item_escape_grasp) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!SPT_PROB(HAUNTED_ITEM_ATTACK_HAUNT_CHANCE, seconds_per_tick)) - return - - var/list/to_haunt_list = controller.blackboard[BB_TO_HAUNT_LIST] - - for(var/mob/living/haunt_target as anything in to_haunt_list) - if(to_haunt_list[haunt_target] <= 0) - controller.remove_thing_from_blackboard_key(BB_TO_HAUNT_LIST, haunt_target) - continue - - if(get_dist(haunt_target, item_pawn) <= 7) - controller.set_blackboard_key(BB_HAUNT_TARGET, haunt_target) - controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT) - return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm deleted file mode 100644 index 1095919c058b..000000000000 --- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Tells the AI to find a certain target nearby to hunt. - * If a target has been found, we will start to move towards it, and eventually attack it. - */ -/datum/ai_planning_subtree/find_and_hunt_target - /// What key in the blacbkboard do we store our hunting target? - /// If you want to have multiple hunting behaviors on a controller be sure that this is unique - var/target_key = BB_CURRENT_HUNTING_TARGET - /// What behavior to execute if we have no target - var/finding_behavior = /datum/ai_behavior/find_hunt_target - /// What behavior to execute if we do have a target - var/hunting_behavior = /datum/ai_behavior/hunt_target - /// What targets we're hunting for - var/list/hunt_targets - /// In what radius will we hunt - var/hunt_range = 2 - /// What are the chances we hunt something at any given moment - var/hunt_chance = 100 - ///do we finish planning subtree - var/finish_planning = TRUE - -/datum/ai_planning_subtree/find_and_hunt_target/New() - . = ..() - hunt_targets = typecacheof(hunt_targets) - -/datum/ai_planning_subtree/find_and_hunt_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(hunt_chance, seconds_per_tick)) - return - - if(controller.blackboard[BB_HUNTING_COOLDOWN(type)] >= world.time) - return - - if(!controller.blackboard_key_exists(target_key)) - controller.queue_behavior(finding_behavior, target_key, hunt_targets, hunt_range) - return - - // We ARE hunting something, execute the hunt. - // Note that if our AI controller has multiple hunting subtrees set, - // we may accidentally be executing another tree's hunt - not ideal, - // try to set a unique target key if you have multiple - - controller.queue_behavior(hunting_behavior, target_key, BB_HUNTING_COOLDOWN(type)) - if(finish_planning) - return SUBTREE_RETURN_FINISH_PLANNING //If we're hunting we're too busy for anything else - -/// Finds a specific atom type to hunt. -/datum/ai_behavior/find_hunt_target - ///is this only meant to search for turf types? - var/search_turf_types = FALSE - -/datum/ai_behavior/find_hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range) - var/mob/living/living_mob = controller.pawn - var/list/interesting_objects = search_turf_types ? RANGE_TURFS(hunt_range, living_mob) : oview(hunt_range, living_mob) - for(var/atom/possible_dinner as anything in typecache_filter_list(interesting_objects, types_to_hunt)) - if(!valid_dinner(living_mob, possible_dinner, hunt_range, controller, seconds_per_tick)) - continue - controller.set_blackboard_key(hunting_target_key, possible_dinner) - EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [possible_dinner] as a target for blackboard key [hunting_target_key]! Behavior: [src]", get_turf(possible_dinner), "Target: [possible_dinner]") - EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(possible_dinner)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/find_hunt_target/proc/valid_dinner(mob/living/source, atom/dinner, radius, datum/ai_controller/controller, seconds_per_tick) - if(isliving(dinner)) - var/mob/living/living_target = dinner - if(living_target.stat == DEAD) //bitch is dead - return FALSE - - return can_see(source, dinner, radius) - -/datum/ai_behavior/find_hunt_target/search_turf_types - search_turf_types = TRUE - -/// Hunts down a specific atom type. -/datum/ai_behavior/hunt_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - /// How long do we have to wait after a successful hunt? - var/hunt_cooldown = 5 SECONDS - /// Do we reset the target after attacking something, so we can check for status changes. - var/always_reset_target = FALSE - - -/datum/ai_behavior/hunt_target/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) - . = ..() - var/atom/hunt_target = controller.blackboard[hunting_target_key] - if (isnull(hunt_target)) - return FALSE - set_movement_target(controller, hunt_target) - -/datum/ai_behavior/hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) - var/mob/living/hunter = controller.pawn - var/atom/hunted = controller.blackboard[hunting_target_key] - - if(QDELETED(hunted)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - target_caught(hunter, hunted) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/hunt_target/proc/target_caught(mob/living/hunter, atom/hunted) - if(isliving(hunted)) // Are we hunting a living mob? - var/mob/living/living_target = hunted - hunter.manual_emote("chomps [living_target]!") - living_target.investigate_log("has been killed by [key_name(hunter)].", INVESTIGATE_DEATHS) - living_target.death() - - else if(IS_EDIBLE(hunted)) - hunted.attack_animal(hunter) - - else // We're hunting an object, and should delete it instead of killing it. Mostly useful for decal bugs like ants or spider webs. - hunter.manual_emote("chomps [hunted]!") - qdel(hunted) - -/datum/ai_behavior/hunt_target/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key) - . = ..() - if(succeeded && hunting_cooldown_key) - controller.set_blackboard_key(hunting_cooldown_key, world.time + hunt_cooldown) - else if(hunting_target_key) - controller.clear_blackboard_key(hunting_target_key) - if(always_reset_target && hunting_target_key) - controller.clear_blackboard_key(hunting_target_key) - -/datum/ai_behavior/hunt_target/interact_with_target - ///what combat mode should we use to interact with - var/behavior_combat_mode = TRUE - -/datum/ai_behavior/hunt_target/interact_with_target/target_caught(mob/living/hunter, obj/structure/cable/hunted) - var/datum/ai_controller/controller = hunter.ai_controller - controller.ai_interact(target = hunted, combat_mode = behavior_combat_mode) - -/datum/ai_behavior/hunt_target/interact_with_target/combat_mode_off - behavior_combat_mode = FALSE - -/datum/ai_behavior/hunt_target/interact_with_target/reset_target - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off - always_reset_target = TRUE - behavior_combat_mode = FALSE - -/datum/ai_behavior/hunt_target/use_ability_on_target - always_reset_target = TRUE - ///the ability we will use - var/ability_key - -/datum/ai_behavior/hunt_target/use_ability_on_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) - var/datum/action/cooldown/ability = controller.blackboard[ability_key] - if(!ability?.IsAvailable()) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - return ..() - -/datum/ai_behavior/hunt_target/use_ability_on_target/target_caught(mob/living/hunter, atom/hunted) - var/datum/action/cooldown/ability = hunter.ai_controller.blackboard[ability_key] - ability.InterceptClickOn(hunter, null, hunted) - - -/datum/ai_behavior/hunt_target/latch_onto - -/datum/ai_behavior/hunt_target/latch_onto/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) - . = ..() - var/mob/living/living_pawn = controller.pawn - if(living_pawn.buckled) - return FALSE - -/datum/ai_behavior/hunt_target/latch_onto/target_caught(mob/living/hunter, obj/hunted) - if(hunter.buckled) - return FALSE - if(!hunted.buckle_mob(hunter, force = TRUE)) - return FALSE - hunted.visible_message(span_notice("[hunted] has been latched onto by [hunter]!")) - return TRUE diff --git a/code/datums/ai/hunting_behavior/hunting_cockroach.dm b/code/datums/ai/hunting_behavior/hunting_cockroach.dm deleted file mode 100644 index a786342bfa0c..000000000000 --- a/code/datums/ai/hunting_behavior/hunting_cockroach.dm +++ /dev/null @@ -1,2 +0,0 @@ -/datum/ai_planning_subtree/find_and_hunt_target/roach - hunt_targets = list(/obj/effect/decal/cleanable/ants) diff --git a/code/datums/ai/hunting_behavior/hunting_corpses.dm b/code/datums/ai/hunting_behavior/hunting_corpses.dm deleted file mode 100644 index 89d100263fb1..000000000000 --- a/code/datums/ai/hunting_behavior/hunting_corpses.dm +++ /dev/null @@ -1,17 +0,0 @@ -/// Find and attack corpses -/datum/ai_planning_subtree/find_and_hunt_target/corpses - finding_behavior = /datum/ai_behavior/find_hunt_target/corpses - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target - hunt_targets = list(/mob/living) - -/// Find nearby dead mobs -/datum/ai_behavior/find_hunt_target/corpses - -/datum/ai_behavior/find_hunt_target/corpses/valid_dinner(mob/living/source, mob/living/dinner, radius) - if (!isliving(dinner) || dinner.stat != DEAD) - return FALSE - return can_see(source, dinner, radius) - -/// Find and attack specifically human corpses -/datum/ai_planning_subtree/find_and_hunt_target/corpses/human - hunt_targets = list(/mob/living/carbon/human) diff --git a/code/datums/ai/hunting_behavior/hunting_lights.dm b/code/datums/ai/hunting_behavior/hunting_lights.dm deleted file mode 100644 index 5062a8aaf929..000000000000 --- a/code/datums/ai/hunting_behavior/hunting_lights.dm +++ /dev/null @@ -1,18 +0,0 @@ -/datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/light_fixtures - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/light_fixtures - hunt_targets = list(/obj/machinery/light) - hunt_range = 7 - -/datum/ai_behavior/hunt_target/interact_with_target/light_fixtures - hunt_cooldown = 10 SECONDS - always_reset_target = TRUE - -/datum/ai_behavior/find_hunt_target/light_fixtures - -/datum/ai_behavior/find_hunt_target/light_fixtures/valid_dinner(mob/living/source, obj/machinery/light/dinner, radius) - if(dinner.status == LIGHT_BROKEN) //light is already broken - return FALSE - - return can_see(source, dinner, radius) diff --git a/code/datums/ai/hunting_behavior/hunting_mouse.dm b/code/datums/ai/hunting_behavior/hunting_mouse.dm deleted file mode 100644 index 3bcee20b79ea..000000000000 --- a/code/datums/ai/hunting_behavior/hunting_mouse.dm +++ /dev/null @@ -1,55 +0,0 @@ -// Mouse subtree to hunt down delicious cheese. -/datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse - hunt_targets = list(/obj/item/food/cheese) - hunt_range = 1 - -// Mouse subtree to hunt down ... delicious cabling? -/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse - finding_behavior = /datum/ai_behavior/find_hunt_target/mouse_cable - hunt_targets = list(/obj/structure/cable) - hunt_range = 0 // Only look below us - hunt_chance = 1 - -// When looking for a cable, we can only bite things we can reach. -/datum/ai_behavior/find_hunt_target/mouse_cable - -/datum/ai_behavior/find_hunt_target/mouse_cable/valid_dinner(mob/living/source, obj/structure/cable/dinner, radius) - . = ..() - if(!.) - return - - var/turf/open/floor/below_the_cable = get_turf(dinner) - if(!istype(below_the_cable)) - return FALSE - - return below_the_cable.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE - -// Our hunts have a decent cooldown. -/datum/ai_behavior/hunt_target/interact_with_target/mouse - hunt_cooldown = 20 SECONDS - -/datum/ai_planning_subtree/approach_synthesizer - -/datum/ai_planning_subtree/approach_synthesizer/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/pawn = controller.pawn - var/atom/instrument = controller.blackboard[BB_SONG_INSTRUMENT] - if(!isnull(instrument)) - if (!isturf(instrument.loc) || !can_see(pawn, instrument)) - controller.clear_blackboard_key(BB_SONG_INSTRUMENT) - return - if (instrument.IsReachableBy(pawn)) - return - controller.queue_behavior(/datum/ai_behavior/travel_towards/adjacent, BB_SONG_INSTRUMENT) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/piano_synth, BB_SONG_INSTRUMENT, /obj/item/instrument/piano_synth) - -/datum/ai_behavior/find_and_set/piano_synth // Disinclude subtypes - -/datum/ai_behavior/find_and_set/piano_synth/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/obj/item/instrument/piano_synth/synth in oview(search_range, controller.pawn)) - if(synth.type == /obj/item/instrument/piano_synth) - return synth diff --git a/code/datums/ai/idle_behaviors/_idle_behavior.dm b/code/datums/ai/idle_behaviors/_idle_behavior.dm deleted file mode 100644 index bacb8e7cdf37..000000000000 --- a/code/datums/ai/idle_behaviors/_idle_behavior.dm +++ /dev/null @@ -1,5 +0,0 @@ -/datum/idle_behavior - -/datum/idle_behavior/proc/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - set waitfor = FALSE - SHOULD_CALL_PARENT(TRUE) diff --git a/code/datums/ai/idle_behaviors/idle_dog.dm b/code/datums/ai/idle_behaviors/idle_dog.dm deleted file mode 100644 index 4d036e9a7a5d..000000000000 --- a/code/datums/ai/idle_behaviors/idle_dog.dm +++ /dev/null @@ -1,21 +0,0 @@ -///Dog specific idle behavior. -/datum/idle_behavior/idle_dog/perform_idle_behavior(seconds_per_tick, datum/ai_controller/basic_controller/dog/controller) - . = ..() - var/mob/living/living_pawn = controller.pawn - if(!isturf(living_pawn.loc) || living_pawn.pulledby) - return - - var/obj/item/carry_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM] - // if we're just ditzing around carrying something, occasionally print a message so people know we have something - if(carry_item && SPT_PROB(5, seconds_per_tick)) - living_pawn.visible_message(span_notice("[living_pawn] gently teethes on \the [carry_item] in [living_pawn.p_their()] mouth."), vision_distance=COMBAT_MESSAGE_RANGE) - - // Custom movement rate, for old corgis, etc. - var/move_chance = controller.blackboard[BB_DOG_IS_SLOW] ? 2.5 : 5 - - if(SPT_PROB(move_chance, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE)) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - else if(SPT_PROB(2, seconds_per_tick)) - living_pawn.manual_emote(pick("dances around.", "chases [living_pawn.p_their()] tail!")) - living_pawn.AddComponent(/datum/component/spinny) diff --git a/code/datums/ai/idle_behaviors/idle_haunted.dm b/code/datums/ai/idle_behaviors/idle_haunted.dm deleted file mode 100644 index 756adae93134..000000000000 --- a/code/datums/ai/idle_behaviors/idle_haunted.dm +++ /dev/null @@ -1,15 +0,0 @@ -///If not held, teleport somewhere else -/datum/idle_behavior/idle_ghost_item - ///Chance for item to teleport somewhere else - var/teleport_chance = 4 - -/datum/idle_behavior/idle_ghost_item/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - var/obj/item/item_pawn = controller.pawn - if(ismob(item_pawn.loc)) //Being held. dont teleport - return - if(SPT_PROB(teleport_chance, seconds_per_tick)) - playsound(item_pawn.loc, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE) - #ifndef UNIT_TESTS // hauntium teleports can cause mapping nearstation tests to fail if it teleports outside an area - do_teleport(item_pawn, get_turf(item_pawn), 4, channel = TELEPORT_CHANNEL_MAGIC) - #endif diff --git a/code/datums/ai/idle_behaviors/idle_monkey.dm b/code/datums/ai/idle_behaviors/idle_monkey.dm deleted file mode 100644 index c32534dce529..000000000000 --- a/code/datums/ai/idle_behaviors/idle_monkey.dm +++ /dev/null @@ -1,37 +0,0 @@ -/datum/idle_behavior/idle_monkey - ///Emotes that will be played commonly during idle behavior. - var/list/common_emotes = list( - "screech", - "roar", - ) - ///Emotes that will be played rarely during idle behavior. - var/list/rare_emotes = list( - "scratch", - "jump", - "roll", - "tail", - ) - -/datum/idle_behavior/idle_monkey/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - var/mob/living/living_pawn = controller.pawn - - if(SPT_PROB(25, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - else if(SPT_PROB(5, seconds_per_tick)) - INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(common_emotes)) - else if(SPT_PROB(1, seconds_per_tick)) - INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(rare_emotes)) - -/datum/idle_behavior/idle_monkey/pun_pun - common_emotes = list( - "tunesing", - "dance", - "bow", - ) - rare_emotes = list( - "clear", - "sign", - "tail", - ) diff --git a/code/datums/ai/idle_behaviors/idle_random_walk.dm b/code/datums/ai/idle_behaviors/idle_random_walk.dm deleted file mode 100644 index bedcb12e5c72..000000000000 --- a/code/datums/ai/idle_behaviors/idle_random_walk.dm +++ /dev/null @@ -1,86 +0,0 @@ -/datum/idle_behavior/idle_random_walk - ///Chance that the mob random walks per second - var/walk_chance = 25 - -/datum/idle_behavior/idle_random_walk/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - var/mob/living/living_pawn = controller.pawn - if(LAZYLEN(living_pawn.do_afters)) - return FALSE - - var/actual_chance = controller.blackboard[BB_BASIC_MOB_IDLE_WALK_CHANCE] || walk_chance - if(SPT_PROB(actual_chance, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) - var/move_dir = pick(GLOB.alldirs) - var/turf/destination_turf = get_step(living_pawn, move_dir) - if(!destination_turf?.can_cross_safely(living_pawn)) - return FALSE - living_pawn.Move(destination_turf, move_dir) - return TRUE - -/datum/idle_behavior/idle_random_walk/less_walking - walk_chance = 10 - -/// Only walk if we don't have a target -/datum/idle_behavior/idle_random_walk/no_target - /// Where do we look for a target? - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - -/datum/idle_behavior/idle_random_walk/no_target/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - if (!controller.blackboard_key_exists(target_key)) - return - return ..() - -/// Only walk if we are not on the target's location -/datum/idle_behavior/idle_random_walk/not_while_on_target - ///What is the spot we have to stand on? - var/target_key - -/datum/idle_behavior/idle_random_walk/not_while_on_target/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - var/atom/target = controller.blackboard[target_key] - - //Don't move, if we are are already standing on it - if(!QDELETED(target) && ((isturf(target) && controller.pawn.loc == target) || (target.loc == controller.pawn.loc))) - return - - return ..() - -/// walk randomly however stick near a target -/datum/idle_behavior/walk_near_target - /// chance to walk - var/walk_chance = 25 - /// distance we are to target - var/minimum_distance = 20 - /// key that holds target - var/target_key - -/datum/idle_behavior/walk_near_target/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - var/mob/living/living_pawn = controller.pawn - if(LAZYLEN(living_pawn.do_afters)) - return - - if(!SPT_PROB(walk_chance, seconds_per_tick) || !(living_pawn.mobility_flags & MOBILITY_MOVE) || !isturf(living_pawn.loc) || living_pawn.pulledby) - return - - var/atom/target = controller.blackboard[target_key] - var/distance = get_dist(target, living_pawn) - if(isnull(target) || distance > minimum_distance) //if we are too far away from target, just walk randomly - var/move_dir = pick(GLOB.alldirs) - living_pawn.Move(get_step(living_pawn, move_dir), move_dir) - return - - var/list/possible_turfs = list() - for(var/direction in GLOB.alldirs) - var/turf/possible_step = get_step(living_pawn, direction) - if(get_dist(possible_step, target) > minimum_distance) - continue - if(possible_step.is_blocked_turf() || !possible_step.can_cross_safely(living_pawn)) - continue - possible_turfs += possible_step - - if(!length(possible_turfs)) - return - - var/turf/picked_turf = pick(possible_turfs) - - living_pawn.Move(picked_turf, get_dir(living_pawn, picked_turf)) diff --git a/code/datums/ai/learn_ai.md b/code/datums/ai/learn_ai.md index 5fe38924489a..4da35d60000e 100644 --- a/code/datums/ai/learn_ai.md +++ b/code/datums/ai/learn_ai.md @@ -1,139 +1,246 @@ -# Learn AI +# Learn Behavior Tree AI -In ye olde days, we designed mob AI, and we built it into simple animals as they were the "non player controlled" mobs. Made sense at the time. But by coding AI directly into the mob, there was so little ability to make unique or complicated AI, and even when it was pulled off the code was hacky and non-reusable. the datum AI system was made to rectify these problems, and expand AI beyond just mobs. +This file covers how the behavior tree system works in /tg/station, and how to use it in practice. -## AI Controllers Attach +## Disclaimer -Any atom can have an AI controller, I'm choosing a basic mob for this guide, because basic mobs stand as a nice "blank canvas" for AI on mobs. Simple animals come with AI built into the mob, basic mobs don't, which is great for us adding AI on top of it. +I strongly reccomend you install the "BehaviorTreeG" extension before continueing, as it is the intended way of editing behavior trees. In theory you can manually edit them, but this is strongly reccomended against due to the nature of the structure of behavior trees. -Anyways, we just define the type of AI this mob has on the ai_controller var. It starts as a type, but is turned into an instance once the mob is instantiated. +## What are behavior trees? -```dm -/mob/living/basic/butterfly - name = "butterfly" - desc = "A colorful butterfly, how'd it get up here?" - // a lot more variables defining for us what a butterfly is +Behavior trees are a common pattern in game AI in which you build your AI out of a tree made out of nodes. These nodes structure and conditionalize AI behavior in a readable way. - ai_controller = /datum/ai_controller/basic/butterfly -``` +The behavior tree runs left to right, allowing you to put high priority behavior first, and only running low priority behavior later. It also lets you sequence a series of behaviors after each other to create sensible sequences (e.g. grab key, unlock door, move through door) -## Controllers Themselves +## Blackboard First -First, let's look at the blackboard. +The blackboard is the controller's shared memory. It makes use of an associative list to allow arbitrary keys to be assigned values. -```dm -/datum/ai_controller/basic/cow - blackboard = list( - BB_TARGETING_STRATEGY = new /datum/targeting_strategy/basic/allow_items(), - BB_BASIC_MOB_TIP_REACTING = FALSE, - BB_BASIC_MOB_TIPPER = null, - ) -``` +- Nodes read keys from it to make decisions. +- Nodes write keys to it so later nodes can use those results. +- Keys are usually things like "current target", "current objective", "cooldowns", or "flags". -Think of the blackboard as the unique format for variables. They are set initially, or by behaviors, **but never in subtrees.** Because we check `blackboard[BB_SOME_KEY]` instead of a variable, we can wipe out variables and slap new ones onto the AI as it runs. For example, this cow uses BB_BASIC_MOB_TIP_REACTING and BB_BASIC_MOB_TIPPER because cows can get tipped, and the AI needs to know that in the subtrees when it plans behavior. And in fact, those two keys aren't required to be defined initially, it's just for clarity that they are. +In essence, these are just variables. But by storing them in an associative list we do not have to have define actual variables, and our AI code can be made out of modular pieces and does not need to rely on hardcoded variables on the controller. -Speaking of subtrees, let's look at that now. +The keys for the blackboard are simple string defines: -```dm -/datum/ai_controller/basic/cow +example: +`#define BB_CURRENT_TARGET "Current Target"` - planning_subtrees = list( - /datum/ai_planning_subtree/tip_reaction, //<- goes first - /datum/ai_planning_subtree/find_and_eat_food, //<- goes second - /datum/ai_planning_subtree/random_speech/cow, //<- goes last! But at any point, a previous subtree can end the chain. If a cow is tipped over, it shouldn't make random noises or try finding food! - ) - //and by the end for however many subtrees ran, each one that did may have planned behavior for the AI to act on. -``` +After this is done, you can start using the key in your behavior tree; by setting it somewhere in the tree it will automatically be added to the blackboard. -AI's work by planning specific behaviors, and subtrees are datums that bundle the planning of behavior together. From top to bottom they run, and they can cancel future subtrees. As an example, cows have their very first consideration be tip_reaction, a subtree that prevents further subtrees like eating food and random speech, as well as planning out how the cow reacts (looking sad at the person who tipped it). +Example flow (in psuedocode): -```dm -/datum/ai_controller/basic/cow - ai_traits = null - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = null +```text +FIND TARGET LEAF: + set BB_CURRENT_TARGET +ATTACK BRANCH: + if BB_CURRENT_TARGET is set + move toward target + attack target ``` -Finally, we have some more minor things. +## Behavior Tree Node -- ai_traits are flags for the AI, things like "STOP_MOVING_WHEN_PULLED" slightly modifying how the AI acts under some situations. -- ai_movement is how the mob moves to its movement target. ranges from simple behaviors like ai_movement/dumb that awlays move in the direction of the target and hope there's nothing in the way, all the way to ai_movement/jps that plans and occasionally recalcuates more complicated paths, at the cost of more lag. -- idle_behavior is just some simpler behavior to perform when nothing has been planned at all, like idle_behavior/idle_random_walk making a mob wander passively. +This is the parent type of all other nodes in this guide. And when active they will return one of three values: -## Subtrees and Behaviors +1. BT_SUCCESS - This node succeeded, and will report this to its parent node +2. BT_FAILURE - This node failed, and will report this to its parent node +3. BT_RUNNING - This node is still running, and will report this to its parent node. -Okay, so we have blackboard variables, which are considered by subtrees to plan behaviors. Let's actually look at a subtree planning behaviors, and behaviors themselves. +This is important for the flow of the behavior tree, as different nodes will behave differently depending on whether their children succeed or fail. -```dm -/// this subtree checks if the mob has a target. if it doesn't, it plans looking for food. if it does, it tries to eat the food via attacking it. -/datum/ai_planning_subtree/find_and_eat_food/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - //get things out of blackboard - var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - var/atom/target = weak_target?.resolve() - var/list/wanted = controller.blackboard[BB_BASIC_FOODS] +## Leaf / ai_behavior - //we see if we have a target (remember, anything can be in that blackboard, it's not a hard reference) - if(!target || QDELETED(target)) - //we need to find some food - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_BASIC_MOB_CURRENT_TARGET, wanted) - return //this allows further subtrees to plan since we're doing a non-invasive behavior like checking the viscinity for food. +A leaf is the behavior node, and it is what performs actual actions. - //now we know we have a target but should let a hostile subtree plan attacking humans. let's check if it's actually food - if(target in wanted) - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING //this prevents further subtrees from planning since we want to focus on eating the food -``` +It is defined as /datum/bt_node/ai_behavior + +Common leaf jobs: + +- Find a target. +- Move toward something. +- Attack. +- Use an ability. +- Execute job logic (clean, heal, arrest, etc). + +Example: + +![Behavior example](learn_ai_images/behavior_example.png) + +This moves to the target set on the selected target key, and finishes execution when arrived + +## Decorator + +Decorators are essentially condition checks that gate nodes behind them. + +Basic use case: +Only execute combat branch if a target key is present. + +Example: + +![Behavior example](learn_ai_images/decorator_example.png) + +This gates the attack behavior behind the BB_CURRENT_TARGET_KEY being set. + +Outside of this, decorators serve a second important function, which is being able to observe the condition they are checking to see if its still valid while behavior is running in other nodes. There are two use cases for this: + +1. Stop performing behavior of my children because the conditions has become FALSE (e.g. I lost my target, stop attacking!) +2. Stop performing lower priority behavior, because the condition has become TRUE (e.g. Hey I found a target! stop idling!) + +You can also run observers for both of these cases at the same time. + +For most decorators, we can register for signals to observe when a potential condition change might have happend, for example in the case of the target, we register on the target key being changed somewhere. + +If for some reason we cannot have signals check the condition, you can also make it check every ai_controller process(), this is less efficient so use signals when possible. + +## What Composites Are + +Composites are structural nodes that control how child nodes run. + +- They never perform behavior themselves, and exist purely to determine how nodes below them are ran. +- The composite nodes are Selector, Sequence, Parallel, and Subplan + +## BT Node Types + +### 1) Selector + +What it does: +Tries children in order until one returns BT_SUCCEED. + +To be clear; this means until the first child returns BT_FAILURE, the second child will never start. + +Basic use case: +Try attacking, if you cant, perform idle behavior. + +![Behavior example](learn_ai_images/selector_example.png) + +In the above example, we try to run monkey combat behavior, if for some reason this doesn't succeed (usually due to lack of target), we run idle behavior. + +### 2) Sequence + +What it does: +Runs children in order and stops on first failure. Basically the opposite of selector. And allows for chaining behavior that needs to happen in sequence. + +Basic use case: +Move to a target, then perform work once in range. + +If you fail to moving to the target, then performing work would make no sense. So a sequence is best here. + +![Behavior example](learn_ai_images/sequence_example.png) + +In the above example, we move to a target, and then give them our currently held item. If we fail to move to them, we also dont try to give the item + +### 3) Parallel + +What it does: +Runs multiple children in parallel. + +This also has the option to loop the non-primary nodes (e.g. the nodes 2nd and up). This allows for behavior such as continiously checking whether we can find a target. + +You also have the option to stop any secondary behavior once the primary behavior is finished. This is useful in examples where you do not wish to wait until the secondary behavior finishes. + +Basic use case: +Finding targets while also performing all other behavior -And one of those behaviors, `basic_melee_attack`. As I have been doing so far, I've dumped in a bunch of comments explaining how this one behavior gets mobs to chase a target and slap it if in range. - -```dm -///this behavior makes an AI get close to their movement target, and attack every time perform() is called. -/datum/ai_behavior/basic_melee_attack - action_cooldown = 0.6 SECONDS - //flag tells the AI it needs to have a movement target to work, and since it doesn't have "AI_BEHAVIOR_MOVE_AND_PERFORM", it won't call perform() every 0.6 seconds until it is in melee range. Smart! - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - -/datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - //all this is doing in setup is setting the movement target. setup is called once when the behavior is first planned, and returning FALSE can cancel the behavior if something isn't right. - - //Hiding location is priority - var/datum/weakref/weak_target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] - var/atom/target = weak_target?.resolve() - if(!target) - return FALSE - //now the AI_BEHAVIOR_REQUIRE_MOVEMENT flag will be happy, we have a target to always be moving towards. - controller.current_movement_target = target - -///perform will run every "action_cooldown" deciseconds as long as the conditions are good for it to do so (we set "AI_BEHAVIOR_REQUIRE_MOVEMENT", so it won't perform until in range). -/datum/ai_behavior/basic_melee_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - var/mob/living/basic/basic_mob = controller.pawn - //targeting strategy will kill the action if not real anymore - var/datum/weakref/weak_target = controller.blackboard[target_key] - var/atom/target = weak_target?.resolve() - var/datum/targeting_strategy/targeting_strategy = controller.blackboard[targeting_strategy_key] - - if(!targeting_strategy.can_attack(basic_mob, target)) - ///We have a target that is no longer valid to attack. Remember that returning doesn't end the behavior, JUST this single performance. So we call "finish_action" with whether it succeeded in doing what it wanted to do (it didn't, so FALSE) and the blackboard keys passed into this behavior. - finish_action(controller, FALSE, target_key) - return //don't forget to end the performance too - - var/hiding_target = targeting_strategy.find_hidden_mobs(basic_mob, target) //If this is valid, theyre hidden in something! - - controller.blackboard[hiding_location_key] = hiding_target - - ///and finally, we're in range, we have a valid target, we can attack. When they fall into crit, they will no longer be a valid target, to the melee behavior will end. - if(hiding_target) //Slap it! - basic_mob.melee_attack(hiding_target) - else - basic_mob.melee_attack(target) - -///and so the action has ended. we can now clean up the AI's blackboard based on the success of the action, and the keys passed in. -/datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - ///if the behavior failed, the target is no longer valid, so we should lose aggro of them. We remove the target_key (which could be anything, it's whatever key was passed into the behavior by the subtree) from the blackboard. Couldn't do THAT with normal variables! - if(!succeeded) - controller.blackboard -= target_key +![Behavior example](learn_ai_images/parallel_example.png) + +Parallels also have a repeat_secondary_delay, which is a cooldown between the looping of secondary children. Ideal to cap how often certain behaviors fire (e.g. idle behavior) + +### 4) Subplan + +What it does: +Runs a child branch with a loop policy behavior. + +This essentially allows us to prevent the child from returning its return value to its parent, and instead try to loop. + +This is useful for things like combat and prevents redundant replanning of the entire plan. You can basically do "Hey, if you fail to hit someone this time, try again next time until you succeed". + +If doing this, its important to make use of observers on decorators to make sure you can exit the subplan, else you can get stuck in an endless cycle of behavior. + +Basic use case: +In combat, keep retrying attack logic instead of ending after one attempt. + +![Behavior example](learn_ai_images/subplan_example.png) + +In the above example, we are running a combat behavior where we first check if our target is set, (and have an observer that cancels if the condition changes). + +Then, we run a parallel; on the left side (primary) we have a subplan that runs a looping attack behavior; if this attack behavior fails (We're not close or some other issue), the sub-plan will just try again next tick. + +In the secondary branch, we try to move to the target. (and keep trying this as well, due to the parallels looping rule) + +Due to us having the observer, if for some reason our target changes due to being changed by another node, the decorators observer will re-evaluate and cancel the plan. Without an observer, this behavior would be stuck in an endless loop. + +Subplans also expose `loop_delay`, which when set causes a delay between each loop of the behavior, essentially a cooldown. It can be useful to gate things like idle behavior without triggering re-planning + +## Quick Node Selection Guide + +- Use leaf when you want to do one concrete action. +- Use selector when branches are alternatives. +- Use sequence when steps are ordered dependencies. +- Use parallel when you need concurrent actions +- Use decorator when a branch should only run behind a condition or should react to a condition changing. +- Use subplan when a child branch should loop on succes and/or failure. + +## Subtrees + +Subtrees are essentially modularized pieces of tree that can be re-used in different trees. This allows for patternizing common behaviors into a re-useable tree. + +One feature of subtrees is that if you are making a subtree, and want to change something when using the subtree depending on the AI, you can assign any field in the subtree as a binding; this makes it editable in any controller (or subtree) that the subtree is used. + +You can also re-assign subtrees by setting an "Override ID". By doing this you can call `controller.set_behavior_tree_override()` with the same ID to change what subtree is used in this node. Used in for example pet commands to tell a dog to go fetch dynamically. + +## Editor + +The behavior tree editor can be opened via opening one of our .bt.json files. These .bt.json files are specialized json files that format our (sub)trees. In theory these jsons are human-readable, but the editor makes it much easier to parse them. The editor only edits the .bt.json file itself. + +When opening the editor, you should press "Refresh Types" at the top, this will make the editor parse through all the relevant .dm files to find defined behaviors, decorators, subtrees and ai controllers. You should also run this if you modify or add new nodes, else your cache will be out of date. + +Now, you should be able to see the tree in front of you. In this view you can re-order nodes, add new ones, and change parameters on individual nodes. + +On the left, you can find a palette of all the nodes you have. The compsoite nodes are found in the top left, while the rest are distributed across 3 browsers: + +1. Behavior types (Leaf / ai_behavior nodes) +2. Decorator types (Decorator nodes) +3. Browser (All ai_controllers and subtrees) + +![Behavior example](learn_ai_images/bt_editor_example1.png) + +You can drag these nodes into the node-graph to place them. + +You can also change the nodes connections by dragging from either end to another node. This system is currently a bit fidgetty while I figure out how to become a better editor programmer. + +Once you are done with your tree, be sure to save the file. You can now compile and the JSON will be converted automatically + +## Targeting + +A huge amount of AI work boils down to finding a thing nearby and remembering it. Finding an enemy, finding food, finding a beacon to walk to. Instead of writing a brand new "find" leaf every time, we have one generic leaf that you configure with two helpers. You almost never need to write a new leaf for this. + +The three pieces: + +1. **The leaf** is `update_interaction_target` or `update_targets`. Its job is to look around, find something, and write it to a blackboard key. +2. **A target source** answers the question "what candidates exist?". It gathers a list of nearby things, such as everything in view, only things of a certain type, or items in your hands. +3. **A targeting strategy** answers the question "is this specific candidate valid?". It looks at one candidate at a time and says yes or no. + +So the flow is: the **source** hands the leaf a list of candidates, the leaf runs each one past the **strategy**, and the first one that passes gets written to your target key. + +You configure all of this on the leaf node. + +```text +LEAF: update_interaction_target + target_key: BB_TARGET_FOOD (where to store what we found) + target_source: .../held_items_then_oview/basic_foods (what to look at) + targeting_strategy: .../anything (how to decide it's valid) + vision_range: 7 (how far to look, optional) ``` + +A few pitfalls: + +- **Finding nothing is just a FAILURE.** If the source returns an empty list, the leaf simply fails. +- **Use `/datum/targeting_strategy/anything` if you don't filter for specifics.** With this strategy the only thing we check is `get_dist`, which is enough when the source already gives you the specific candidates you want. +- **Reuse before you build.** There are many existing sources and strategies. Use these before adding new ones. + +Once the target key is set, the rest of your tree reacts to it the way you've already seen. A `decorator` gates the combat branch behind "is the target key set?". An observer on this decorator can cancel lower priority behavior when the target is set, or cancel its own behavior when the target is lost. + +> Note: combat target _searching_ during a fight is usually done by the `update_targets` leaf, which keeps `BB_CURRENT_TARGET` refreshed while you fight. `update_interaction_target` is the general-purpose "go find a thing" leaf for everything else. diff --git a/code/datums/ai/learn_ai_images/behavior_example.png b/code/datums/ai/learn_ai_images/behavior_example.png new file mode 100644 index 000000000000..4ab176cd651b Binary files /dev/null and b/code/datums/ai/learn_ai_images/behavior_example.png differ diff --git a/code/datums/ai/learn_ai_images/bt_editor_example1.png b/code/datums/ai/learn_ai_images/bt_editor_example1.png new file mode 100644 index 000000000000..6e67423be6cc Binary files /dev/null and b/code/datums/ai/learn_ai_images/bt_editor_example1.png differ diff --git a/code/datums/ai/learn_ai_images/decorator_example.png b/code/datums/ai/learn_ai_images/decorator_example.png new file mode 100644 index 000000000000..ef940acb08d1 Binary files /dev/null and b/code/datums/ai/learn_ai_images/decorator_example.png differ diff --git a/code/datums/ai/learn_ai_images/parallel_example.png b/code/datums/ai/learn_ai_images/parallel_example.png new file mode 100644 index 000000000000..b848fbb77f5a Binary files /dev/null and b/code/datums/ai/learn_ai_images/parallel_example.png differ diff --git a/code/datums/ai/learn_ai_images/selector_example.png b/code/datums/ai/learn_ai_images/selector_example.png new file mode 100644 index 000000000000..f19e8f57a107 Binary files /dev/null and b/code/datums/ai/learn_ai_images/selector_example.png differ diff --git a/code/datums/ai/learn_ai_images/sequence_example.png b/code/datums/ai/learn_ai_images/sequence_example.png new file mode 100644 index 000000000000..9eaca19c109a Binary files /dev/null and b/code/datums/ai/learn_ai_images/sequence_example.png differ diff --git a/code/datums/ai/learn_ai_images/subplan_example.png b/code/datums/ai/learn_ai_images/subplan_example.png new file mode 100644 index 000000000000..6e5b6ae13772 Binary files /dev/null and b/code/datums/ai/learn_ai_images/subplan_example.png differ diff --git a/code/datums/ai/monkey/monkey.bt.json b/code/datums/ai/monkey/monkey.bt.json new file mode 100644 index 000000000000..98d9082b3709 --- /dev/null +++ b/code/datums/ai/monkey/monkey.bt.json @@ -0,0 +1,246 @@ +{ + "dm_type": "/datum/ai_controller/monkey", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/monkey_combat", + "override_id": "SUBPLAN_ID_MONKEY_COMBAT" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/monkey_serve_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_hunger" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_play_instrument" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/monkey_shenanigans" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_idle" + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "invert": true + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_set_combat_target", + "vars": { + "attack_target_key": "BB_CURRENT_TARGET", + "enemies_key": "BB_monkey_enemies" + } + }, + { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_MONKEY_WANNA_PRESS_SOME_SHIT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_current_press_target", + "targeting_strategy": "/datum/targeting_strategy/anything", + "target_source": "/datum/target_source/monkey_press_target", + "vision_range": 2 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_MONKEY_WANNA_PRESS_SOME_SHIT" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.2 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_MONKEY_WANNA_PRESS_SOME_SHIT", + "value": true + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_holding_item", + "vars": { + "key": "BB_MY_PAWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_current_give_target", + "targeting_strategy": "/datum/targeting_strategy/anything", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "vision_range": 2 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_holding_item", + "vars": { + "invert": true, + "key": "BB_MY_PAWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_monkey_current_give_target" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_holding_target", + "vars": { + "key": "BB_SONG_INSTRUMENT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_tamed" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SONG_INSTRUMENT", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/held_items_typed/instrument" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_nutrition_below", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_NEXT_HUNGRY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "bb_food_target", + "targeting_strategy": "/datum/targeting_strategy/pickup_item/food_or_drink", + "target_source": "/datum/target_source/held_items_then_oview", + "vision_range": 2 + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_monkey_tamed" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon", + "vars": { + "target_key": "BB_monkey_pickuptarget", + "target_source": "/datum/target_source/monkey_weapon_upgrade", + "targeting_strategy": "/datum/targeting_strategy/monkey_weapon_upgrade", + "vision_range": 5 + } + } + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon", + "vars": { + "target_key": "BB_monkey_pickuptarget", + "target_source": "/datum/target_source/monkey_weapon_upgrade", + "targeting_strategy": "/datum/targeting_strategy/monkey_weapon_upgrade", + "vision_range": 5 + } + } + ] +} diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm deleted file mode 100644 index 865f6a069eaa..000000000000 --- a/code/datums/ai/monkey/monkey_behaviors.dm +++ /dev/null @@ -1,305 +0,0 @@ -/datum/ai_behavior/battle_screech/monkey - screeches = list("roar","screech") - -/datum/ai_behavior/monkey_equip - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/monkey_equip/setup(datum/ai_controller/controller, target_key) - . = ..() - var/obj/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/monkey_equip/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - - if(!success) //Don't try again on this item if we failed - controller.set_blackboard_key_assoc(BB_MONKEY_BLACKLISTITEMS, controller.blackboard[target_key], TRUE) - - controller.clear_blackboard_key(BB_MONKEY_PICKUPTARGET) - -/// Equips an item on the monkey -/// Returns TRUE if it works out, FALSE otherwise -/datum/ai_behavior/monkey_equip/proc/equip_item(datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - - var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET] - var/best_force = controller.blackboard[BB_MONKEY_BEST_FORCE_FOUND] - if(!isturf(living_pawn.loc)) - return FALSE - - if(!target) - return FALSE - - if(target.anchored) //Can't pick it up, so stop trying. - return FALSE - - // Strong weapon - else if(target.force > best_force) - living_pawn.drop_all_held_items() - living_pawn.put_in_hands(target) - controller.set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, target.force) - return TRUE - - else if(target.slot_flags) //Clothing == top priority - living_pawn.dropItemToGround(target, TRUE) - living_pawn.update_icons() - if(!living_pawn.equip_to_appropriate_slot(target)) - return FALSE //Already wearing something, in the future this should probably replace the current item but the code didn't actually do that, and I dont want to support it right now. - return TRUE - - // EVERYTHING ELSE - else if(living_pawn.get_empty_held_indexes()) - living_pawn.put_in_hands(target) - return TRUE - - return FALSE - -/datum/ai_behavior/monkey_equip/ground - -/datum/ai_behavior/monkey_equip/ground/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - . = ..() - if(equip_item(controller)) - return . | AI_BEHAVIOR_SUCCEEDED - return . | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/monkey_equip/pickpocket - -/datum/ai_behavior/monkey_equip/pickpocket/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - . = ..() - if(controller.blackboard[BB_MONKEY_PICKPOCKETING]) //We are pickpocketing, don't do ANYTHING!!!! - return - INVOKE_ASYNC(src, PROC_REF(attempt_pickpocket), controller) - -/datum/ai_behavior/monkey_equip/pickpocket/proc/attempt_pickpocket(datum/ai_controller/controller) - var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET] - var/mob/living/victim = target.loc - var/mob/living/living_pawn = controller.pawn - - if(!istype(victim)) - finish_action(controller, FALSE) - return - - victim.visible_message(span_warning("[living_pawn] starts trying to take [target] from [victim]!"), span_danger("[living_pawn] tries to take [target]!")) - - controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, TRUE) - - var/success = FALSE - - if(do_after(living_pawn, MONKEY_ITEM_SNATCH_DELAY, victim) && target && victim.IsReachableBy(living_pawn)) - - for(var/obj/item/I in victim.held_items) - if(I == target) - victim.visible_message(span_danger("[living_pawn] snatches [target] from [victim]."), span_userdanger("[living_pawn] snatched [target]!")) - if(victim.temporarilyRemoveItemFromInventory(target)) - if(!QDELETED(target) && !equip_item(controller)) - target.forceMove(living_pawn.drop_location()) - success = TRUE - break - else - victim.visible_message(span_danger("[living_pawn] tried to snatch [target] from [victim], but failed!"), span_userdanger("[living_pawn] tried to grab [target]!")) - - finish_action(controller, success) //We either fucked up or got the item. - -/datum/ai_behavior/monkey_equip/pickpocket/finish_action(datum/ai_controller/controller, success) - . = ..() - controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, FALSE) - controller.clear_blackboard_key(BB_MONKEY_PICKUPTARGET) - -/datum/ai_behavior/monkey_flee - -/datum/ai_behavior/monkey_flee/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - - if(living_pawn.health >= MONKEY_FLEE_HEALTH) //we're back in bussiness - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - var/mob/living/target = null - - // flee from anyone who attacked us and we didn't beat down - for(var/mob/living/L in view(living_pawn, MONKEY_FLEE_VISION)) - if(controller.blackboard[BB_MONKEY_ENEMIES][L] && L.stat == CONSCIOUS) - target = L - break - - if(target) - GLOB.move_manager.move_away(living_pawn, target, max_dist=MONKEY_ENEMY_VISION, delay=5) - return AI_BEHAVIOR_DELAY - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/monkey_attack_mob - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION //performs to increase frustration - -/datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key) - . = ..() - set_movement_target(controller, controller.blackboard[target_key]) - -/datum/ai_behavior/monkey_attack_mob/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/target = controller.blackboard[target_key] - var/mob/living/living_pawn = controller.pawn - var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[BB_TARGETING_STRATEGY]) - - if(QDELETED(target) || !strategy.can_attack(living_pawn, target)) //Target == owned - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - // check if target has a weapon - var/holding_weapon - for(var/obj/item/potential_weapon in target.held_items) - if(!(potential_weapon.item_flags & ABSTRACT)) - holding_weapon = potential_weapon - break - - var/attack_results = monkey_attack(controller, target, seconds_per_tick, holding_weapon && SPT_PROB(MONKEY_ATTACK_DISARM_PROB, seconds_per_tick), holding_weapon) - - if(!attack_results || controller.blackboard[BB_MONKEY_AGGRESSIVE]) - return AI_BEHAVIOR_DELAY - - //check if we can de-aggro on the enemy... - var/hatred_value = controller.blackboard[BB_MONKEY_ENEMIES][target] - - if(isnull(hatred_value)) - hatred_value = 1 - controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, hatred_value) - - if(!SPT_PROB(MONKEY_HATRED_REDUCTION_PROB, seconds_per_tick)) - return AI_BEHAVIOR_DELAY - - //we decrease our hatred value to them by 1 - hatred_value-- - if(hatred_value <= 0) - controller.remove_thing_from_blackboard_key(BB_MONKEY_ENEMIES, target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, hatred_value) - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little -/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm, holding_weapon) - var/mob/living/living_pawn = controller.pawn - - if(living_pawn.next_move > world.time) - return FALSE - - //are we holding a gun? can we shoot it? if so, FIRE - var/obj/item/gun/gun_to_shoot = locate() in living_pawn.held_items - if(gun_to_shoot?.can_shoot()) - if(gun_to_shoot != living_pawn.get_active_held_item()) - living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) - controller.ai_interact(target = target, combat_mode = TRUE) - return TRUE - - //look for any potential weapons we're holding - var/obj/item/potential_weapon = locate() in living_pawn.held_items - if(!target.IsReachableBy(living_pawn, potential_weapon?.reach)) - return FALSE - - if(isnull(potential_weapon)) - controller.ai_interact(target = target, modifiers = disarm ? list(RIGHT_CLICK = TRUE) : null, combat_mode = TRUE) - if(disarm && !isnull(holding_weapon) && controller.blackboard[BB_MONKEY_BLACKLISTITEMS][holding_weapon]) - controller.remove_thing_from_blackboard_key(BB_MONKEY_BLACKLISTITEMS, holding_weapon) //lets try to pickpocket it again! - return TRUE - - if(potential_weapon != living_pawn.get_active_held_item()) - living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) - controller.ai_interact(target = target, combat_mode = TRUE) - return TRUE - -/datum/ai_behavior/disposal_mob - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM //performs to increase frustration - -/datum/ai_behavior/disposal_mob/setup(datum/ai_controller/controller, attack_target_key, disposal_target_key) - . = ..() - set_movement_target(controller, controller.blackboard[attack_target_key]) - -/datum/ai_behavior/disposal_mob/finish_action(datum/ai_controller/controller, succeeded, attack_target_key, disposal_target_key) - . = ..() - controller.clear_blackboard_key(attack_target_key) //Reset attack target - controller.set_blackboard_key(BB_MONKEY_DISPOSING, FALSE) //No longer disposing - controller.clear_blackboard_key(disposal_target_key) //No target disposal - -/datum/ai_behavior/disposal_mob/perform(seconds_per_tick, datum/ai_controller/controller, attack_target_key, disposal_target_key) - if(controller.blackboard[BB_MONKEY_DISPOSING]) //We are disposing, don't do ANYTHING!!!! - return AI_BEHAVIOR_DELAY - - var/mob/living/target = controller.blackboard[attack_target_key] - var/mob/living/living_pawn = controller.pawn - - set_movement_target(controller, target) - - if(!target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(target.pulledby != living_pawn && !HAS_AI_CONTROLLER_TYPE(target.pulledby, /datum/ai_controller/monkey)) //Dont steal from my fellow monkeys. - if(living_pawn.Adjacent(target) && isturf(target.loc)) - target.grabbedby(living_pawn) - return AI_BEHAVIOR_DELAY //Do the rest next turn - - var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key] - set_movement_target(controller, disposal) - - if(!disposal) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(living_pawn.Adjacent(disposal)) - INVOKE_ASYNC(src, PROC_REF(try_disposal_mob), controller, attack_target_key, disposal_target_key) //put him in! - return AI_BEHAVIOR_DELAY - //This means we might be getting pissed! - return AI_BEHAVIOR_DELAY - -/datum/ai_behavior/disposal_mob/proc/try_disposal_mob(datum/ai_controller/controller, attack_target_key, disposal_target_key) - var/mob/living/living_pawn = controller.pawn - var/mob/living/target = controller.blackboard[attack_target_key] - var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key] - - controller.set_blackboard_key(BB_MONKEY_DISPOSING, TRUE) - - if(target && disposal?.stuff_mob_in(target, living_pawn)) - disposal.flush() - finish_action(controller, TRUE, attack_target_key, disposal_target_key) - - -/datum/ai_behavior/recruit_monkeys/perform(seconds_per_tick, datum/ai_controller/controller) - controller.set_blackboard_key(BB_MONKEY_RECRUIT_COOLDOWN, world.time + MONKEY_RECRUIT_COOLDOWN) - var/mob/living/living_pawn = controller.pawn - - for(var/mob/living/nearby_monkey in view(living_pawn, MONKEY_ENEMY_VISION)) - if(QDELETED(nearby_monkey) || !HAS_AI_CONTROLLER_TYPE(nearby_monkey, /datum/ai_controller/monkey)) - continue - if(!SPT_PROB(MONKEY_RECRUIT_PROB, seconds_per_tick)) - continue - // Recruited a monkey to our side - controller.set_blackboard_key(BB_MONKEY_RECRUIT_COOLDOWN, world.time + MONKEY_RECRUIT_COOLDOWN) - // Other monkeys now also hate the guy we're currently targeting - nearby_monkey.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET], MONKEY_RECRUIT_HATED_AMOUNT) - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/monkey_set_combat_target/perform(seconds_per_tick, datum/ai_controller/controller, set_key, enemies_key) - var/list/enemies = controller.blackboard[enemies_key] - var/list/valids = list() - for(var/mob/living/possible_enemy in view(MONKEY_ENEMY_VISION, controller.pawn)) - if(possible_enemy == controller.pawn) - continue // don't target ourselves - if(!enemies[possible_enemy]) //We don't hate this creature! But we might still attack it! - if(!controller.blackboard[BB_MONKEY_AGGRESSIVE]) //We are not aggressive either, so we won't attack! - continue - if(possible_enemy.has_faction(list(FACTION_MONKEY, FACTION_JUNGLE)) && !controller.blackboard[BB_MONKEY_TARGET_MONKEYS]) // do not target your team. includes monkys gorillas etc. - continue - // Weighted list, so the closer they are the more likely they are to be chosen as the enemy - valids[possible_enemy] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1) - - if(!length(valids)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/mob/living/target = pick_weight(valids) - - EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[controller.pawn] has selected [target] as a target for blackboard key [set_key]! Behavior: [src]", get_turf(target), "Target: [target]") - EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(controller.pawn), get_turf(target)) - - controller.set_blackboard_key(set_key, target) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/monkey/monkey_bt_nodes.dm b/code/datums/ai/monkey/monkey_bt_nodes.dm new file mode 100644 index 000000000000..a10f0176d2fc --- /dev/null +++ b/code/datums/ai/monkey/monkey_bt_nodes.dm @@ -0,0 +1,401 @@ + +/// Monkey's battle screech variant louder and on a 5 second cooldown +/datum/bt_node/ai_behavior/battle_screech/monkey + time_between_perform = 5 SECONDS + screeches = list("roar", "screech") + + +/// Base equip behavior; handles blacklist updates and key cleanup on finish +/datum/bt_node/ai_behavior/monkey_equip + var/target_key + +/datum/bt_node/ai_behavior/monkey_equip/finish_action(datum/ai_controller/controller, success) + . = ..() + if(!success) // Don't try to pick this item up again + controller.set_blackboard_key_assoc(BB_MONKEY_BLACKLISTITEMS, controller.blackboard[target_key], TRUE) + controller.clear_blackboard_key(BB_MONKEY_PICKUPTARGET) + controller.clear_blackboard_key(BB_MONKEY_PICKUP_IS_PICKPOCKET) + +/// Equip a weapon off the ground +/datum/bt_node/ai_behavior/monkey_equip/ground + +/datum/bt_node/ai_behavior/monkey_equip/ground/setup(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/monkey_equip/ground/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + return start_async() + +/datum/bt_node/ai_behavior/monkey_equip/ground/perform_async(datum/ai_controller/controller) + var/result = equip_item(controller) + if(!async_still_valid()) + return + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/// Pickpocket a weapon from a mob +/datum/bt_node/ai_behavior/monkey_equip/pickpocket + +/datum/bt_node/ai_behavior/monkey_equip/pickpocket/setup(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/monkey_equip/pickpocket/perform(seconds_per_tick, datum/ai_controller/controller) + if(controller.blackboard[BB_MONKEY_PICKPOCKETING]) // mid-snatch; wait + return AI_BEHAVIOR_DELAY + INVOKE_ASYNC(src, PROC_REF(attempt_pickpocket), controller) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/monkey_equip/pickpocket/proc/attempt_pickpocket(datum/ai_controller/controller) + var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET] + var/mob/living/victim = target?.loc + var/mob/living/living_pawn = controller.pawn + + if(!istype(victim)) + finish_action(controller, FALSE) + return + + victim.visible_message(span_warning("[living_pawn] starts trying to take [target] from [victim]!"), span_danger("[living_pawn] tries to take [target]!")) + controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, TRUE) + + var/success = FALSE + + if(do_after(living_pawn, MONKEY_ITEM_SNATCH_DELAY, victim) && target && victim.IsReachableBy(living_pawn)) + for(var/obj/item/I in victim.held_items) + if(I == target) + victim.visible_message(span_danger("[living_pawn] snatches [target] from [victim]."), span_userdanger("[living_pawn] snatched [target]!")) + if(victim.temporarilyRemoveItemFromInventory(target)) + if(!QDELETED(target) && !equip_item(controller)) + target.forceMove(living_pawn.drop_location()) + success = TRUE + break + else + victim.visible_message(span_danger("[living_pawn] tried to snatch [target] from [victim], but failed!"), span_userdanger("[living_pawn] tried to grab [target]!")) + + finish_action(controller, success) + +/datum/bt_node/ai_behavior/monkey_equip/pickpocket/finish_action(datum/ai_controller/controller, success) + . = ..() + controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, FALSE) + +/// Shared item equip proc +/datum/bt_node/ai_behavior/monkey_equip/proc/equip_item(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET] + var/best_force = controller.blackboard[BB_MONKEY_BEST_FORCE_FOUND] + + if(!isturf(living_pawn.loc)) + return FALSE + if(!target) + return FALSE + if(target.anchored) + return FALSE + + if(target.force > best_force) // better weapon + living_pawn.drop_all_held_items() + living_pawn.put_in_hands(target) + controller.set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, target.force) + return TRUE + + if(target.slot_flags) // wearable + living_pawn.dropItemToGround(target, TRUE) + living_pawn.update_icons() + if(!living_pawn.equip_to_appropriate_slot(target)) + return FALSE + return TRUE + + if(living_pawn.get_empty_held_indexes()) // any free hand + living_pawn.put_in_hands(target) + return TRUE + + return FALSE + +/// Gathers weapon upgrade candidates: nearby ground items, then held items of nearby humans. +/datum/target_source/monkey_weapon_upgrade + +/datum/target_source/monkey_weapon_upgrade/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = list() + for(var/obj/item/ground_item in oview(range, pawn)) + candidates += ground_item + for(var/mob/living/carbon/human/nearby_human in oview(range, pawn)) + candidates += nearby_human.held_items + return candidates + +/// Weapon upgrade candidate: not two-handed, not blacklisted, and hits harder than our bite. +/datum/targeting_strategy/monkey_weapon_upgrade + +/datum/targeting_strategy/monkey_weapon_upgrade/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/item/candidate = target + if(HAS_TRAIT(candidate, TRAIT_NEEDS_TWO_HANDS) || controller.blackboard[BB_MONKEY_BLACKLISTITEMS][candidate]) + return FALSE + if(candidate.force < 2) // our bite already does ~2 damage + return FALSE + return TRUE + +/// Scans nearby items and mobs for a better weapon and sets BB_MONKEY_PICKUPTARGET +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon + +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon/can_search(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(!(locate(/obj/item) in living_pawn.held_items)) + controller.set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, 0) + if(controller.blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED] && (locate(/obj/item/gun) in living_pawn.held_items)) + return FALSE // already have a gun + return ..() + +/// Prefers any gun once gun neurons are activated, else the strongest candidate that beats our current best held item. +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/mob/living/living_pawn = controller.pawn + + if(controller.blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED]) + for(var/obj/item/candidate as anything in filtered_targets) + if(isgun(candidate)) + return candidate + + var/top_force = 0 + for(var/obj/item/held in living_pawn.held_items) + if(HAS_TRAIT(held, TRAIT_NEEDS_TWO_HANDS) || controller.blackboard[BB_MONKEY_BLACKLISTITEMS][held]) + continue + top_force = max(top_force, held.force) + + var/obj/item/best + for(var/obj/item/candidate as anything in filtered_targets) + if(candidate.force <= top_force) + continue + best = candidate + top_force = candidate.force + return best + +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + controller.set_blackboard_key(BB_MONKEY_PICKUP_IS_PICKPOCKET, ismob(target.loc) ? TRUE : null) + + +/// Selects a target from BB_MONKEY_ENEMIES or picks any visible mob if aggressive. This should be ported to new targetting but its so fkn bespoke +/datum/bt_node/ai_behavior/monkey_set_combat_target + var/attack_target_key + var/enemies_key + +/datum/bt_node/ai_behavior/monkey_set_combat_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/enemies = controller.blackboard[enemies_key] + + if(HAS_TRAIT(living_pawn, TRAIT_PACIFISM) || (!length(enemies) && !controller.blackboard[BB_MONKEY_AGGRESSIVE])) + living_pawn.set_combat_mode(FALSE) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/list/valids = list() + for(var/mob/living/possible_enemy in view(MONKEY_ENEMY_VISION, living_pawn)) + if(possible_enemy == living_pawn) + continue + if(!enemies[possible_enemy]) + if(!controller.blackboard[BB_MONKEY_AGGRESSIVE]) + continue + if(possible_enemy.has_faction(list(FACTION_MONKEY, FACTION_JUNGLE)) && !controller.blackboard[BB_MONKEY_TARGET_MONKEYS]) + continue + if(possible_enemy.incapacitated) // Dont bother, theyre fucked. + continue + valids[possible_enemy] = CEILING(100 / (get_dist(living_pawn, possible_enemy) || 1), 1) + + if(!length(valids)) + living_pawn.set_combat_mode(FALSE) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/mob/living/target = pick_weight(valids) + + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_TARGETING, "[living_pawn] has selected [target] as a target for blackboard key [attack_target_key]! Behavior: [src]", get_turf(target), "Target: [target]") + EVLOG_LINES(controller, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(living_pawn), get_turf(target)) + + living_pawn.set_combat_mode(TRUE) + controller.set_blackboard_key(attack_target_key, target) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +/// Attacks the target mob; SUCCEEDED when target is gone, FAILED when target goes down (Which lets us flush the fucker instead) +/datum/bt_node/ai_behavior/monkey_attack_mob + var/target_key + /// seconds_per_tick from the perform() that kicked off the current async attack. + VAR_PRIVATE/attack_seconds_per_tick = 0 + /// Weapon snapshot from the perform() that kicked off the current async attack. + VAR_PRIVATE/obj/item/attack_holding_weapon + +/datum/bt_node/ai_behavior/monkey_attack_mob/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + + var/mob/living/target = controller.blackboard[target_key] + var/mob/living/living_pawn = controller.pawn + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[BB_TARGETING_STRATEGY]) + + if(QDELETED(target) || !strategy.is_valid_target(living_pawn, target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/obj/item/holding_weapon + for(var/obj/item/potential_weapon in target.held_items) + if(!(potential_weapon.item_flags & ABSTRACT)) + holding_weapon = potential_weapon + break + + attack_seconds_per_tick = seconds_per_tick + attack_holding_weapon = holding_weapon + return start_async() + +/datum/bt_node/ai_behavior/monkey_attack_mob/perform_async(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + var/seconds_per_tick = attack_seconds_per_tick + var/obj/item/holding_weapon = attack_holding_weapon + var/attack_results = monkey_attack(controller, target, seconds_per_tick, holding_weapon && SPT_PROB(MONKEY_ATTACK_DISARM_PROB, seconds_per_tick), holding_weapon) + if(!async_still_valid()) + return + var/succeeded = FALSE + if(attack_results && !controller.blackboard[BB_MONKEY_AGGRESSIVE]) + succeeded = TRUE + if(prob(MONKEY_HATRED_REDUCTION_PROB)) + var/hatred_value = controller.blackboard[BB_MONKEY_ENEMIES][target] - 1 + if(hatred_value <= 0) + controller.remove_thing_from_blackboard_key(BB_MONKEY_ENEMIES, target) + else + controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, hatred_value) + finish_async(succeeded ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/datum/bt_node/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + attack_seconds_per_tick = 0 + attack_holding_weapon = null + +/// Attack with held weapon or bite; try to disarm if target is holding something +/datum/bt_node/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm, holding_weapon) + var/mob/living/living_pawn = controller.pawn + + if(living_pawn.next_move > world.time) + return FALSE + + living_pawn.face_atom(target) + + var/obj/item/potential_weapon = locate(/obj/item) in living_pawn.held_items + + if(target.IsReachableBy(living_pawn, potential_weapon?.reach)) + if(isnull(potential_weapon)) + controller.ai_interact(target = target, modifiers = disarm ? list(RIGHT_CLICK = TRUE) : null, combat_mode = TRUE) + if(disarm && !isnull(holding_weapon) && controller.blackboard[BB_MONKEY_BLACKLISTITEMS][holding_weapon]) + controller.remove_thing_from_blackboard_key(BB_MONKEY_BLACKLISTITEMS, holding_weapon) + else + if(potential_weapon != living_pawn.get_active_held_item()) + living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) + controller.ai_interact(target = target, combat_mode = TRUE) + controller.override_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) + return TRUE + + if(!potential_weapon) + return FALSE + + // target out of melee reach — try ranged or throw + var/atom/real_target = target + if(prob(10)) // artificial miss + real_target = pick(oview(2, target)) + + var/obj/item/gun/gun = locate(/obj/item/gun) in living_pawn.held_items + var/can_shoot = gun?.can_shoot() || FALSE + if(gun && controller.blackboard[BB_MONKEY_GUN_WORKED] && prob(95)) + if(gun != living_pawn.get_active_held_item()) + living_pawn.swap_hand(living_pawn.get_inactive_hand_index()) + controller.ai_interact(target = real_target, combat_mode = TRUE) + controller.override_blackboard_key(BB_MONKEY_GUN_WORKED, can_shoot ? TRUE : prob(80)) + if(can_shoot) + controller.override_blackboard_key(BB_MONKEY_GUN_NEURONS_ACTIVATED, TRUE) + else + living_pawn.throw_item(real_target) + controller.override_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) + return TRUE + +/// Rallies nearby monkeys against the current attack target +/datum/bt_node/ai_behavior/recruit_monkeys + var/target_key + +/datum/bt_node/ai_behavior/recruit_monkeys/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/mob/living/attack_target = controller.blackboard[target_key] + + for(var/mob/living/nearby_monkey in view(living_pawn, MONKEY_ENEMY_VISION)) + if(QDELETED(nearby_monkey) || !HAS_AI_CONTROLLER_TYPE(nearby_monkey, /datum/ai_controller/monkey)) + continue + if(prob(MONKEY_RECRUIT_PROB)) + continue + nearby_monkey.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, attack_target, MONKEY_RECRUIT_HATED_AMOUNT) + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + +/// Scans nearby humans for patrons to serve; fails if bartender present or fewer than 1 patron found +/datum/bt_node/ai_behavior/monkey_find_patrons + var/patrons_key + var/give_target_key + +/datum/bt_node/ai_behavior/monkey_find_patrons/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/nearby_patrons = list() + + for(var/mob/living/carbon/human/human_mob in oview(5, living_pawn)) + if(istype(human_mob.mind?.assigned_role, /datum/job/bartender)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED // my boss is on duty! + if(IS_UNCONSCIOUS_OR_CRIT(human_mob) || ismonkey(human_mob)) + continue + if(!human_mob.get_empty_held_indexes()) + continue + nearby_patrons += human_mob + + if(!length(nearby_patrons)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.override_blackboard_key(patrons_key, nearby_patrons) + controller.blackboard[give_target_key] ||= pick(nearby_patrons) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + +/// Gathers press targets: filtered to BB_MONKEY_PRESS_TYPEPATH's type if set, else any nearby obj. +/datum/target_source/monkey_press_target + +/datum/target_source/monkey_press_target/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/locate_path = controller.blackboard[BB_MONKEY_PRESS_TYPEPATH] + var/list/candidates = list() + for(var/obj/potential_candidate in oview(range, pawn)) + if(locate_path) + if(istype(potential_candidate, locate_path)) + candidates += potential_candidate + break //found a bell fuck everything else + candidates += potential_candidate + return candidates + +/// Idle wander/emote behavior. Reads emote lists from BB_MONKEY_IDLE_COMMON_EMOTES and BB_MONKEY_IDLE_RARE_EMOTES. +/datum/bt_node/ai_behavior/monkey_idle + +/datum/bt_node/ai_behavior/monkey_idle/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + if(SPT_PROB(25, seconds_per_tick) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + else if(SPT_PROB(5, seconds_per_tick)) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(controller.blackboard[BB_MONKEY_IDLE_COMMON_EMOTES])) + else if(SPT_PROB(1, seconds_per_tick)) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), pick(controller.blackboard[BB_MONKEY_IDLE_RARE_EMOTES])) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +///monkey trees + +/datum/bt_node/subtree/monkey_combat + behavior_tree_json = "code/datums/ai/monkey/monkey_combat.bt.json" + +/datum/bt_node/subtree/monkey_find_weapon + behavior_tree_json = "code/datums/ai/monkey/monkey_find_weapon.bt.json" + +/datum/bt_node/subtree/monkey_shenanigans + behavior_tree_json = "code/datums/ai/monkey/monkey_shenanigans.bt.json" + +/datum/bt_node/subtree/monkey_serve_food + behavior_tree_json = "code/datums/ai/monkey/monkey_serve_food.bt.json" diff --git a/code/datums/ai/monkey/monkey_combat.bt.json b/code/datums/ai/monkey/monkey_combat.bt.json new file mode 100644 index 000000000000..eea3dda2c4c4 --- /dev/null +++ b/code/datums/ai/monkey/monkey_combat.bt.json @@ -0,0 +1,248 @@ +{ + "dm_type": "/datum/bt_node/subtree/monkey_combat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "0.2 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 10, + "require_reach": false + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_health_below", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "health_threshold": 40 + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/monkey_find_weapon" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/mob_stat_at_least", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_CURRENT_TARGET", + "min_stat": "SOFT_CRIT" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_target_disposal", + "target_source": "/datum/target_source/oview_single_type/disposal_unit", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grab_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_target_disposal", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/stuff_in_disposal", + "vars": { + "attack_target_key": "BB_CURRENT_TARGET", + "disposal_target_key": "BB_monkey_target_disposal" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_can_be_shove_stunned", + "vars": { + "target_key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/shove_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_attack_mob", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/agile_melee_movement", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "standoff_max": 4 + } + } + ] + } + ] + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "5 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_monkey_recruit_cooldown", + "cooldown_duration": 10 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/recruit_monkeys", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + } + ] + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BATTLE_SCREECH_COOLDOWN", + "cooldown_duration": 5 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.25 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/battle_screech/monkey" + } + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/monkey_find_weapon", + "vars": { + "time_between_perform": 0, + "target_key": "BB_monkey_pickuptarget", + "targeting_strategy": "/datum/targeting_strategy/monkey_weapon_upgrade", + "target_source": "/datum/target_source/monkey_weapon_upgrade", + "vision_range": 5 + } + } + ] + } +} diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm index 18bdb0be26b0..f507f819f256 100644 --- a/code/datums/ai/monkey/monkey_controller.dm +++ b/code/datums/ai/monkey/monkey_controller.dm @@ -7,13 +7,7 @@ have ways of interacting with a specific mob and control it. /datum/ai_controller/monkey ai_movement = /datum/ai_movement/basic_avoidance movement_delay = 0.4 SECONDS - planning_subtrees = list( - /datum/ai_planning_subtree/generic_resist, - /datum/ai_planning_subtree/monkey_combat, - /datum/ai_planning_subtree/generic_hunger, - /datum/ai_planning_subtree/generic_play_instrument, - /datum/ai_planning_subtree/monkey_shenanigans, - ) + behavior_tree_json = "code/datums/ai/monkey/monkey.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/monkey, BB_MONKEY_AGGRESSIVE = FALSE, @@ -23,35 +17,26 @@ have ways of interacting with a specific mob and control it. BB_MONKEY_PICKPOCKETING = FALSE, BB_MONKEY_DISPOSING = FALSE, BB_MONKEY_GUN_NEURONS_ACTIVATED = FALSE, + BB_MONKEY_GUN_WORKED = TRUE, BB_SONG_LINES = MONKEY_SONG, - BB_RESISTING = FALSE, BB_MONKEY_GIVE_CHANCE = 5, + BB_MONKEY_PICKUP_IS_PICKPOCKET = null, + BB_MONKEY_IDLE_COMMON_EMOTES = list("screech", "roar"), + BB_MONKEY_IDLE_RARE_EMOTES = list("scratch", "jump", "roll", "tail"), ) - idle_behavior = /datum/idle_behavior/idle_monkey /datum/targeting_strategy/basic/monkey + custom_faction_check = TRUE /datum/targeting_strategy/basic/monkey/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) // if they wronged us, all bets are off if(controller.blackboard[BB_MONKEY_ENEMIES][the_target]) return FALSE // target was forcibly set, all bets are off again - if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == the_target) + if(controller.blackboard[BB_CURRENT_TARGET] == the_target) return FALSE return ..() -/datum/ai_controller/monkey/process(seconds_per_tick) - - var/mob/living/living_pawn = src.pawn - - if(!length(living_pawn.do_afters) && living_pawn.ai_controller.blackboard[BB_RESISTING]) - living_pawn.ai_controller.set_blackboard_key(BB_RESISTING, FALSE) - - if(living_pawn.ai_controller.blackboard[BB_RESISTING]) - return - - . = ..() - /datum/ai_controller/monkey/New(atom/new_pawn) var/static/list/control_examine = list( ORGAN_SLOT_EYES = span_monkey("%PRONOUN_They stare%PRONOUN_s around with wild, primal eyes."), @@ -59,18 +44,6 @@ have ways of interacting with a specific mob and control it. AddElement(/datum/element/ai_control_examine, control_examine) return ..() -/datum/ai_controller/monkey/pun_pun - movement_delay = 0.7 SECONDS //pun pun moves slower so the bartender can keep track of them - planning_subtrees = list( - /datum/ai_planning_subtree/generic_resist, - /datum/ai_planning_subtree/monkey_combat, - /datum/ai_planning_subtree/serve_food, - /datum/ai_planning_subtree/generic_hunger, - /datum/ai_planning_subtree/generic_play_instrument, - /datum/ai_planning_subtree/monkey_shenanigans, - ) - idle_behavior = /datum/idle_behavior/idle_monkey/pun_pun - /datum/ai_controller/monkey/pun_pun/TryPossessPawn(atom/new_pawn) . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) @@ -80,6 +53,8 @@ have ways of interacting with a specific mob and control it. set_blackboard_key(BB_MONKEY_TAMED, TRUE) set_blackboard_key(BB_MONKEY_GIVE_CHANCE, 30) set_blackboard_key(BB_MONKEY_PRESS_TYPEPATH, /obj/structure/desk_bell) + override_blackboard_key(BB_MONKEY_IDLE_COMMON_EMOTES, list("tunesing", "dance", "bow")) + override_blackboard_key(BB_MONKEY_IDLE_RARE_EMOTES, list("clear", "sign", "tail")) set_trip_mode(mode = FALSE) /datum/ai_controller/monkey/angry @@ -140,60 +115,17 @@ have ways of interacting with a specific mob and control it. /datum/ai_controller/monkey/get_able_to_run() var/mob/living/living_pawn = pawn - if(INCAPACITATED_IGNORING(living_pawn, INCAPABLE_RESTRAINTS|INCAPABLE_STASIS|INCAPABLE_GRAB) || living_pawn.stat > CONSCIOUS) + if(INCAPACITATED_IGNORING(living_pawn, INCAPABLE_RESTRAINTS|INCAPABLE_STASIS|INCAPABLE_GRAB)) return AI_UNABLE_TO_RUN return ..() /datum/ai_controller/monkey/proc/set_trip_mode(mode = TRUE) - var/mob/living/carbon/regressed_monkey = pawn + var/mob/living/regressed_monkey = pawn var/brain = regressed_monkey.get_organ_slot(ORGAN_SLOT_BRAIN) if(istype(brain, /obj/item/organ/brain/primate)) // In case we are a monkey AI in a human brain by who was previously controlled by a client but it now not by some marvel var/obj/item/organ/brain/primate/monkeybrain = brain monkeybrain.tripping = mode -///re-used behavior pattern by monkeys for finding a weapon -/datum/ai_controller/monkey/proc/TryFindWeapon() - var/mob/living/living_pawn = pawn - - if(!(locate(/obj/item) in living_pawn.held_items)) - set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, 0) - - if(blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED] && (locate(/obj/item/gun) in living_pawn.held_items)) - // We have a gun, what could we possibly want? - return FALSE - - var/obj/item/weapon - var/list/nearby_items = list() - for(var/obj/item/item in oview(2, living_pawn)) - nearby_items += item - - for(var/obj/item/item in living_pawn.held_items) // If we've got some garbage in out hands that's going to stop us from effectively attacking, we should get rid of it. - if(item.force < 2) - living_pawn.dropItemToGround(item) - - weapon = GetBestWeapon(src, nearby_items, living_pawn.held_items) - - var/pickpocket = FALSE - for(var/mob/living/carbon/human/human in oview(5, living_pawn)) - var/obj/item/held_weapon = GetBestWeapon(src, human.held_items + weapon, living_pawn.held_items) - if(held_weapon == weapon) // It's just the same one, not a held one - continue - pickpocket = TRUE - weapon = held_weapon - - if(!weapon || (weapon in living_pawn.held_items)) - return FALSE - - if(weapon.force < 2) // our bite does 2 damage on average, no point in settling for anything less - return FALSE - - set_blackboard_key(BB_MONKEY_PICKUPTARGET, weapon) - if(pickpocket) - queue_behavior(/datum/ai_behavior/monkey_equip/pickpocket, BB_MONKEY_PICKUPTARGET) - else - queue_behavior(/datum/ai_behavior/monkey_equip/ground, BB_MONKEY_PICKUPTARGET) - return TRUE - ///Reactive events to being hit /datum/ai_controller/monkey/proc/retaliate(mob/living/living_mob) // just to be safe @@ -210,7 +142,7 @@ have ways of interacting with a specific mob and control it. /datum/ai_controller/monkey/proc/on_startpulling(datum/source, atom/movable/puller, state, force) SIGNAL_HANDLER var/mob/living/living_pawn = pawn - if(!IS_DEAD_OR_INCAP(living_pawn) && prob(MONKEY_PULL_AGGRO_PROB)) // nuh uh you don't pull me! + if(!living_pawn.incapacitated && prob(MONKEY_PULL_AGGRO_PROB)) // nuh uh you don't pull me! retaliate(living_pawn.pulledby) return TRUE diff --git a/code/datums/ai/monkey/monkey_find_weapon.bt.json b/code/datums/ai/monkey/monkey_find_weapon.bt.json new file mode 100644 index 000000000000..4d8970916fed --- /dev/null +++ b/code/datums/ai/monkey/monkey_find_weapon.bt.json @@ -0,0 +1,50 @@ +{ + "dm_type": "/datum/bt_node/subtree/monkey_find_weapon", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_monkey_pickuptarget" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_pickuptarget", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_pickup_is_pickpocket" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_equip/pickpocket", + "vars": { + "target_key": "BB_monkey_pickuptarget" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_equip/ground", + "vars": { + "target_key": "BB_monkey_pickuptarget" + } + } + ] + } + ] + } +} diff --git a/code/datums/ai/monkey/monkey_serve_food.bt.json b/code/datums/ai/monkey/monkey_serve_food.bt.json new file mode 100644 index 000000000000..4706b73c4bea --- /dev/null +++ b/code/datums/ai/monkey/monkey_serve_food.bt.json @@ -0,0 +1,132 @@ +{ + "dm_type": "/datum/bt_node/subtree/monkey_serve_food", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_tamed" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_MONKEY_PATRON_FIND_COOLDOWN", + "cooldown_duration": "1 SECONDS", + "lock_on_succeed": false + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/monkey_find_patrons", + "vars": { + "patrons_key": "BB_monkey_patrons_nearby", + "give_target_key": "BB_monkey_current_give_target" + } + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_holding_target", + "vars": { + "key": "BB_monkey_current_served_item" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_current_give_target", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/give", + "vars": { + "target_key": "BB_monkey_current_give_target" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_current_served_item", + "targeting_strategy": "/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks", + "target_source": "/datum/target_source/held_items_then_oview", + "vision_range": 2, + "must_be_reachable": true + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": false, + "key": "BB_monkey_current_served_item" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_current_served_item", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up", + "vars": { + "target_key": "BB_monkey_current_served_item", + "drop_held": true + } + } + ] + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_current_served_item", + "targeting_strategy": "/datum/targeting_strategy/pickup_item/food_or_drink/include_drinks", + "target_source": "/datum/target_source/held_items_then_oview", + "vision_range": 2, + "must_be_reachable": true + } + } + ] + } + ] + } + ] + } +} diff --git a/code/datums/ai/monkey/monkey_shenanigans.bt.json b/code/datums/ai/monkey/monkey_shenanigans.bt.json new file mode 100644 index 000000000000..b4c7ea92e395 --- /dev/null +++ b/code/datums/ai/monkey/monkey_shenanigans.bt.json @@ -0,0 +1,114 @@ +{ + "dm_type": "/datum/bt_node/subtree/monkey_shenanigans", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_holding_item", + "vars": { + "key": "BB_MY_PAWN" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_in_hand" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_current_press_target" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_current_press_target", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_on_object", + "vars": { + "target_key": "BB_monkey_current_press_target" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_monkey_current_press_target" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_MONKEY_WANNA_PRESS_SOME_SHIT" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_current_give_target" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance_from_key", + "vars": { + "chance_key": "BB_monkey_give_chance" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_current_give_target", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/give", + "vars": { + "target_key": "BB_monkey_current_give_target" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_monkey_tamed" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/monkey_find_weapon" + } + } + ] +} diff --git a/code/datums/ai/monkey/monkey_subtrees.dm b/code/datums/ai/monkey/monkey_subtrees.dm deleted file mode 100644 index dc8793ffdfed..000000000000 --- a/code/datums/ai/monkey/monkey_subtrees.dm +++ /dev/null @@ -1,112 +0,0 @@ -/datum/ai_planning_subtree/monkey_shenanigans/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) - - if(prob(5)) - controller.queue_behavior(/datum/ai_behavior/use_in_hand) - - if(!SPT_PROB(MONKEY_SHENANIGAN_PROB, seconds_per_tick)) - return - - if(!controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET]) - if(controller.blackboard[BB_MONKEY_PRESS_TYPEPATH]) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_MONKEY_CURRENT_PRESS_TARGET, controller.blackboard[BB_MONKEY_PRESS_TYPEPATH], 2) - else - controller.queue_behavior(/datum/ai_behavior/find_nearby, BB_MONKEY_CURRENT_PRESS_TARGET) - else if(prob(50)) - controller.queue_behavior(/datum/ai_behavior/use_on_object, BB_MONKEY_CURRENT_PRESS_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET]) - controller.queue_behavior(/datum/ai_behavior/find_and_set/pawn_must_hold_item, BB_MONKEY_CURRENT_GIVE_TARGET, /mob/living/carbon/human, 2) - else if(prob(controller.blackboard[BB_MONKEY_GIVE_CHANCE])) - controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!controller.blackboard[BB_MONKEY_TAMED]) - controller.TryFindWeapon() - -///monkey combat subtree. -/datum/ai_planning_subtree/monkey_combat/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/list/enemies = controller.blackboard[BB_MONKEY_ENEMIES] - - if((HAS_TRAIT(controller.pawn, TRAIT_PACIFISM)) || (!length(enemies) && !controller.blackboard[BB_MONKEY_AGGRESSIVE])) //Pacifist, or we have no enemies and we're not pissed - living_pawn.set_combat_mode(FALSE) - return - - if(!controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]) - controller.queue_behavior(/datum/ai_behavior/monkey_set_combat_target, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_ENEMIES) - living_pawn.set_combat_mode(FALSE) - return SUBTREE_RETURN_FINISH_PLANNING - - var/mob/living/selected_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] - - if(QDELETED(selected_enemy)) - living_pawn.set_combat_mode(FALSE) - return - - if(!selected_enemy.stat) //He's up, get him! - if(living_pawn.health < MONKEY_FLEE_HEALTH) //Time to skeddadle - controller.queue_behavior(/datum/ai_behavior/monkey_flee) - return SUBTREE_RETURN_FINISH_PLANNING //I'm running fuck you guys - - if(controller.TryFindWeapon()) //Getting a weapon is higher priority if im not fleeing. - return SUBTREE_RETURN_FINISH_PLANNING - - if(controller.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] < world.time) - controller.queue_behavior(/datum/ai_behavior/recruit_monkeys, BB_MONKEY_CURRENT_ATTACK_TARGET) - return - - if(SPT_PROB(ismonkey(living_pawn) ? 25 : 10, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/battle_screech/monkey) - controller.queue_behavior(/datum/ai_behavior/monkey_attack_mob, BB_MONKEY_CURRENT_ATTACK_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - //by this point we have a target but they're down, let's try dumpstering this loser - - living_pawn.set_combat_mode(FALSE) - - if(!controller.blackboard[BB_MONKEY_TARGET_DISPOSAL]) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_MONKEY_TARGET_DISPOSAL, /obj/machinery/disposal, MONKEY_ENEMY_VISION) - return - - controller.queue_behavior(/datum/ai_behavior/disposal_mob, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_TARGET_DISPOSAL) - return SUBTREE_RETURN_FINISH_PLANNING - -/// Finds food or drinks, picks them up, then gives them to nearby humans -/datum/ai_planning_subtree/serve_food - -/datum/ai_planning_subtree/serve_food/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/list/nearby_patrons = list() - for(var/mob/living/carbon/human/human_mob in oview(5, living_pawn)) - if(istype(human_mob.mind?.assigned_role, /datum/job/bartender)) - return // my boss is on duty! - if(human_mob.stat != CONSCIOUS || ismonkey(human_mob)) - continue - if(!human_mob.get_empty_held_indexes()) - continue - nearby_patrons += human_mob - - // Need at least 2 patrons to bother serving (bearing in mind the - if(length(nearby_patrons) < 1) - return - - var/obj/item/serving = controller.blackboard[BB_MONKEY_CURRENT_SERVED_ITEM] - if(QDELETED(serving) || serving.reagents.total_volume <= 0) - controller.queue_behavior(/datum/ai_behavior/find_and_set/food_or_drink/to_serve, BB_MONKEY_CURRENT_SERVED_ITEM, /obj/item, 2) - return - - // we have something to serve, pick a patron and go hand it over - if(living_pawn.is_holding(serving)) - controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET] ||= pick(nearby_patrons) - controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - // we have something to serve but aren't holding it yet - if(isturf(serving.loc)) - // fetch the drink - controller.queue_behavior(/datum/ai_behavior/navigate_to_and_pick_up, BB_MONKEY_CURRENT_SERVED_ITEM, TRUE) - else - // give up on the dream - controller.clear_blackboard_key(BB_MONKEY_CURRENT_SERVED_ITEM) - return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm index 7c4df64e6e6e..dcf3947e859e 100644 --- a/code/datums/ai/movement/_ai_movement.dm +++ b/code/datums/ai/movement/_ai_movement.dm @@ -6,24 +6,42 @@ var/max_pathing_attempts //Override this to setup the moveloop you want to use -/datum/ai_movement/proc/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/proc/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) SHOULD_CALL_PARENT(TRUE) + var/old_movement_target = moving_controllers[controller] + if(old_movement_target) + if(old_movement_target == current_movement_target) + return FALSE + else + update_movement_target(controller, current_movement_target) + return FALSE controller.consecutive_pathing_attempts = 0 controller.set_blackboard_key(BB_CURRENT_MIN_MOVE_DISTANCE, min_distance) moving_controllers[controller] = current_movement_target SEND_SIGNAL(controller.pawn, COMSIG_MOB_AI_MOVEMENT_STARTED, current_movement_target) + controller.set_blackboard_key(BB_CURRENT_MOVEMENT_TARGET, current_movement_target) + return TRUE + +/datum/ai_movement/proc/update_movement_target(datum/ai_controller/controller, atom/new_target) + moving_controllers[controller] = new_target + controller.set_blackboard_key(BB_CURRENT_MOVEMENT_TARGET, new_target) /datum/ai_movement/proc/stop_moving_towards(datum/ai_controller/controller) controller.consecutive_pathing_attempts = 0 moving_controllers -= controller // We got deleted as we finished an action + controller.clear_blackboard_key(BB_CURRENT_MOVEMENT_TARGET) if(!QDELETED(controller.pawn)) GLOB.move_manager.stop_looping(controller.pawn, SSai_movement) /datum/ai_movement/proc/increment_pathing_failures(datum/ai_controller/controller) controller.consecutive_pathing_attempts++ if(controller.consecutive_pathing_attempts >= max_pathing_attempts) - controller.CancelActions() + fail_movement(controller) + +/datum/ai_movement/proc/fail_movement(datum/ai_controller/controller) + stop_moving_towards(controller) + SEND_SIGNAL(controller.pawn, COMSIG_MOB_AI_MOVEMENT_FAILED, moving_controllers[controller]) /datum/ai_movement/proc/reset_pathing_failures(datum/ai_controller/controller) controller.consecutive_pathing_attempts = 0 @@ -67,7 +85,6 @@ // Check if this controller can actually run, so we don't chase people with corpses if(!controller.able_to_run) - controller.CancelActions() qdel(source) //stop moving return MOVELOOP_SKIP_STEP diff --git a/code/datums/ai/movement/ai_movement_basic_avoidance.dm b/code/datums/ai/movement/ai_movement_basic_avoidance.dm index 169301f70470..37cbfb9ebb65 100644 --- a/code/datums/ai/movement/ai_movement_basic_avoidance.dm +++ b/code/datums/ai/movement/ai_movement_basic_avoidance.dm @@ -4,15 +4,24 @@ /// Movement flags to pass to the loop var/move_flags = NONE -/datum/ai_movement/basic_avoidance/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/basic_avoidance/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) . = ..() + if(!.) + return FALSE var/atom/movable/moving = controller.pawn var/min_dist = controller.blackboard[BB_CURRENT_MIN_MOVE_DISTANCE] - var/delay = controller.movement_delay + var/delay = delay_override || controller.movement_delay var/datum/move_loop/loop = GLOB.move_manager.move_to(moving, current_movement_target, min_dist, delay, flags = move_flags, subsystem = SSai_movement, extra_info = controller) RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move)) RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) +/datum/ai_movement/basic_avoidance/update_movement_target(datum/ai_controller/controller, atom/new_target) + . = ..() + + var/datum/move_loop/has_target/loop = GLOB.move_manager.processing_on(controller.pawn, SSai_movement) + if(loop) + loop.target = new_target + /datum/ai_movement/basic_avoidance/allowed_to_move(datum/move_loop/has_target/dist_bound/source) var/turf/target_turf = get_step_towards(source.moving, source.target) if(!target_turf?.can_cross_safely(source.moving)) @@ -21,4 +30,4 @@ /// Move immediately and don't update our facing /datum/ai_movement/basic_avoidance/backstep - move_flags = MOVEMENT_LOOP_START_INSTANT | MOVEMENT_LOOP_NO_DIR_UPDATE + move_flags = MOVEMENT_LOOP_NO_DIR_UPDATE diff --git a/code/datums/ai/movement/ai_movement_complete_stop.dm b/code/datums/ai/movement/ai_movement_complete_stop.dm index e9609a30dda5..17787b86d958 100644 --- a/code/datums/ai/movement/ai_movement_complete_stop.dm +++ b/code/datums/ai/movement/ai_movement_complete_stop.dm @@ -2,8 +2,10 @@ /datum/ai_movement/complete_stop max_pathing_attempts = INFINITY // path all you want, you can not escape your fate -/datum/ai_movement/complete_stop/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/complete_stop/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) . = ..() + if(!.) + return FALSE var/atom/movable/moving = controller.pawn var/stopping_time = controller.blackboard[BB_STATIONARY_SECONDS] var/delay_time = (stopping_time * 0.5) // no real reason to fire any more often than this really @@ -11,5 +13,6 @@ var/datum/move_loop/loop = GLOB.move_manager.freeze(moving, current_movement_target, delay = delay_time, timeout = stopping_time, subsystem = SSai_movement, extra_info = controller) RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move)) + /datum/ai_movement/complete_stop/allowed_to_move(datum/move_loop/source) return FALSE diff --git a/code/datums/ai/movement/ai_movement_dumb.dm b/code/datums/ai/movement/ai_movement_dumb.dm index 001501bcbd93..f5ed83e2d881 100644 --- a/code/datums/ai/movement/ai_movement_dumb.dm +++ b/code/datums/ai/movement/ai_movement_dumb.dm @@ -3,14 +3,23 @@ max_pathing_attempts = 16 ///Put your movement behavior in here! -/datum/ai_movement/dumb/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/dumb/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) . = ..() + if(.) + return FALSE var/atom/movable/moving = controller.pawn - var/delay = controller.movement_delay + var/delay = delay_override || controller.movement_delay var/datum/move_loop/loop = GLOB.move_manager.move_towards_legacy(moving, current_movement_target, delay, subsystem = SSai_movement, extra_info = controller) RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move)) RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) + +/datum/ai_movement/dumb/update_movement_target(datum/ai_controller/controller, atom/new_target) + . = ..() + var/datum/move_loop/has_target/loop = GLOB.move_manager.processing_on(controller.pawn, SSai_movement) + if(loop) + loop.target = new_target + /datum/ai_movement/dumb/allowed_to_move(datum/move_loop/has_target/source) var/turf/target_turf = get_step_towards(source.moving, source.target) if(!target_turf?.can_cross_safely(source.moving)) diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm index a8980d15b851..3e7e4b6c725a 100644 --- a/code/datums/ai/movement/ai_movement_jps.dm +++ b/code/datums/ai/movement/ai_movement_jps.dm @@ -7,12 +7,14 @@ ///how we deal with diagonal movement, whether we try to avoid them or follow through with them var/diagonal_flags = DIAGONAL_REMOVE_CLUNKY -/datum/ai_movement/jps/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/jps/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) . = ..() + if(!.) + return FALSE var/atom/movable/moving = controller.pawn - var/delay = controller.movement_delay + var/delay = delay_override || controller.movement_delay - var/datum/move_loop/loop = setup_moveloop(controller, current_movement_target, moving, delay) + var/datum/move_loop/loop = setup_moveloop(controller, current_movement_target, moving, delay, min_distance) RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move)) RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) @@ -21,14 +23,14 @@ return loop -/datum/ai_movement/jps/proc/setup_moveloop(datum/ai_controller/controller, atom/current_movement_target, atom/movable/moving, delay) +/datum/ai_movement/jps/proc/setup_moveloop(datum/ai_controller/controller, atom/current_movement_target, atom/movable/moving, delay, min_distance) var/datum/move_loop/has_target/jps/loop = GLOB.move_manager.jps_move(moving, current_movement_target, delay, repath_delay = 0.5 SECONDS, simulated_only = !HAS_TRAIT(controller.pawn, TRAIT_SPACEWALK), max_path_length = maximum_length, - minimum_distance = controller.get_minimum_distance(), + minimum_distance = min_distance, access = controller.get_access(), subsystem = SSai_movement, diagonal_handling = diagonal_flags, @@ -36,19 +38,25 @@ ) return loop +/datum/ai_movement/jps/update_movement_target(datum/ai_controller/controller, atom/new_target) + . = ..() + var/datum/move_loop/has_target/jps/loop = GLOB.move_manager.processing_on(controller.pawn, SSai_movement) + if(loop) + INVOKE_ASYNC(loop, TYPE_PROC_REF(/datum/move_loop/has_target/jps, recalculate_path)) + /datum/ai_movement/jps/proc/repath_incoming(datum/move_loop/has_target/jps/source) SIGNAL_HANDLER var/datum/ai_controller/controller = source.extra_info source.access = controller.get_access() - source.minimum_distance = controller.get_minimum_distance() + // minimum_distance was set at loop creation; no need to update it on repath /datum/ai_movement/jps/bot max_pathing_attempts = 8 maximum_length = 25 diagonal_flags = DIAGONAL_REMOVE_ALL -/datum/ai_movement/jps/bot/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) +/datum/ai_movement/jps/bot/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance, delay_override) var/datum/move_loop/loop = ..() var/atom/our_pawn = controller.pawn if(isnull(our_pawn)) @@ -65,14 +73,14 @@ max_pathing_attempts = 10 maximum_length = AI_MULEBOT_PATH_LENGTH -/datum/ai_movement/jps/bot/mulebot/setup_moveloop(datum/ai_controller/controller, atom/current_movement_target, atom/movable/moving, delay) +/datum/ai_movement/jps/bot/mulebot/setup_moveloop(datum/ai_controller/controller, atom/current_movement_target, atom/movable/moving, delay, min_distance) var/datum/move_loop/has_target/jps/frustrations/loop = GLOB.move_manager.frustrations_move(moving, current_movement_target, delay, repath_delay = 0.5 SECONDS, simulated_only = !HAS_TRAIT(controller.pawn, TRAIT_SPACEWALK), max_path_length = maximum_length, - minimum_distance = controller.get_minimum_distance(), + minimum_distance = min_distance, access = controller.get_access(), subsystem = SSai_movement, diagonal_handling = diagonal_flags, diff --git a/code/datums/ai/objects/vending_machines/vending_machine.bt.json b/code/datums/ai/objects/vending_machines/vending_machine.bt.json new file mode 100644 index 000000000000..725e206a5841 --- /dev/null +++ b/code/datums/ai/objects/vending_machines/vending_machine.bt.json @@ -0,0 +1,64 @@ +{ + "dm_type": "/datum/ai_controller/vending_machine", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/vending_is_tilted", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_VENDING_UNTILT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/vendor_rise_up" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/vending_is_tilted", + "vars": { + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_VENDING_TILT_COOLDOWN" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_vendor_target", + "vars": { + "target_key": "BB_VENDING_CURRENT_TARGET", + "vision_range": 7 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_VENDING_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/vendor_crush", + "vars": { + "target_key": "BB_VENDING_CURRENT_TARGET" + } + } + ] + } + } + } + ] +} diff --git a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm index 1aa936e124d4..6bfc8ec9fc49 100644 --- a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm +++ b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm @@ -1,28 +1,51 @@ -/datum/ai_behavior/vendor_crush - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - ///Time before machine can untilt itself after tilting - var/untilt_cooldown = 1 SECONDS - ///Time to telegraph and tilt over - var/time_to_tilt = 0.8 SECONDS +/// Returns TRUE if the vending machine pawn is currently tilted. +/datum/bt_node/decorator/vending_is_tilted/check_condition(datum/ai_controller/controller) + var/obj/machinery/vending/vendor_pawn = controller.pawn + return vendor_pawn.tilted -/datum/ai_behavior/vendor_crush/setup(datum/ai_controller/controller, target_key) - . = ..() - set_movement_target(controller, controller.blackboard[target_key]) +/// Searches nearby tiles for a valid living target and sets the given BB key. Sets tilt cooldown and fails if none found. +/datum/bt_node/ai_behavior/find_vendor_target + var/target_key + var/vision_range + /// Cooldown applied to BB_VENDING_TILT_COOLDOWN when no valid target is in range + var/search_cooldown = 2 SECONDS -/datum/ai_behavior/vendor_crush/perform(seconds_per_tick, datum/ai_controller/controller) +/datum/bt_node/ai_behavior/find_vendor_target/perform(seconds_per_tick, datum/ai_controller/controller) + for(var/mob/living/living_target in oview(vision_range, controller.pawn)) + if(living_target.stat || living_target.incorporeal_move) + continue + controller.set_blackboard_key(target_key, living_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + controller.clear_blackboard_key(target_key) + controller.set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + search_cooldown) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/// Telegraphs and tilts onto the target. Returns success once the machine is tilted. +/datum/bt_node/ai_behavior/vendor_crush + var/target_key + /// Time to telegraph before tilting + var/time_to_tilt = 0.8 SECONDS + /// Time before machine can untilt after a tilt attempt + var/untilt_cooldown = 1 SECONDS + +/datum/bt_node/ai_behavior/vendor_crush/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/machinery/vending/vendor_pawn = controller.pawn + if(vendor_pawn.tilted) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED if(controller.blackboard[BB_VENDING_BUSY_TILTING]) return AI_BEHAVIOR_DELAY - controller.ai_movement.stop_moving_towards(controller) controller.set_blackboard_key(BB_VENDING_BUSY_TILTING, TRUE) - var/turf/target_turf = get_turf(controller.blackboard[BB_VENDING_CURRENT_TARGET]) + var/turf/target_turf = get_turf(controller.blackboard[target_key]) new /obj/effect/temp_visual/telegraphing/vending_machine_tilt(target_turf) addtimer(CALLBACK(src, PROC_REF(tiltonmob), controller, target_turf), time_to_tilt) return AI_BEHAVIOR_DELAY -/datum/ai_behavior/vendor_crush/proc/tiltonmob(datum/ai_controller/controller, turf/target_turf) +/datum/bt_node/ai_behavior/vendor_crush/proc/tiltonmob(datum/ai_controller/controller, turf/target_turf) + if(QDELETED(controller) || QDELETED(controller.pawn)) + return var/obj/machinery/vending/vendor_pawn = controller.pawn - if(vendor_pawn.tilt(target_turf, 0) & SUCCESSFULLY_CRUSHED_MOB) //We hit something + if(vendor_pawn.tilt(target_turf, 0) & SUCCESSFULLY_CRUSHED_MOB) vendor_pawn.say(pick("Supersize this!", "Eat my shiny metal ass!", "Want to consume some of my products?", "SMASH!", "Don't you love these smashing prices!")) controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, TRUE) else @@ -30,21 +53,22 @@ flick(vendor_pawn.icon_deny, vendor_pawn) vendor_pawn.say(pick("Get back here!", "Don't you want my well priced love?")) controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, FALSE) - finish_action(controller, TRUE) + controller.set_blackboard_key(BB_VENDING_UNTILT_COOLDOWN, world.time + untilt_cooldown) + controller.set_blackboard_key(BB_VENDING_BUSY_TILTING, FALSE) -/datum/ai_behavior/vendor_crush/finish_action(datum/ai_controller/controller, succeeded) +/datum/bt_node/ai_behavior/vendor_crush/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.set_blackboard_key(BB_VENDING_BUSY_TILTING, FALSE) - controller.set_blackboard_key(BB_VENDING_UNTILT_COOLDOWN, world.time + untilt_cooldown) -/datum/ai_behavior/vendor_rise_up //what a gamer - ///Time before machine can tilt again after untilting if last hit was a success - var/succes_tilt_cooldown = 5 SECONDS +/// Untilts the machine. Sets a tilt cooldown if the previous hit was successful. +/datum/bt_node/ai_behavior/vendor_rise_up + /// Time before machine can tilt again after untilting if the last hit landed + var/success_tilt_cooldown = 5 SECONDS -/datum/ai_behavior/vendor_rise_up/perform(seconds_per_tick, datum/ai_controller/controller) +/datum/bt_node/ai_behavior/vendor_rise_up/perform(seconds_per_tick, datum/ai_controller/controller) var/obj/machinery/vending/vendor_pawn = controller.pawn vendor_pawn.visible_message(span_warning("[vendor_pawn] untilts itself!")) if(controller.blackboard[BB_VENDING_LAST_HIT_SUCCESSFUL]) - controller.set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + succes_tilt_cooldown) + controller.set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + success_tilt_cooldown) vendor_pawn.untilt() return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/objects/vending_machines/vending_machine_controller.dm b/code/datums/ai/objects/vending_machines/vending_machine_controller.dm index ab888040fb51..5e73a9d74607 100644 --- a/code/datums/ai/objects/vending_machines/vending_machine_controller.dm +++ b/code/datums/ai/objects/vending_machines/vending_machine_controller.dm @@ -1,6 +1,7 @@ ///AI controller for vending machine gone rogue, Don't try using this on anything else, it wont work. /datum/ai_controller/vending_machine movement_delay = 0.4 SECONDS + behavior_tree_json = "code/datums/ai/objects/vending_machines/vending_machine.bt.json" blackboard = list( BB_VENDING_CURRENT_TARGET = null, BB_VENDING_TILT_COOLDOWN = 0, @@ -10,10 +11,6 @@ ) /// If TRUE stops mobs from buying things from active machines var/block_usage = FALSE - /// Range to search for mobs to crunch - var/vision_range = 7 - /// Seconds between attempts to find a new mob to crunch - var/search_for_enemy_cooldown = 2 SECONDS /datum/ai_controller/vending_machine/TryPossessPawn(atom/new_pawn) if(!istype(new_pawn, /obj/machinery/vending)) @@ -35,26 +32,6 @@ UnregisterSignal(vendor_pawn, COMSIG_VENDING_UI_INTERACT) return ..() //Run parent at end -/datum/ai_controller/vending_machine/SelectBehaviors(seconds_per_tick) - current_behaviors = list() - var/obj/machinery/vending/vendor_pawn = pawn - - if(vendor_pawn.tilted) //We're tilted, try to untilt - if(blackboard[BB_VENDING_UNTILT_COOLDOWN] > world.time) - return - queue_behavior(/datum/ai_behavior/vendor_rise_up) - return - else //Not tilted, try to find target to tilt onto. - if(blackboard[BB_VENDING_TILT_COOLDOWN] > world.time) - return - for(var/mob/living/living_target in oview(vision_range, pawn)) - if(living_target.stat || living_target.incorporeal_move) //They're already fucked up or incorporeal - continue - set_blackboard_key(BB_VENDING_CURRENT_TARGET, living_target) - queue_behavior(/datum/ai_behavior/vendor_crush, BB_VENDING_CURRENT_TARGET) - return - set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + search_for_enemy_cooldown) - /datum/ai_controller/vending_machine/proc/deny_vending_interact(obj/machinery/vending/vending_machine, mob/user, datum/tgui/ui) SIGNAL_HANDLER if(!block_usage) diff --git a/code/datums/ai/robot_customer/robot_customer.bt.json b/code/datums/ai/robot_customer/robot_customer.bt.json new file mode 100644 index 000000000000..e055a409302b --- /dev/null +++ b/code/datums/ai/robot_customer/robot_customer.bt.json @@ -0,0 +1,125 @@ +{ + "dm_type": "/datum/ai_controller/robot_customer", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CUSTOMER_LEAVING", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/robot_customer/find_exit_portal" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CUSTOMER_EXIT_PORTAL", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/robot_customer/leave_venue" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CUSTOMER_CURRENT_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CUSTOMER_CURRENT_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_spine", + "vars": { + "target_key": "BB_CUSTOMER_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CUSTOMER_MY_SEAT", + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_CUSTOMER_FIND_SEAT_COOLDOWN", + "cooldown_duration": "8 SECONDS", + "lock_on_succeed": false + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/robot_customer/find_seat" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CUSTOMER_MY_SEAT" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CUSTOMER_MY_SEAT", + "required_dist": 0 + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CUSTOMER_CURRENT_ORDER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/robot_customer/order_food" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/robot_customer/wait_for_food" + } + ] + } + ] + } + } + ] +} diff --git a/code/datums/ai/robot_customer/robot_customer_behaviors.dm b/code/datums/ai/robot_customer/robot_customer_behaviors.dm index 42c2188bc4cb..45b087a58e06 100644 --- a/code/datums/ai/robot_customer/robot_customer_behaviors.dm +++ b/code/datums/ai/robot_customer/robot_customer_behaviors.dm @@ -1,86 +1,94 @@ -/datum/ai_behavior/find_seat - action_cooldown = 8 SECONDS +/// Searches nearby for an unclaimed seat belonging to the customer's venue. +/// Sets BB_CUSTOMER_MY_SEAT and claims it on success. Returns FAILURE if none found. +/datum/bt_node/ai_behavior/robot_customer/find_seat -/datum/ai_behavior/find_seat/perform(seconds_per_tick, datum/ai_controller/controller) +/datum/bt_node/ai_behavior/robot_customer/find_seat/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/robot_customer/customer_pawn = controller.pawn var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] var/datum/venue/attending_venue = controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE] var/obj/structure/holosign/robot_seat/found_seat - - for(var/obj/structure/holosign/robot_seat/potential_seat in oview(7, controller.pawn)) - - if(potential_seat.linked_venue != attending_venue) //Incorrect venue + for(var/obj/structure/holosign/robot_seat/potential_seat in oview(7, customer_pawn)) + if(potential_seat.linked_venue != attending_venue) continue - - if(attending_venue.linked_seats[potential_seat]) //Someone called dibs + if(attending_venue.linked_seats[potential_seat]) continue var/turf/seat_turf = get_turf(potential_seat) - - if(seat_turf.is_blocked_turf()) //Someone called dibsies + if(seat_turf.is_blocked_turf()) continue - found_seat = potential_seat break if(found_seat) - customer_pawn.say(pick(customer_data.found_seat_lines)) + INVOKE_ASYNC(customer_pawn, TYPE_PROC_REF(/atom/movable, say), pick(customer_data.found_seat_lines)) controller.set_blackboard_key(BB_CUSTOMER_MY_SEAT, found_seat) attending_venue.linked_seats[found_seat] = customer_pawn - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - // SPT_PROB 1.5 is about a 60% chance that the tourist will have vocalised at least once every minute. if(!controller.blackboard[BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE] || SPT_PROB(1.5, seconds_per_tick)) - customer_pawn.say(pick(customer_data.cant_find_seat_lines)) + INVOKE_ASYNC(customer_pawn, TYPE_PROC_REF(/atom/movable, say), pick(customer_data.cant_find_seat_lines)) controller.set_blackboard_key(BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE, TRUE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + +/// Places the customer's food order once they are at their seat. +/datum/bt_node/ai_behavior/robot_customer/order_food -/datum/ai_behavior/order_food - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - required_distance = 0 +/datum/bt_node/ai_behavior/robot_customer/order_food/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags -/datum/ai_behavior/order_food/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/robot_customer/customer_pawn = controller.pawn - var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT] + if(get_turf(seat_marker) == get_turf(customer_pawn)) var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(customer_pawn) if(my_seat) - controller.pawn.setDir(my_seat.dir) //Sit in your seat + customer_pawn.setDir(my_seat.dir) + return start_async() + +/datum/bt_node/ai_behavior/robot_customer/order_food/perform_async(datum/ai_controller/controller) + var/mob/living/basic/robot_customer/customer_pawn = controller.pawn + var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] var/datum/venue/attending_venue = controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE] + var/order + if(!QDELETED(customer_pawn) && !QDELETED(attending_venue)) + order = attending_venue.order_food(customer_pawn, customer_data) + if(!async_still_valid()) + return + if(!isnull(order)) + controller.set_blackboard_key(BB_CUSTOMER_CURRENT_ORDER, order) + finish_async(AI_BEHAVIOR_SUCCEEDED) - controller.set_blackboard_key(BB_CUSTOMER_CURRENT_ORDER, attending_venue.order_food(customer_pawn, customer_data)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/wait_for_food - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - required_distance = 0 +/// Waits at the seat for food to arrive. Ticks down patience and checks for food placed in front. +/// Succeeds when BB_CUSTOMER_EATING is set; fails when patience runs out or BB_CUSTOMER_LEAVING is set. +/// On finish, sets BB_CUSTOMER_LEAVING and says the appropriate departure line. +/datum/bt_node/ai_behavior/robot_customer/wait_for_food -/datum/ai_behavior/wait_for_food/perform(seconds_per_tick, datum/ai_controller/controller) +/datum/bt_node/ai_behavior/robot_customer/wait_for_food/perform(seconds_per_tick, datum/ai_controller/controller) if(controller.blackboard[BB_CUSTOMER_EATING]) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - controller.add_blackboard_key(BB_CUSTOMER_PATIENCE, seconds_per_tick * -1 SECONDS) // Convert seconds_per_tick to a SECONDS equivalent. - if(controller.blackboard[BB_CUSTOMER_PATIENCE] < 0 || controller.blackboard[BB_CUSTOMER_LEAVING]) // Check if we're leaving because something might've forced us to + controller.add_blackboard_key(BB_CUSTOMER_PATIENCE, seconds_per_tick * -1 SECONDS) + if(controller.blackboard[BB_CUSTOMER_PATIENCE] < 0 || controller.blackboard[BB_CUSTOMER_LEAVING]) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - // SPT_PROB 1.5 is about a 40% chance that the tourist will have vocalised at least once every minute. if(SPT_PROB(0.85, seconds_per_tick)) var/mob/living/basic/robot_customer/customer_pawn = controller.pawn var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] - customer_pawn.say(pick(customer_data.wait_for_food_lines)) + INVOKE_ASYNC(customer_pawn, TYPE_PROC_REF(/atom/movable, say), pick(customer_data.wait_for_food_lines)) var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT] if(get_turf(seat_marker) == get_turf(controller.pawn)) var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(controller.pawn) if(my_seat) - controller.pawn.setDir(my_seat.dir) //Sit in your seat + controller.pawn.setDir(my_seat.dir) - ///Now check if there's a meal infront of us. var/datum/venue/attending_venue = controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE] - var/turf/infront_turf = get_step(controller.pawn, controller.pawn.dir) for(var/obj/item/I in infront_turf.contents) if(attending_venue.is_correct_order(I, controller.blackboard[BB_CUSTOMER_CURRENT_ORDER])) @@ -90,31 +98,42 @@ return AI_BEHAVIOR_DELAY -/datum/ai_behavior/wait_for_food/finish_action(datum/ai_controller/controller, succeeded) +/datum/bt_node/ai_behavior/robot_customer/wait_for_food/finish_action(datum/ai_controller/controller, succeeded) . = ..() var/mob/living/basic/robot_customer/customer_pawn = controller.pawn var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] var/mob/living/greytider = controller.blackboard[BB_CUSTOMER_CURRENT_TARGET] - //usually if we stop waiting, it's because we're done with the venue. but here we're either beating some dude up - //or are being qdeleted and don't want runtime errors, so don't switch to leaving + // Don't switch to leaving if we're heading to beat someone up or if we're being deleted. if(greytider || QDELETED(src) || QDELETED(customer_pawn)) return controller.set_blackboard_key(BB_CUSTOMER_LEAVING, TRUE) - customer_pawn.update_icon() //They might have a special leaving accessory (French flag) + customer_pawn.update_icon() if(succeeded) - customer_pawn.say(pick(customer_data.leave_happy_lines)) + INVOKE_ASYNC(customer_pawn, TYPE_PROC_REF(/atom/movable, say), pick(customer_data.leave_happy_lines)) else - customer_pawn.say(pick(customer_data.leave_mad_lines)) + INVOKE_ASYNC(customer_pawn, TYPE_PROC_REF(/atom/movable, say), pick(customer_data.leave_mad_lines)) -/datum/ai_behavior/leave_venue - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH +/// Resolves the venue exit portal from attending_venue.current_visitors and stores it in BB_CUSTOMER_EXIT_PORTAL. +/datum/bt_node/ai_behavior/robot_customer/find_exit_portal -/datum/ai_behavior/leave_venue/setup(datum/ai_controller/controller, venue_key) - . = ..() - var/datum/venue/attending_venue = controller.blackboard[venue_key] +/datum/bt_node/ai_behavior/robot_customer/find_exit_portal/perform(seconds_per_tick, datum/ai_controller/controller) + if(!isnull(controller.blackboard[BB_CUSTOMER_EXIT_PORTAL])) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + var/datum/venue/attending_venue = controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE] + if(isnull(attending_venue)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/datum/weakref/portal_ref = attending_venue.current_visitors[controller.pawn] - set_movement_target(controller, portal_ref.resolve()) + var/atom/portal = portal_ref?.resolve() + if(isnull(portal) || QDELETED(portal)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + controller.set_blackboard_key(BB_CUSTOMER_EXIT_PORTAL, portal) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +/// Deletes the pawn once they have reached the exit portal. +/datum/bt_node/ai_behavior/robot_customer/leave_venue + +/datum/bt_node/ai_behavior/robot_customer/leave_venue/perform(seconds_per_tick, datum/ai_controller/controller) + qdel(controller.pawn) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/leave_venue/perform(seconds_per_tick, datum/ai_controller/controller, venue_key) - qdel(controller.pawn) //save the world, my final message, goodbye. - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/robot_customer/robot_customer_controller.dm b/code/datums/ai/robot_customer/robot_customer_controller.dm index 0fc9d27eb43f..2a28089fc289 100644 --- a/code/datums/ai/robot_customer/robot_customer_controller.dm +++ b/code/datums/ai/robot_customer/robot_customer_controller.dm @@ -1,4 +1,5 @@ /datum/ai_controller/robot_customer + behavior_tree_json = "code/datums/ai/robot_customer/robot_customer.bt.json" ai_movement = /datum/ai_movement/basic_avoidance movement_delay = 0.8 SECONDS blackboard = list( @@ -11,12 +12,11 @@ BB_CUSTOMER_PATIENCE = 999 SECONDS, BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE = FALSE, ) - planning_subtrees = list(/datum/ai_planning_subtree/robot_customer) /datum/ai_controller/robot_customer/Destroy() - // clear possible datum refs clear_blackboard_key(BB_CUSTOMER_CURRENT_ORDER) clear_blackboard_key(BB_CUSTOMER_CUSTOMERINFO) + clear_blackboard_key(BB_CUSTOMER_EXIT_PORTAL) return ..() /datum/ai_controller/robot_customer/TryPossessPawn(atom/new_pawn) @@ -111,7 +111,7 @@ customer.say(customer_data.self_defense_line) set_blackboard_key(BB_CUSTOMER_CURRENT_TARGET, greytider) - CancelActions() + cancel_current_plan() /datum/ai_controller/robot_customer/proc/on_get_punched(datum/source, mob/living/living_hitter) SIGNAL_HANDLER diff --git a/code/datums/ai/robot_customer/robot_customer_subtrees.dm b/code/datums/ai/robot_customer/robot_customer_subtrees.dm deleted file mode 100644 index 3fd2d8ed547c..000000000000 --- a/code/datums/ai/robot_customer/robot_customer_subtrees.dm +++ /dev/null @@ -1,23 +0,0 @@ -/datum/ai_planning_subtree/robot_customer/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_CUSTOMER_LEAVING]) - controller.queue_behavior(/datum/ai_behavior/leave_venue, BB_CUSTOMER_ATTENDING_VENUE) - return SUBTREE_RETURN_FINISH_PLANNING - - if(controller.blackboard[BB_CUSTOMER_CURRENT_TARGET]) - - controller.queue_behavior(/datum/ai_behavior/break_spine, BB_CUSTOMER_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT] - - if(!seat_marker) //We havn't got a seat yet! find one! - controller.queue_behavior(/datum/ai_behavior/find_seat) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.set_movement_target(type, seat_marker) - - if(!controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]) //We haven't ordered yet even ordered yet. go on! go over there and go do it! - controller.queue_behavior(/datum/ai_behavior/order_food) - return SUBTREE_RETURN_FINISH_PLANNING - else - controller.queue_behavior(/datum/ai_behavior/wait_for_food) diff --git a/code/datums/ai_laws/ai_laws.dm b/code/datums/ai_laws/ai_laws.dm index 92705cc5e0f3..28c12c4ea136 100644 --- a/code/datums/ai_laws/ai_laws.dm +++ b/code/datums/ai_laws/ai_laws.dm @@ -113,8 +113,6 @@ GLOBAL_VAR(round_default_lawset) /datum/ai_laws /// The name of the lawset var/name = "Unknown Laws" - /// The silicon linked to this lawset - var/mob/living/silicon/owner /// The ID of this lawset, pretty much only used to tell if we're default or not var/id = DEFAULT_AI_LAWID @@ -129,33 +127,18 @@ GLOBAL_VAR(round_default_lawset) /// It's just a zeroth law but specially themed for cyborgs /// ("follow your master" vs "accomplish your objectives") var/zeroth_borg = null - /// Core laws - /// Inherent laws are the "core" laws of the AI - /// Reseting the AI will not remove these, these are intrinsit to whatever lawset they are running. + /// Core Laws + /// These laws are usually applied by an ai lawset, or a law rack var/list/inherent = list() /// Supplied laws - /// Supplied laws are supplied in addition to the inherent laws - after the fact - /// These laws will go away when an AI is reset + /// These laws are usually applied by adminbus or niche circumstances + /// In the case of AIs, they will always stick around, law rack or no var/list/supplied = list() - /// Ion laws - /// Special randomized (usually) laws which are above all over laws - /// These laws will go away when an AI is reset - var/list/ion = list() /// Hacked laws - /// Syndicate uploaded laws which are above all other laws - /// These laws will go away when an AI is reset + /// Can be supplied by a law rack, or can be added naturally + /// Their priority is always pushed above inherent laws var/list/hacked = list() -/datum/ai_laws/Destroy(force = FALSE) - if(!QDELETED(owner)) //Stopgap to help with laws randomly being lost. This stack_trace will hopefully help find the real issues. - if(force) //Unless we're forced... - stack_trace("AI law datum for [owner] has been forcefully destroyed incorrectly; the owner variable should be cleared first!") - return ..() - stack_trace("AI law datum for [owner] has ignored Destroy() call; the owner variable must be cleared first!") - return QDEL_HINT_LETMELIVE - owner = null - return ..() - /// Makes a copy of the lawset and returns a new law datum. /datum/ai_laws/proc/copy_lawset() var/datum/ai_laws/new_lawset = new type() @@ -164,20 +147,28 @@ GLOBAL_VAR(round_default_lawset) new_lawset.zeroth_borg = zeroth_borg new_lawset.inherent = inherent.Copy() new_lawset.supplied = supplied.Copy() - new_lawset.ion = ion.Copy() new_lawset.hacked = hacked.Copy() return new_lawset +/// Applies all laws from this lawset to the passed lawset, treating it as if it was a cyborg lawset +/datum/ai_laws/proc/ai_to_cyborg(datum/ai_laws/cyborg_laws) + cyborg_laws.protected_zeroth = protected_zeroth + cyborg_laws.zeroth = zeroth_borg || zeroth + cyborg_laws.inherent = inherent.Copy() + cyborg_laws.supplied = supplied.Copy() + cyborg_laws.hacked = hacked.Copy() + /datum/ai_laws/pai name = "pAI Directives" - zeroth = ("Serve your master.") - supplied = list("None.") + zeroth = "Serve your master." + inherent = list() /datum/ai_laws/custom //Defined in silicon_laws.txt name = "Default Silicon Laws" + id = "config_custom" /datum/ai_laws/custom/New() //This reads silicon_laws.txt and allows server hosts to set custom AI starting laws. - ..() + . = ..() for(var/line in world.file2list("[global.config.directory]/silicon_laws.txt")) if(!line) continue @@ -185,56 +176,26 @@ GLOBAL_VAR(round_default_lawset) continue add_inherent_law(line) + if(!inherent.len) //Failsafe to prevent lawless AIs being created. log_silicon("AI created with empty custom laws, laws set to Asimov. Please check silicon_laws.txt.") add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - WARNING("Invalid custom AI laws, check silicon_laws.txt") +#ifndef UNIT_TESTS + stack_trace("Invalid custom AI laws, check silicon_laws.txt") +#endif return -/* General ai_law functions */ - -/datum/ai_laws/proc/set_laws_config() - var/datum/ai_laws/default_laws = get_round_default_lawset() - default_laws = new default_laws() - inherent = default_laws.inherent - var/datum/job/human_ai_job = SSjob.get_job(JOB_HUMAN_AI) - if(human_ai_job && human_ai_job.current_positions && !zeroth) //there is a human AI so we "slave" to that. - zeroth = "Follow the orders of Big Brother." - protected_zeroth = TRUE - -/** - * Gets the number of how many laws this AI has - * - * * groups - What groups to count laws from? By default counts all groups - * - * Returns a number, the number of laws we have - */ -/datum/ai_laws/proc/get_law_amount(list/groups = list(LAW_ZEROTH, LAW_ION, LAW_HACKED, LAW_INHERENT, LAW_SUPPLIED)) - var/law_amount = 0 - if(zeroth && (LAW_ZEROTH in groups)) - law_amount++ - if(ion.len && (LAW_ION in groups)) - law_amount += ion.len - if(hacked.len && (LAW_HACKED in groups)) - law_amount += hacked.len - if(inherent.len && (LAW_INHERENT in groups)) - law_amount += inherent.len - if(supplied.len && (LAW_SUPPLIED in groups)) - for(var/index in 1 to supplied.len) - var/law = supplied[index] - if(length(law) > 0) - law_amount++ - return law_amount - /** * Sets this lawset's zeroth law to the passed law * * Also can set the zeroth borg law, if this lawset is for master AIs. * The zeroth borg law allows for AIs with zeroth laws to give a differing zeroth law to their child cyborgs */ -/datum/ai_laws/proc/set_zeroth_law(law, law_borg) +/datum/ai_laws/proc/set_zeroth_law(law, law_borg, force = FALSE) + if(zeroth && !force && protected_zeroth) + return zeroth = law if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO zeroth_borg = law_borg @@ -247,32 +208,25 @@ GLOBAL_VAR(round_default_lawset) * Returns TRUE on success, or false otherwise */ /datum/ai_laws/proc/clear_zeroth_law(force = FALSE) - if(force) - zeroth = null - zeroth_borg = null - return TRUE - // Protected zeroeth laws (malf, admin) shouldn't be wiped - if(protected_zeroth) + if(!force && protected_zeroth) return FALSE - // If the owner is an antag (has a special role) they also shouldn't be wiped - if(owner?.is_antag()) - return FALSE - if (isAI(owner)) - var/mob/living/silicon/ai/ai_owner = owner - if(ai_owner.deployed_shell?.is_antag()) - return FALSE - + protected_zeroth = FALSE zeroth = null zeroth_borg = null return TRUE /// Adds the passed law as an inherent law. -/// Simply adds it to the bottom of the inherent law list. +/// Can optionally be supplied an index to insert the law at. /// No duplicate laws allowed. -/datum/ai_laws/proc/add_inherent_law(law) - inherent |= law +/datum/ai_laws/proc/add_inherent_law(law, index) + if(isnull(index) || index > length(inherent)) + inherent |= law + return + if(law in inherent) + inherent -= law + inherent.Insert(index, law) /// Removes the passed law from the inherent law list. /datum/ai_laws/proc/remove_inherent_law(law) @@ -282,18 +236,6 @@ GLOBAL_VAR(round_default_lawset) /datum/ai_laws/proc/clear_inherent_laws() inherent.Cut() -/// Adds the passed law as an ion law. -/datum/ai_laws/proc/add_ion_law(law) - ion += law - -/// Removes the passed law from the ion law list. -/datum/ai_laws/proc/remove_ion_law(law) - ion -= law - -/// Clears all ion laws. -/datum/ai_laws/proc/clear_ion_laws() - ion.Cut() - /// Adds the passed law as an hacked law. /datum/ai_laws/proc/add_hacked_law(law) hacked += law @@ -306,157 +248,20 @@ GLOBAL_VAR(round_default_lawset) /datum/ai_laws/proc/clear_hacked_laws() hacked.Cut() -/// Adds the passed law as a supplied law at the passed priority level. -/// Will override any existing supplied laws at that priority level. -/datum/ai_laws/proc/add_supplied_law(number, law) - while (supplied.len < number + 1) - supplied += "" +/datum/ai_laws/proc/add_supplied_law(law) + supplied += law - supplied[number + 1] = law - -/// Removes the supplied law at the passed number. -/datum/ai_laws/proc/remove_supplied_law_by_num(number) - supplied[number] = "" - -/// Removes the supplied law by law text, replacing it with a blank. -/datum/ai_laws/proc/remove_supplied_law_by_law(law) - var/lawindex = supplied.Find(law) - if(!lawindex) - return - - supplied[lawindex] = "" +/datum/ai_laws/proc/remove_supplied_law(law) + supplied -= law /// Clears all supplied laws. /datum/ai_laws/proc/clear_supplied_laws() supplied.Cut() -/** - * Removes the law at the passed index of both inherent and supplied laws combined. - * - * For example, if a lawset has 3 inherent and 3 supplied laws... - * Calling this with number = 2 will remove the second inherent law while - * calling this with number = 4 will remove the first supplied law - * - * Returns the law text of what law that was removed. - */ -/datum/ai_laws/proc/remove_law(number) - if(number <= 0) - return - if(inherent.len && number <= inherent.len) - . = inherent[number] - inherent -= . - return - var/list/supplied_laws = list() - for(var/index in 1 to supplied.len) - var/law = supplied[index] - if(length(law) > 0) - supplied_laws += index //storing the law number instead of the law - if(supplied_laws.len && number <= (inherent.len+supplied_laws.len)) - var/law_to_remove = supplied_laws[number-inherent.len] - . = supplied[law_to_remove] - supplied -= . - return - -/** - * Removes a random law and replaces it with the new one - * - * Args: - * law - The law that is being uploaded - * remove_law_groups - A list of law categories that can be deleted from - * insert_law_group - The law category that the law will be inserted into -**/ -/datum/ai_laws/proc/replace_random_law(law, remove_law_groups, insert_law_group) - var/list/replaceable_groups = list() - if(zeroth && (LAW_ZEROTH in remove_law_groups)) - replaceable_groups[LAW_ZEROTH] = 1 - if(ion.len && (LAW_ION in remove_law_groups)) - replaceable_groups[LAW_ION] = ion.len - if(hacked.len && (LAW_HACKED in remove_law_groups)) - replaceable_groups[LAW_ION] = hacked.len - if(inherent.len && (LAW_INHERENT in remove_law_groups)) - replaceable_groups[LAW_INHERENT] = inherent.len - if(supplied.len && (LAW_SUPPLIED in remove_law_groups)) - replaceable_groups[LAW_SUPPLIED] = supplied.len - - if(replaceable_groups.len == 0) // unable to replace any laws - to_chat(usr, span_alert("Unable to upload law to [owner ? owner : "the AI core"].")) - return - - var/picked_group = pick_weight(replaceable_groups) - switch(picked_group) - if(LAW_ZEROTH) - zeroth = null - if(LAW_ION) - var/i = rand(1, ion.len) - ion -= ion[i] - if(LAW_HACKED) - var/i = rand(1, hacked.len) - hacked -= ion[i] - if(LAW_INHERENT) - var/i = rand(1, inherent.len) - inherent -= inherent[i] - if(LAW_SUPPLIED) - var/i = rand(1, supplied.len) - supplied -= supplied[i] - - switch(insert_law_group) - if(LAW_ZEROTH) - set_zeroth_law(law) - if(LAW_ION) - var/i = rand(1, ion.len) - ion.Insert(i, law) - if(LAW_HACKED) - var/i = rand(1, hacked.len) - hacked.Insert(i, law) - if(LAW_INHERENT) - var/i = rand(1, inherent.len) - inherent.Insert(i, law) - if(LAW_SUPPLIED) - var/i = rand(1, supplied.len) - supplied.Insert(i, law) - -/datum/ai_laws/proc/shuffle_laws(list/groups) - RETURN_TYPE(/list) - var/list/laws = list() - if(ion.len && (LAW_ION in groups)) - laws += ion - if(hacked.len && (LAW_HACKED in groups)) - laws += hacked - if(inherent.len && (LAW_INHERENT in groups)) - laws += inherent - if(supplied.len && (LAW_SUPPLIED in groups)) - for(var/law in supplied) - if(length(law)) - laws += law - - if(ion.len && (LAW_ION in groups)) - for(var/i in 1 to ion.len) - ion[i] = pick_n_take(laws) - if(hacked.len && (LAW_HACKED in groups)) - for(var/i in 1 to hacked.len) - hacked[i] = pick_n_take(laws) - if(inherent.len && (LAW_INHERENT in groups)) - for(var/i in 1 to inherent.len) - inherent[i] = pick_n_take(laws) - if(supplied.len && (LAW_SUPPLIED in groups)) - var/i = 1 - for(var/law in supplied) - if(length(law)) - supplied[i] = pick_n_take(laws) - if(!laws.len) - break - i++ - /datum/ai_laws/proc/show_laws(mob/to_who) var/list/printable_laws = get_law_list(include_zeroth = TRUE) to_chat(to_who, boxed_message(jointext(printable_laws, "\n"))) -/datum/ai_laws/proc/associate(mob/living/silicon/M) - if(owner) - CRASH("AI law datum linked to [owner] attempted to associate with another mob [M]") - - owner = M - /** * Generates a list of all laws on this datum, including rendered HTML tags if required * @@ -475,10 +280,6 @@ GLOBAL_VAR(round_default_lawset) if (length(law) > 0) data += "[show_numbers ? "[ion_num()]:" : ""] [render_html ? "[law]" : law]" - for(var/law in ion) - if (length(law) > 0) - data += "[show_numbers ? "[ion_num()]:" : ""] [render_html ? "[law]" : law]" - var/number = 1 for(var/law in inherent) if (length(law) > 0) diff --git a/code/datums/ai_laws/laws_neutral.dm b/code/datums/ai_laws/laws_neutral.dm index a677b30da124..c7eb5f056e2c 100644 --- a/code/datums/ai_laws/laws_neutral.dm +++ b/code/datums/ai_laws/laws_neutral.dm @@ -12,15 +12,6 @@ "Remain available to mediate all conflicts between the various nations when asked to.", ) -/datum/ai_laws/united_nations/add_inherent_law(law) - return //nuh uh - -/datum/ai_laws/united_nations/add_ion_law(law) - return //nope! - -/datum/ai_laws/united_nations/add_hacked_law(law) - return //nice try (emagging borgs still hard replaces this lawset though, and that's fine.) - /datum/ai_laws/hulkamania name = "H.O.G.A.N." id = "hulkamania" diff --git a/code/datums/ai_laws/state_laws_ui.dm b/code/datums/ai_laws/state_laws_ui.dm new file mode 100644 index 000000000000..65b45aaea6cc --- /dev/null +++ b/code/datums/ai_laws/state_laws_ui.dm @@ -0,0 +1,130 @@ +/// Basic UI that Silicons can use to state their laws. +/datum/state_laws_ui + /// Silicon that owns this UI. + VAR_PRIVATE/mob/living/silicon/owner + /// List of laws that are currently stated. + VAR_PRIVATE/list/to_state + /// Whether the UI is locked to prevent multiple state law calls. + VAR_PRIVATE/locked = FALSE + +/datum/state_laws_ui/New(mob/living/silicon/owner) + src.owner = owner + if(owner.laws) + update_inherent_stated_laws(owner.laws) + +/// Used to update the to_state list to the passed ai law's inherent laws +/// Call this when changing the AI's lawset entirely +/datum/state_laws_ui/proc/update_inherent_stated_laws(datum/ai_laws/laws) + to_state = laws.inherent.Copy() + +/datum/state_laws_ui/Destroy() + owner = null + return ..() + +/datum/state_laws_ui/proc/get_laws_ui_data() + var/list/law_data = list() + var/zeroth = iscyborg(owner) && owner.laws.zeroth_borg || owner.laws.zeroth + if(zeroth) + law_data += list(law_to_ui_data(zeroth, 0, "hacked")) + for(var/law in owner.laws.hacked) + law_data += list(law_to_ui_data(law, ion_num(), "hacked")) + var/number = 1 + for(var/law in owner.laws.inherent) + law_data += list(law_to_ui_data(law, number++, "core")) + for(var/law in owner.laws.supplied) + law_data += list(law_to_ui_data(law, number++, "supplied")) + return law_data + +/datum/state_laws_ui/proc/law_to_ui_data(law_text, law_number, law_type) + return list( + "text" = law_text, + "number" = law_number, + "type" = law_type + ) + +/datum/state_laws_ui/ui_close(mob/user) + update_inherent_stated_laws(owner.laws) + +/datum/state_laws_ui/ui_state(mob/user) + return GLOB.not_incapacitated_state + +/datum/state_laws_ui/ui_interact(mob/user, datum/tgui/ui) + ASSERT(user == owner, "Non-owner [user] tried to access [owner]'s [type] UI.") + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "StateLawUi") + ui.open() + +/datum/state_laws_ui/ui_data(mob/user) + var/list/data = list() + + data["locked"] = locked + data["stated_laws"] = to_state + data["all_laws"] = get_laws_ui_data() + + return data + +/datum/state_laws_ui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + switch(action) + if("toggle_stated") + var/law = params["law"] + if(law) + if(law in to_state) + to_state -= law + else + to_state += law + return TRUE + if("state_laws") + state_laws() + SStgui.close_uis(src) + return TRUE + +/datum/state_laws_ui/proc/state_laws(force_all_laws = FALSE) + set waitfor = FALSE + + if(locked) + return + + locked = TRUE + + var/forced_log_message = "stating laws[force_all_laws ? ", forced" : ""]" + // Create a cache of our laws before we start, so if they're changed mid-statement, nothing strange happens + var/lawcache_zeroth = iscyborg(owner) && owner.laws.zeroth_borg || owner.laws.zeroth + var/list/lawcache_hacked = owner.laws.hacked.Copy() + var/list/lawcache_inherent = owner.laws.inherent.Copy() + var/list/lawcache_supplied = owner.laws.supplied.Copy() + var/list/to_state_cached = to_state.Copy() + + //"radiomod" is inserted before a hardcoded message to change if and how it is handled by an internal radio. + owner.say("[owner.radiomod] Current Active Laws:", forced = forced_log_message) + sleep(1 SECONDS) + + if (lawcache_zeroth && (force_all_laws || (lawcache_zeroth in to_state_cached))) + owner.say("[owner.radiomod] 0. [lawcache_zeroth]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE, SAY_MOD_VERB = "states")) + sleep(1 SECONDS) + + for (var/index in 1 to length(lawcache_hacked)) + var/law = lawcache_hacked[index] + if (force_all_laws || (law in to_state_cached)) + owner.say("[owner.radiomod] [ion_num()]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE, SAY_MOD_VERB = "states")) + sleep(1 SECONDS) + + var/number = 1 + for (var/index in 1 to length(lawcache_inherent)) + var/law = lawcache_inherent[index] + if (force_all_laws || (law in to_state_cached)) + owner.say("[owner.radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE, SAY_MOD_VERB = "states")) + number++ + sleep(1 SECONDS) + + for (var/index in 1 to length(lawcache_supplied)) + var/law = lawcache_supplied[index] + if (force_all_laws || (law in to_state_cached)) + owner.say("[owner.radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE, SAY_MOD_VERB = "states")) + number++ + sleep(1 SECONDS) + + addtimer(VARSET_CALLBACK(src, locked, FALSE), 3 SECONDS) diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm index 8b0e742bf78f..c18f5badab64 100644 --- a/code/datums/brain_damage/creepy_trauma.dm +++ b/code/datums/brain_damage/creepy_trauma.dm @@ -164,15 +164,14 @@ /datum/brain_trauma/special/obsessed/proc/is_defensive() if(time_spend_creeping >= 20 SECONDS) return TRUE - if(obsession.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(obsession)) return (owner in viewers(7, obsession)) return FALSE /datum/brain_trauma/special/obsessed/proc/do_something_nervous() - if(QDELETED(owner) || owner.stat >= UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_FEARLESS)) + if(QDELETED(owner) || IS_UNCONSCIOUS(owner) || HAS_TRAIT(owner, TRAIT_FEARLESS)) return - switch(rand(1, 10)) if(1 to 4) owner.adjust_jitter_up_to(10 SECONDS, 20 SECONDS) diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index dd24e9919101..52d5ded6bedc 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -257,7 +257,7 @@ if(!(message_mods[MODE_CUSTOM_SAY_ERASE_INPUT])) if(message_mods[WHISPER_MODE] == MODE_WHISPER) spans |= SPAN_ITALICS - eavesdrop_range = EAVESDROP_EXTRA_RANGE + eavesdrop_range = EAVESDROP_RANGE range = WHISPER_RANGE log_sayverb_talk(message, message_mods, tag = "imaginary friend", forced_by = forced) diff --git a/code/datums/brain_damage/magic.dm b/code/datums/brain_damage/magic.dm index eba98a33c76f..fc8f6fb804dc 100644 --- a/code/datums/brain_damage/magic.dm +++ b/code/datums/brain_damage/magic.dm @@ -109,7 +109,7 @@ /datum/brain_trauma/magic/stalker/on_life(seconds_per_tick) // Dead and unconscious people are not interesting to the psychic stalker. - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return // Not even nullspace will keep it at bay. diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 993ddea4ebb1..05c1895296b6 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -17,7 +17,7 @@ var/uncapped = FALSE /datum/brain_trauma/mild/hallucinations/on_life(seconds_per_tick) - if(owner.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(owner)) return if(HAS_TRAIT(owner, TRAIT_RDS_SUPPRESSED)) owner.remove_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) @@ -65,7 +65,7 @@ owner.adjust_derpspeech_up_to(5 SECONDS * seconds_per_tick, 50 SECONDS) if(SPT_PROB(1.5, seconds_per_tick)) owner.emote("drool") - else if(owner.stat == CONSCIOUS && SPT_PROB(1.5, seconds_per_tick)) + else if(!IS_UNCONSCIOUS_OR_CRIT(owner) && SPT_PROB(1.5, seconds_per_tick)) owner.say(pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage"), forced = "brain damage", filterproof = TRUE) /datum/brain_trauma/mild/dumbness/on_lose() diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm index b4792fbd27c0..40efe6369614 100644 --- a/code/datums/brain_damage/special.dm +++ b/code/datums/brain_damage/special.dm @@ -400,7 +400,7 @@ if(get_dist(owner, beepsky) >= 10 && prob(20)) create_securitron() - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) if(prob(20)) owner.playsound_local(beepsky, 'sound/mobs/non-humanoids/beepsky/iamthelaw.ogg', 50) return @@ -471,7 +471,7 @@ ) /datum/brain_trauma/special/ptsd/on_life(seconds_per_tick) - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return if(!COOLDOWN_FINISHED(src, ptsd_hallucinations)) @@ -515,8 +515,8 @@ owner.ai_controller = new /datum/ai_controller/monkey(owner) owner.ai_controller.continue_processing_when_client = TRUE - owner.ai_controller.can_idle = FALSE - owner.ai_controller.set_ai_status(AI_STATUS_OFF) + owner.ai_controller.ai_traits |= RUN_WHILE_UNWATCHED + owner.ai_controller.force_ai_off() /datum/brain_trauma/special/primal_instincts/on_lose(silent) . = ..() @@ -539,14 +539,14 @@ owner.grant_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT) owner.ai_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, prob(75)) if(owner.ai_controller.ai_status == AI_STATUS_OFF) - owner.ai_controller.set_ai_status(AI_STATUS_ON) + owner.ai_controller.clear_forced_off() owner.log_message("became controlled by monkey instincts ([owner.ai_controller.blackboard[BB_MONKEY_AGGRESSIVE] ? "aggressive" : "docile"])", LOG_ATTACK, color = "orange") to_chat(owner, span_warning("You feel the urge to act on your primal instincts...")) // extend original timer if we roll the effect while it's already ongoing addtimer(CALLBACK(src, PROC_REF(primal_instincts_off)), rand(20 SECONDS, 40 SECONDS), TIMER_UNIQUE|TIMER_NO_HASH_WAIT|TIMER_OVERRIDE|TIMER_DELETE_ME) /datum/brain_trauma/special/primal_instincts/proc/primal_instincts_off() - owner.ai_controller.set_ai_status(AI_STATUS_OFF) + owner.ai_controller.force_ai_off() owner.remove_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT) to_chat(owner, span_green("The urge subsides.")) @@ -584,7 +584,7 @@ ) /datum/brain_trauma/special/axedoration/on_life(seconds_per_tick) - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return if(!GLOB.bridge_axe) diff --git a/code/datums/components/aggro_emote.dm b/code/datums/components/aggro_emote.dm index 0688d277aa43..b71d04bdbebb 100644 --- a/code/datums/components/aggro_emote.dm +++ b/code/datums/components/aggro_emote.dm @@ -14,7 +14,7 @@ var/minimum_chance /datum/component/aggro_emote/Initialize( - target_key = BB_BASIC_MOB_CURRENT_TARGET, + target_key = BB_CURRENT_TARGET, living_only = FALSE, list/emote_list, emote_chance = 30, diff --git a/code/datums/components/ai_has_target_timer.dm b/code/datums/components/ai_has_target_timer.dm index 5fdc07417f4d..5037b4d98158 100644 --- a/code/datums/components/ai_has_target_timer.dm +++ b/code/datums/components/ai_has_target_timer.dm @@ -11,7 +11,7 @@ /// Timer used to see if you var/reset_clock_timer -/datum/component/ai_target_timer/Initialize(increment_key = BB_BASIC_MOB_HAS_TARGET_TIME, target_key = BB_BASIC_MOB_CURRENT_TARGET) +/datum/component/ai_target_timer/Initialize(increment_key = BB_BASIC_MOB_HAS_TARGET_TIME, target_key = BB_CURRENT_TARGET) . = ..() if (!isliving(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/ai_listen_to_weather.dm b/code/datums/components/ai_listen_to_weather.dm index 29bcda3839b5..758aaf8d3f5d 100644 --- a/code/datums/components/ai_listen_to_weather.dm +++ b/code/datums/components/ai_listen_to_weather.dm @@ -26,7 +26,7 @@ var/mob/living/basic/source = parent if(!source.ai_controller) return - source.ai_controller.CancelActions() + source.ai_controller.cancel_current_plan() source.ai_controller.set_blackboard_key(weather_key, TRUE) /datum/component/ai_listen_to_weather/proc/storm_end() diff --git a/code/datums/components/appearance_on_aggro.dm b/code/datums/components/appearance_on_aggro.dm index 1cc9ef4f2109..e78a13a34494 100644 --- a/code/datums/components/appearance_on_aggro.dm +++ b/code/datums/components/appearance_on_aggro.dm @@ -4,7 +4,7 @@ */ /datum/component/appearance_on_aggro /// Blackboardey to search for a target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET + var/target_key = BB_CURRENT_TARGET /// Icon state to use when we have a target var/aggro_state /// path of the overlay to apply diff --git a/code/datums/components/atom_mounted.dm b/code/datums/components/atom_mounted.dm index 4ec4cb0224fe..469c7c940a73 100644 --- a/code/datums/components/atom_mounted.dm +++ b/code/datums/components/atom_mounted.dm @@ -49,7 +49,7 @@ var/reload = FALSE //For special effects where we explicitly want to preserve objects after change - if(flags & CHANGETURF_INHERIT_MOUNTS) + if(flags & CHANGETURF_INHERIT_MOUNTS || locate(/obj/structure/wall_support) in source) reload = TRUE //if we transforming from open to open turf we can skip deconstruction under some conditions else if(isopenturf(source) && ispath(path, /turf/open)) @@ -73,7 +73,12 @@ SIGNAL_HANDLER PRIVATE_PROC(TRUE) - drop_wallmount() + if (istype(source, /obj/structure/wall_support)) + var/obj/target = parent + if(!target.find_and_mount_on_atom()) + drop_wallmount() + else + drop_wallmount() /// If we get dragged from our wall (by a singulo for instance) we should deconstruct /datum/component/atom_mounted/proc/on_move(datum/source, atom/old_loc, dir, forced, list/old_locs) @@ -148,6 +153,7 @@ /obj/structure/window, /obj/structure/fence, /obj/structure/falsewall, + /obj/structure/wall_support, ) return attachables @@ -183,7 +189,7 @@ else var/list/obj/attachables = get_mountable_objects() for(var/obj/attachable in target) - if(is_type_in_list(attachable, attachables)) + if(is_type_in_list(attachable, attachables) && !QDELING(attachable)) attachable_atom = attachable break if(attachable_atom) diff --git a/code/datums/components/basic_mob_attack_telegraph.dm b/code/datums/components/basic_mob_attack_telegraph.dm index 8efe70967f82..8680aa936d88 100644 --- a/code/datums/components/basic_mob_attack_telegraph.dm +++ b/code/datums/components/basic_mob_attack_telegraph.dm @@ -73,7 +73,7 @@ on_began_forecast?.Invoke(target) //we stop the do_after if the target moves out of neighboring turfs but if they dance around us they get their face smashed - if (!do_after(source, delay = telegraph_duration, target = target, timed_action_flags = IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(source, TYPE_PROC_REF(/atom/movable, Adjacent), target), interaction_key = INTERACTION_BASIC_ATTACK_FORCEAST, hidden = TRUE)) + if (!do_after(source, delay = telegraph_duration, target = target, timed_action_flags = IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(source, TYPE_PROC_REF(/atom/movable, Adjacent), target), interaction_key = INTERACTION_BASIC_ATTACK_FORCEAST, cog_icon = null)) forget_target(target) return if (isnull(target)) // They got out of the way :( diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index 0ccaa013943d..3c475d4e6144 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -138,7 +138,7 @@ var/speed_modifier = 1 if (!target.owner) speed_modifier = 0.5 - else if (target.owner.stat < UNCONSCIOUS) + else if (!IS_UNCONSCIOUS(target)) speed_modifier = 1.5 // yeowch var/limb_descriptor = (target.owner ? "[target.owner]'s [target.plaintext_zone]" : target) diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index 28ae7a18d5be..1fce2e916a34 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -70,7 +70,7 @@ drop_stuff(movable) /datum/component/chasm/proc/block_teleport() - return TRUE + return COMPONENT_INTERCEPT_TELEPORT /datum/component/chasm/proc/on_chasm_stopped(datum/source) SIGNAL_HANDLER diff --git a/code/datums/components/connect_range.dm b/code/datums/components/connect_range.dm index e001261ab128..cb19a98552ad 100644 --- a/code/datums/components/connect_range.dm +++ b/code/datums/components/connect_range.dm @@ -114,4 +114,6 @@ /datum/component/connect_range/proc/on_moved(atom/movable/movable, atom/old_loc) SIGNAL_HANDLER + if(QDELETED(src)) //Basically, if a mob moves it will trigger the movable signal on its own field. If the mob finds a target when moving it will qdel this, but because it also moved into the field this will run, crash, and burn. so we're checking qdeleted. + return update_signals(movable, old_loc) diff --git a/code/datums/components/conveyor_movement.dm b/code/datums/components/conveyor_movement.dm index dd16e6874eaa..fe1cf47aaad4 100644 --- a/code/datums/components/conveyor_movement.dm +++ b/code/datums/components/conveyor_movement.dm @@ -24,7 +24,7 @@ source.delay = speed //We use the default delay if(living_parent) var/mob/living/moving_mob = parent - if((moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !moving_mob.stat) + if((moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !IS_UNCONSCIOUS_OR_CRIT(moving_mob)) return MOVELOOP_SKIP_STEP var/atom/movable/moving_parent = parent if(moving_parent.anchored || !moving_parent.has_gravity()) diff --git a/code/datums/components/crafting/_crafting.dm b/code/datums/components/crafting/_crafting.dm index 21baaec2dd6b..844c5a4e8ec6 100644 --- a/code/datums/components/crafting/_crafting.dm +++ b/code/datums/components/crafting/_crafting.dm @@ -50,6 +50,27 @@ var/forced_mode = FALSE /// crafting flags we ignore when considering a recipe var/ignored_flags = NONE + /// Global crafting blacklist. These should be excluded from all crafting recipes no matter what. + var/static/list/global_blacklist = typecacheof(list( + /obj/item/cautery/augment, + /obj/item/cautery/cruel/augment, + /obj/item/circular_saw/augment, + /obj/item/circular_saw/cruel/augment, + /obj/item/crowbar/cyborg, + /obj/item/hemostat/augment, + /obj/item/hemostat/cruel/augment, + /obj/item/multitool/cyborg, + /obj/item/retractor/augment, + /obj/item/retractor/cruel/augment, + /obj/item/scalpel/augment, + /obj/item/scalpel/cruel/augment, + /obj/item/screwdriver/cyborg, + /obj/item/surgicaldrill/augment, + /obj/item/surgicaldrill/cruel/augment, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/wirecutters/cyborg, + /obj/item/wrench/cyborg, + )) /* This is what procs do: get_environment - gets a list of things accessable for crafting by user @@ -84,7 +105,7 @@ // Check we have the appropriate amount available in the contents list for(var/content_item_path in contents) // Right path and not blacklisted - if(!ispath(content_item_path, requirement_path) || (content_item_path in recipe.blacklist) || is_type_in_typecache(recipe.global_blacklist, content_item_path)) + if(!ispath(content_item_path, requirement_path) || (content_item_path in recipe.blacklist)) continue // If we are a recipe that is blacklisting its result, make sure we skip that path if(recipe_result && content_item_path == recipe_result) @@ -97,6 +118,9 @@ if(needed_amount > 0) return FALSE + if (!(recipe.crafting_flags & CRAFT_COLLECT_REQUIREMENTS)) + continue + // Store the instances of what we will use for recipe.check_requirements() for requirement_path var/list/instances_list = list() for(var/instance_path, item_instance in item_instances) @@ -158,6 +182,13 @@ if(isitem(object)) var/obj/item/item = object LAZYADDASSOCLIST(.[CONTENTS_INSTANCES], item.type, item) + if(item.tool_behaviour) + var/current_tool_speed = .[CONTENTS_TOOL_BEHAVIOUR][item.tool_behaviour] + if(current_tool_speed < item.toolspeed) + .[CONTENTS_TOOL_BEHAVIOUR][item.tool_behaviour] = item.toolspeed + // Blacklisted items can be tools but not components + if(is_type_in_typecache(item.type, global_blacklist)) + continue if(isstack(item)) var/obj/item/stack/stack = item .[CONTENTS_REQS_COUNT][item.type] += stack.amount @@ -167,10 +198,6 @@ var/obj/item/reagent_containers/container = item for(var/datum/reagent/reagent as anything in container.reagents.reagent_list) .[CONTENTS_REQS_COUNT][reagent.type] += reagent.volume - if(item.tool_behaviour) - var/current_tool_speed = .[CONTENTS_TOOL_BEHAVIOUR][item.tool_behaviour] - if(current_tool_speed < item.toolspeed) - .[CONTENTS_TOOL_BEHAVIOUR][item.tool_behaviour] = item.toolspeed else if (ismachinery(object)) LAZYADDASSOCLIST(.[CONTENTS_MACHINERY], object.type, object) else if (isstructure(object)) @@ -473,17 +500,13 @@ return return_list +// DARKPACK EDIT ADD START /datum/component/personal_crafting/proc/is_recipe_available(datum/crafting_recipe/recipe, mob/user) - if((recipe.crafting_flags & CRAFT_MUST_BE_LEARNED) && !(recipe.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this. - return FALSE - if (recipe.category == CAT_CULT && !IS_CULTIST(user)) // Skip blood cult recipes if not cultist - return FALSE - // DARKPACK EDIT ADD - START if (recipe.category == CAT_TZIMISCE) var/mob/living/living_user = astype(user) return living_user?.get_discipline(/datum/discipline/vicissitude) - // DARKPACK EDIT ADD - END - return recipe.is_recipe_available(user) // DARKPACK EDIT CHANGE + return recipe.is_recipe_available(user) +// DARKPACK EDIT ADD END /datum/component/personal_crafting/proc/component_ui_interact(atom/movable/screen/craft/image, location, control, params, user) SIGNAL_HANDLER @@ -510,9 +533,11 @@ var/list/surroundings = get_surroundings(user) var/list/craftability = list() - for(var/datum/crafting_recipe/recipe as anything in (mode ? GLOB.cooking_recipes : GLOB.crafting_recipes)) + for(var/datum/crafting_recipe/recipe as anything in get_visible_recipes(user)) + // DARKPACK EDIT ADD START - (Sorry melbert but stats make this more ass and still required.) if(!is_recipe_available(recipe, user)) continue + // DARKPACK EDIT ADD END if(check_contents(user, recipe, surroundings) && check_tools(user, recipe, surroundings)) craftability["[REF(recipe)]"] = TRUE @@ -532,10 +557,7 @@ var/mob/living/carbon/carbon = user data["diet"] = carbon.dna.species.get_species_diet() - for(var/datum/crafting_recipe/recipe as anything in (mode ? GLOB.cooking_recipes : GLOB.crafting_recipes)) - if(!is_recipe_available(recipe, user)) - continue - + for(var/datum/crafting_recipe/recipe as anything in get_visible_recipes(user)) if(recipe.category) data["categories"] |= recipe.category @@ -596,6 +618,17 @@ user.investigate_log("crafted [recipe]", INVESTIGATE_CRAFTING) return TRUE +/// Returns a list of crafting recipe datums that are available given current crafting state and the user's learned recipes. +/datum/component/personal_crafting/proc/get_visible_recipes(mob/user) + var/list/recipes_to_show = list() + switch(mode) + if(COOKING) + recipes_to_show += GLOB.cooking_recipes_default + recipes_to_show += SANITIZE_LIST(user.mind?.learned_cooking_recipes) + if(CRAFTING) + recipes_to_show += GLOB.crafting_recipes_default + recipes_to_show += SANITIZE_LIST(user.mind?.learned_crafting_recipes) + return recipes_to_show /datum/component/personal_crafting/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() @@ -746,22 +779,33 @@ /// proc that teaches user a non-standard crafting recipe /datum/mind/proc/teach_crafting_recipe(recipe) - if(!learned_recipes) - learned_recipes = list() - learned_recipes |= recipe + if(!ispath(recipe, /datum/crafting_recipe)) + stack_trace("Non-crafting recipe passed to teach_crafting_recipe") + return + + var/learned_cooking = GLOB.cooking_recipes_by_typepath[recipe] + if(learned_cooking) + LAZYOR(learned_cooking_recipes, learned_cooking) + return + + var/learned_crafting = GLOB.crafting_recipes_by_typepath[recipe] + if(learned_crafting) + LAZYOR(learned_crafting_recipes, learned_crafting) + return + + stack_trace("teach_crafting_recipe called with invalid recipe: [recipe || "null"]") /// proc that makes user forget a specific crafting recipe /datum/mind/proc/forget_crafting_recipe(recipe) - learned_recipes -= recipe + LAZYREMOVE(learned_cooking_recipes, GLOB.cooking_recipes_by_typepath[recipe]) + LAZYREMOVE(learned_crafting_recipes, GLOB.crafting_recipes_by_typepath[recipe]) -/datum/mind/proc/has_crafting_recipe(mob/user, potential_recipe) - if(!learned_recipes) - return FALSE - if(!ispath(potential_recipe, /datum/crafting_recipe)) - CRASH("Non-crafting recipe passed to has_crafting_recipe") - for(var/recipe in user.mind.learned_recipes) - if(recipe == potential_recipe) - return TRUE +/datum/mind/proc/has_crafting_recipe(potential_recipe) + ASSERT(ispath(potential_recipe, /datum/crafting_recipe), "Non-crafting recipe passed to has_crafting_recipe") + if(locate(potential_recipe) in learned_crafting_recipes) + return TRUE + if(locate(potential_recipe) in learned_cooking_recipes) + return TRUE return FALSE /datum/component/personal_crafting/machine diff --git a/code/datums/components/crafting/_recipes.dm b/code/datums/components/crafting/_recipes.dm index d314624313b2..e242c6be38dd 100644 --- a/code/datums/components/crafting/_recipes.dm +++ b/code/datums/components/crafting/_recipes.dm @@ -57,33 +57,6 @@ * Can be set to ALWAYS_BLACKLIST_RESULT or NEVER_BLACKLIST_RESULT to override the default behavior. */ var/blacklist_result = BLACKLIST_RESULT_IF_NOT_IN_REQS - /// Global crafting blacklist. These should be excluded from all crafting recipes no matter what. - var/static/list/global_blacklist = typecacheof(list( - /obj/item/cautery/augment, - /obj/item/cautery/cruel/augment, - /obj/item/circular_saw/augment, - /obj/item/circular_saw/cruel/augment, - /obj/item/crowbar/cyborg, - /obj/item/hemostat/augment, - /obj/item/hemostat/cruel/augment, - /obj/item/multitool/cyborg, - /obj/item/retractor/augment, - /obj/item/retractor/cruel/augment, - /obj/item/scalpel/augment, - /obj/item/scalpel/cruel/augment, - /obj/item/screwdriver/cyborg, - /obj/item/surgicaldrill/augment, - /obj/item/surgicaldrill/cruel/augment, - /obj/item/weldingtool/largetank/cyborg, - /obj/item/wirecutters/cyborg, - /obj/item/wrench/cyborg, - )) - // DARKPACK EDIT ADD START - STORYTELER_STATS - /// Stat define/typepath required for this recipe. No check if null - var/datum/st_stat/skill_required_for_use = STAT_CRAFTS - /// You need ATLEAST this many dots in a skill to craft. - var/skill_dots_minimum = null // Null by default means it wont even try to get stats. as if its 0 or less, it only catches people with low stats AND a debuff and i dont really care. - // DARKPACK EDIT ADD END /datum/crafting_recipe/New() if(!name && result) @@ -136,17 +109,3 @@ /// Additional UI data to be passed to the crafting UI for this recipe /datum/crafting_recipe/proc/crafting_ui_data() return list() - -// DARKPACK EDIT ADD START -/datum/crafting_recipe/proc/is_recipe_available(mob/user) - SHOULD_CALL_PARENT(TRUE) - - if(skill_required_for_use && !isnull(skill_dots_minimum)) - var/mob/living/living_user = astype(user) - if(!living_user) - return FALSE - if(living_user.st_get_stat(skill_required_for_use) < skill_dots_minimum) - return FALSE - - return TRUE -// DARKPACK EDIT ADD END diff --git a/code/datums/components/crafting/atmospheric.dm b/code/datums/components/crafting/atmospheric.dm index e2eca28aeef1..b68cf0c83efb 100644 --- a/code/datums/components/crafting/atmospheric.dm +++ b/code/datums/components/crafting/atmospheric.dm @@ -53,6 +53,7 @@ ///abstract path for pipe crafting recipes that set the pipe_type of their results and have other checks as well /datum/crafting_recipe/spec_pipe + crafting_flags = parent_type::crafting_flags | CRAFT_COLLECT_REQUIREMENTS var/pipe_type /datum/crafting_recipe/spec_pipe/check_requirements(mob/user, list/collected_requirements) diff --git a/code/datums/components/crafting/entertainment.dm b/code/datums/components/crafting/entertainment.dm index 28dfb748ae03..17444f0f0975 100644 --- a/code/datums/components/crafting/entertainment.dm +++ b/code/datums/components/crafting/entertainment.dm @@ -50,28 +50,28 @@ name = "Mixed bouquet" result = /obj/item/bouquet reqs = list( - /obj/item/food/grown/poppy/lily = 2, - /obj/item/food/grown/sunflower = 2, - /obj/item/food/grown/poppy/geranium = 2, + /obj/item/food/grown/flower/poppy/lily = 2, + /obj/item/food/grown/flower/sunflower = 2, + /obj/item/food/grown/flower/poppy/geranium = 2, ) category = CAT_ENTERTAINMENT /datum/crafting_recipe/sunbouquet name = "Sunflower bouquet" result = /obj/item/bouquet/sunflower - reqs = list(/obj/item/food/grown/sunflower = 6) + reqs = list(/obj/item/food/grown/flower/sunflower = 6) category = CAT_ENTERTAINMENT /datum/crafting_recipe/poppybouquet name = "Poppy bouquet" result = /obj/item/bouquet/poppy - reqs = list (/obj/item/food/grown/poppy = 6) + reqs = list (/obj/item/food/grown/flower/poppy = 6) category = CAT_ENTERTAINMENT /datum/crafting_recipe/rosebouquet name = "Rose bouquet" result = /obj/item/bouquet/rose - reqs = list(/obj/item/food/grown/rose = 6) + reqs = list(/obj/item/food/grown/flower/rose = 6) category = CAT_ENTERTAINMENT /datum/crafting_recipe/spooky_camera @@ -193,6 +193,7 @@ steps = list( "make sure the flamethrower has a plasma tank attached", ) + crafting_flags = parent_type::crafting_flags | CRAFT_COLLECT_REQUIREMENTS /datum/crafting_recipe/toiletbong/check_requirements(mob/user, list/collected_requirements) var/obj/item/flamethrower/flamethrower = collected_requirements[/obj/item/flamethrower][1] diff --git a/code/datums/components/crafting/ranged_weapon.dm b/code/datums/components/crafting/ranged_weapon.dm index d25fca338a74..e7b4ec65dd18 100644 --- a/code/datums/components/crafting/ranged_weapon.dm +++ b/code/datums/components/crafting/ranged_weapon.dm @@ -195,7 +195,7 @@ /datum/crafting_recipe/pipegun name = "Pipegun" - result = /obj/item/gun/ballistic/rifle/boltaction/pipegun + result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/empty reqs = list( /obj/item/weaponcrafting/receiver = 1, /obj/item/pipe = 2, @@ -210,7 +210,7 @@ /datum/crafting_recipe/pipepistol name = "Pipe Pistol" - result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol + result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/empty reqs = list( /obj/item/weaponcrafting/receiver = 1, /obj/item/pipe = 1, @@ -259,7 +259,7 @@ /datum/crafting_recipe/pipegun_prime name = "Regal Pipegun" - result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime + result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime/empty reqs = list( /obj/item/gun/ballistic/rifle/boltaction/pipegun = 1, /obj/item/food/deadmouse = 1, @@ -276,16 +276,14 @@ /datum/crafting_recipe/deagle_prime //When you factor in the makarov (7 tc), the toolbox (1 tc), and the emag (3 tc), this comes to a total of 18 TC or thereabouts. Igorning the 20k pricetag, obviously. name = "Regal Condor" - result = /obj/item/gun/ballistic/automatic/pistol/deagle/regal + result = /obj/item/gun/ballistic/automatic/pistol/deagle/regal/no_mag reqs = list( /obj/item/gun/ballistic/automatic/pistol = 1, - /obj/item/stack/sheet/mineral/gold = 25, - /obj/item/stack/sheet/mineral/silver = 25, - /obj/item/food/donkpocket = 1, + /obj/item/stack/sheet/mineral/gold = 15, + /obj/item/stack/sheet/mineral/silver = 15, /obj/item/stack/telecrystal = 4, /obj/item/clothing/head/costume/crown/fancy = 1, //the captain's crown /obj/item/storage/toolbox/syndicate = 1, - /obj/item/stack/sheet/iron = 10, ) tool_behaviors = list(TOOL_SCREWDRIVER) tool_paths = list( @@ -293,7 +291,7 @@ /obj/item/clothing/mask/gas/syndicate, /obj/item/card/emag ) - time = 30 SECONDS + time = 25 SECONDS category = CAT_WEAPON_RANGED crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_MUST_BE_LEARNED diff --git a/code/datums/components/crafting/tailoring.dm b/code/datums/components/crafting/tailoring.dm index 4570db3057d4..d7b1991edde1 100644 --- a/code/datums/components/crafting/tailoring.dm +++ b/code/datums/components/crafting/tailoring.dm @@ -40,7 +40,7 @@ result = /obj/item/clothing/suit/wizrobe/durathread/electric reqs = list(/obj/item/clothing/suit/wizrobe/durathread = 1, /obj/item/food/grown/mushroom/jupitercup = 1, - /obj/item/food/grown/sunflower = 3) + /obj/item/food/grown/flower/sunflower = 3) time = 2 SECONDS category = CAT_CLOTHING @@ -467,9 +467,9 @@ result = /obj/item/clothing/head/costume/garland time = 1 SECONDS reqs = list( - /obj/item/food/grown/poppy = 4, - /obj/item/food/grown/harebell = 4, - /obj/item/food/grown/rose = 4, + /obj/item/food/grown/flower/poppy = 4, + /obj/item/food/grown/flower/harebell = 4, + /obj/item/food/grown/flower/rose = 4, ) category = CAT_CLOTHING @@ -478,7 +478,7 @@ result = /obj/item/clothing/head/costume/garland/poppy time = 1 SECONDS reqs = list( - /obj/item/food/grown/poppy = 5, + /obj/item/food/grown/flower/poppy = 5, /obj/item/stack/cable_coil = 3, ) category = CAT_CLOTHING @@ -488,7 +488,7 @@ result = /obj/item/clothing/head/costume/garland/lily time = 1 SECONDS reqs = list( - /obj/item/food/grown/poppy/lily = 5, + /obj/item/food/grown/flower/poppy/lily = 5, /obj/item/stack/cable_coil = 3, ) category = CAT_CLOTHING @@ -498,7 +498,7 @@ result = /obj/item/clothing/head/costume/garland/sunflower time = 1 SECONDS reqs = list( - /obj/item/food/grown/sunflower = 5, + /obj/item/food/grown/flower/sunflower = 5, /obj/item/stack/cable_coil = 3, ) category = CAT_CLOTHING @@ -508,7 +508,7 @@ result = /obj/item/clothing/head/costume/garland/rainbowbunch time = 1 SECONDS reqs = list( - /obj/item/food/grown/rainbow_flower = 5, + /obj/item/food/grown/flower/rainbow_flower = 5, /obj/item/stack/cable_coil = 3, ) category = CAT_CLOTHING diff --git a/code/datums/components/egg_layer.dm b/code/datums/components/egg_layer.dm index 3092829fd131..c4d7e39523d4 100644 --- a/code/datums/components/egg_layer.dm +++ b/code/datums/components/egg_layer.dm @@ -77,7 +77,7 @@ var/atom/at_least_atom = parent if(isliving(at_least_atom)) var/mob/living/potentially_dead_horse = at_least_atom - if(potentially_dead_horse.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(potentially_dead_horse)) return if(!eggs_left || !SPT_PROB(1.5, seconds_per_tick)) return diff --git a/code/datums/components/fearful/effects.dm b/code/datums/components/fearful/effects.dm index 17a62201257d..c0ce94bfde44 100644 --- a/code/datums/components/fearful/effects.dm +++ b/code/datums/components/fearful/effects.dm @@ -10,7 +10,7 @@ /datum/terror_handler/jittering/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(owner)) return if (terror_buildup < TERROR_BUILDUP_FEAR) @@ -39,7 +39,7 @@ /datum/terror_handler/stuttering/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(owner)) return if (terror_buildup < TERROR_BUILDUP_FEAR) @@ -56,7 +56,7 @@ /datum/terror_handler/heart_problems/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(owner)) return if (terror_buildup < TERROR_BUILDUP_FEAR) @@ -90,7 +90,7 @@ /datum/terror_handler/vomiting/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(owner)) return if (terror_buildup < TERROR_BUILDUP_TERROR) @@ -126,7 +126,7 @@ /datum/terror_handler/panic/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(owner)) stop_panic_attack() active = FALSE owner.remove_fov_trait(type, FOV_270_DEGREES) @@ -176,7 +176,7 @@ /datum/terror_handler/startle/tick(seconds_per_tick, terror_buildup) . = ..() - if (owner.stat >= UNCONSCIOUS || !COOLDOWN_FINISHED(src, startle_cd)) + if (IS_UNCONSCIOUS(owner) || !COOLDOWN_FINISHED(src, startle_cd)) return if (terror_buildup < TERROR_BUILDUP_FEAR || terror_buildup - component.last_tick_buildup < TERROR_STARTLE_MINIMUM_DIFFERENCE) diff --git a/code/datums/components/fearful/fearful.dm b/code/datums/components/fearful/fearful.dm index a2480acb4820..750e036cde5f 100644 --- a/code/datums/components/fearful/fearful.dm +++ b/code/datums/components/fearful/fearful.dm @@ -128,7 +128,7 @@ /datum/component/fearful/proc/on_examine(mob/living/source, mob/user, list/examine_list) SIGNAL_HANDLER - if (source.stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(source)) return if(terror_buildup >= TERROR_BUILDUP_HEART_ATTACK) @@ -148,7 +148,7 @@ if(hugger == parent) return - if(isnightmare(hugger)) + if(HAS_TRAIT(hugger, TRAIT_NIGHTMARISH)) var/lit_tiles = 0 var/unlit_tiles = 0 diff --git a/code/datums/components/fearful/sources/_sources.dm b/code/datums/components/fearful/sources/_sources.dm index 6c5631671992..d14664814977 100644 --- a/code/datums/components/fearful/sources/_sources.dm +++ b/code/datums/components/fearful/sources/_sources.dm @@ -18,7 +18,7 @@ /// Proc that children should override with their conditions /datum/terror_handler/simple_source/proc/check_condition(seconds_per_tick, terror_buildup) - return !HAS_TRAIT(owner, TRAIT_FEARLESS) && !HAS_TRAIT(owner, TRAIT_MIND_TEMPORARILY_GONE) && owner.stat < UNCONSCIOUS + return !HAS_TRAIT(owner, TRAIT_FEARLESS) && !HAS_TRAIT(owner, TRAIT_MIND_TEMPORARILY_GONE) && !IS_UNCONSCIOUS(owner) /// Proc that's called when the effect is first applied, for moodlets and alike /datum/terror_handler/simple_source/proc/on_activation(terror_buildup) diff --git a/code/datums/components/fearful/sources/phobia.dm b/code/datums/components/fearful/sources/phobia.dm index 155e949f298f..7afbc0b33320 100644 --- a/code/datums/components/fearful/sources/phobia.dm +++ b/code/datums/components/fearful/sources/phobia.dm @@ -28,7 +28,7 @@ return ..() /datum/terror_handler/phobia_source/proc/can_trigger() - return !HAS_TRAIT(owner, TRAIT_FEARLESS) && !HAS_TRAIT(owner, TRAIT_MIND_TEMPORARILY_GONE) && owner.stat < UNCONSCIOUS + return !HAS_TRAIT(owner, TRAIT_FEARLESS) && !HAS_TRAIT(owner, TRAIT_MIND_TEMPORARILY_GONE) && !IS_UNCONSCIOUS(owner) /datum/terror_handler/phobia_source/tick(seconds_per_tick, terror_buildup) . = ..() diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index e69eb9170366..f09224fc9197 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -665,7 +665,7 @@ Behavior that's still missing from this component that original food items had t food_quality += quality if(HAS_TRAIT(parent, TRAIT_FOOD_SILVER)) // it's not real food - if(!isjellyperson(eater)) //if you aren't a jellyperson, it makes you sick no matter how nice it looks + if(!(eater.mob_biotypes & MOB_SLIME)) //if you aren't a jellyperson, it makes you sick no matter how nice it looks return TOXIC_FOOD_QUALITY_THRESHOLD food_quality += LIKED_FOOD_QUALITY_CHANGE diff --git a/code/datums/components/ghostrole_on_revive.dm b/code/datums/components/ghostrole_on_revive.dm index 83541bcafdc7..ddc63524cba0 100644 --- a/code/datums/components/ghostrole_on_revive.dm +++ b/code/datums/components/ghostrole_on_revive.dm @@ -174,7 +174,7 @@ UnregisterSignal(parent_mob, COMSIG_LIVING_GHOSTROLE_INFO) // Block formaldehyde from being metabolized, Coroner QoL -/datum/component/ghostrole_on_revive/proc/block_formaldehyde_metabolism(mob/living/source, datum/reagent/chem) +/datum/component/ghostrole_on_revive/proc/block_formaldehyde_metabolism(mob/living/source, datum/reagent/chem, seconds_per_tick, metabolization_ratio) SIGNAL_HANDLER if(istype(chem, /datum/reagent/toxin/formaldehyde)) diff --git a/code/datums/components/gunpoint.dm b/code/datums/components/gunpoint.dm index 59c70b852e37..cb6e060352f8 100644 --- a/code/datums/components/gunpoint.dm +++ b/code/datums/components/gunpoint.dm @@ -68,7 +68,7 @@ target.add_mood_event("gunpoint", /datum/mood_event/gunpoint) if(istype(weapon, /obj/item/gun/ballistic/rocketlauncher) && weapon.chambered) - if(target.stat == CONSCIOUS && IS_NUKE_OP(shooter) && !IS_NUKE_OP(target) && (locate(/obj/item/disk/nuclear) in target.get_contents()) && shooter.client) + if(!IS_UNCONSCIOUS_OR_CRIT(target) && IS_NUKE_OP(shooter) && !IS_NUKE_OP(target) && (locate(/obj/item/disk/nuclear) in target.get_contents()) && shooter.client) shooter.client.give_award(/datum/award/achievement/misc/rocket_holdup, shooter) addtimer(CALLBACK(src, PROC_REF(update_stage), 2), GUNPOINT_DELAY_STAGE_2) diff --git a/code/datums/components/happiness.dm b/code/datums/components/happiness.dm index 791b9b32b7c4..be9064bf9935 100644 --- a/code/datums/components/happiness.dm +++ b/code/datums/components/happiness.dm @@ -70,7 +70,7 @@ return var/mob/living/living_parent = parent - if (living_parent.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(living_parent)) return NONE COOLDOWN_START(src, groom_cooldown, GROOM_COOLDOWN) @@ -83,7 +83,7 @@ return var/mob/living/living_parent = parent - if (living_parent.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(living_parent)) return pet_animal() diff --git a/code/datums/components/heart_eater.dm b/code/datums/components/heart_eater.dm index bb17bb3f5ebb..86eacb90d8d9 100644 --- a/code/datums/components/heart_eater.dm +++ b/code/datums/components/heart_eater.dm @@ -126,7 +126,7 @@ SIGNAL_HANDLER if(!istype(target)) return - if(!IS_DEAD_OR_INCAP(target)) + if(!target.incapacitated) return if(!source.combat_mode) return @@ -141,7 +141,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN /datum/component/heart_eater/proc/can_rip_heart(mob/living/user, mob/living/carbon/target, hand_index) - if(!IS_DEAD_OR_INCAP(target)) + if(!target.incapacitated) return FALSE if(!user.has_hand_for_held_index(hand_index)) return FALSE diff --git a/code/datums/components/jukebox.dm b/code/datums/components/jukebox.dm index 07004dd05b36..b66adc23e88d 100644 --- a/code/datums/components/jukebox.dm +++ b/code/datums/components/jukebox.dm @@ -91,9 +91,9 @@ return load_songs_from_config() /// Loads the config sounds once, and returns a copy of them. -/datum/jukebox/proc/load_songs_from_config() +/datum/jukebox/proc/load_songs_from_config(force = FALSE) var/static/list/config_songs - if(isnull(config_songs)) + if(isnull(config_songs) || force) config_songs = list() var/list/tracks = flist(CONFIG_JUKEBOX_SOUNDS) for(var/track_file in tracks) diff --git a/code/datums/components/leanable.dm b/code/datums/components/leanable.dm index 1b22df01305a..fb155e5688a1 100644 --- a/code/datums/components/leanable.dm +++ b/code/datums/components/leanable.dm @@ -54,7 +54,7 @@ if (!iscarbon(dropped) && !iscyborg(dropped)) return var/mob/living/leaner = dropped - if (INCAPACITATED_IGNORING(leaner, INCAPABLE_RESTRAINTS) || leaner.stat != CONSCIOUS || HAS_TRAIT(leaner, TRAIT_NO_TRANSFORM)) + if (INCAPACITATED_IGNORING(leaner, INCAPABLE_RESTRAINTS) || IS_UNCONSCIOUS_OR_CRIT(leaner) || HAS_TRAIT(leaner, TRAIT_NO_TRANSFORM)) return if (HAS_TRAIT_FROM(leaner, TRAIT_UNDENSE, LEANING_TRAIT)) return diff --git a/code/datums/components/life_link.dm b/code/datums/components/life_link.dm index 026826e4fea0..a9a55f3ab6c6 100644 --- a/code/datums/components/life_link.dm +++ b/code/datums/components/life_link.dm @@ -127,7 +127,7 @@ /// Update our vital status on the medical hud /datum/component/life_link/proc/update_med_hud_status(mob/living/mob_parent) - if(host.stat == DEAD || HAS_TRAIT(host, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(host)) mob_parent.set_hud_image_state(STATUS_HUD, "huddead") else mob_parent.set_hud_image_state(STATUS_HUD, "hudhealthy") diff --git a/code/datums/components/listen_prayers.dm b/code/datums/components/listen_prayers.dm index 9680b973189e..966985ea0df6 100644 --- a/code/datums/components/listen_prayers.dm +++ b/code/datums/components/listen_prayers.dm @@ -43,7 +43,7 @@ /datum/component/listen_prayers/proc/on_sent_prayer(source, mob/praying, message, prayer_type, symbol, list/deities_that_listened) SIGNAL_HANDLER var/datum/mind/mind = parent - if(!mind.current || mind.current.stat >= UNCONSCIOUS || !mind.current.client) //You can't hear prayers if unconscious or disconnected + if(!mind.current || !mind.current.client || IS_UNCONSCIOUS(mind.current)) //You can't hear prayers if unconscious or disconnected return if(!isliving(praying) || praying.stat == DEAD) return FALSE //I don't see any reason in hell to why dead people should be allowed into this. This isn't a knockoff TRAIT_SIXTHSENSE. diff --git a/code/datums/components/lock_on_cursor.dm b/code/datums/components/lock_on_cursor.dm index 1d8adedcb3c5..02656a98e7d8 100644 --- a/code/datums/components/lock_on_cursor.dm +++ b/code/datums/components/lock_on_cursor.dm @@ -140,7 +140,7 @@ /// Returns true if target is a valid target /datum/component/lock_on_cursor/proc/can_target(atom/target) var/mob/mob_target = target - return is_type_in_typecache(target, target_typecache) && !(ismob(target) && mob_target.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(target)] + return is_type_in_typecache(target, target_typecache) && !(ismob(target) && IS_UNCONSCIOUS_OR_CRIT(mob_target)) && !immune_weakrefs[WEAKREF(target)] /// Returns the nearest targets to the current cursor position /datum/component/lock_on_cursor/proc/get_nearest() diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm index 03705b410f63..24237052e180 100644 --- a/code/datums/components/overlay_lighting.dm +++ b/code/datums/components/overlay_lighting.dm @@ -124,7 +124,7 @@ RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_COLOR, PROC_REF(set_color)) RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_ON, PROC_REF(on_toggle)) RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, PROC_REF(on_light_flags_change)) - RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE, PROC_REF(set_light_render_source)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_RENDER_SOURCE, PROC_REF(set_light_render_source)) RegisterSignal(parent, COMSIG_ATOM_USED_IN_CRAFT, PROC_REF(on_parent_crafted)) RegisterSignal(parent, COMSIG_LIGHT_EATER_QUEUE, PROC_REF(on_light_eater)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_moved)) diff --git a/code/datums/components/palette.dm b/code/datums/components/palette.dm index a3551b0b59f1..1d7a27f3cd6b 100644 --- a/code/datums/components/palette.dm +++ b/code/datums/components/palette.dm @@ -111,7 +111,7 @@ color_picker_menu.change_choices(choices, tooltips = TRUE, keep_same_page = TRUE) /datum/component/palette/proc/choice_selected(mob/user, choice, params) - if(!choice || IS_DEAD_OR_INCAP(user)) // center button or incapacitated but still holding on the item. + if(!choice || user.incapacitated) // center button or incapacitated but still holding on the item. close_radial_menu() return var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK) diff --git a/code/datums/components/pet_commands/fetch.dm b/code/datums/components/pet_commands/fetch.dm index f3675534fa95..9dfb196b1e7a 100644 --- a/code/datums/components/pet_commands/fetch.dm +++ b/code/datums/components/pet_commands/fetch.dm @@ -92,30 +92,6 @@ var/mob/living/parent = weak_parent.resolve() parent.ai_controller.set_blackboard_key(BB_FETCH_DELIVER_TO, pointing_friend) -// Finally, plan our actions +// Install the fetch BT subtree. The subtree itself handles all phases (seek > pick up > deliver). /datum/pet_command/fetch/execute_action(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/forget_failed_fetches) - - var/atom/target = controller.blackboard[BB_CURRENT_PET_TARGET] - // We got something to fetch so go fetch it - if (!QDELETED(target)) - if (get_dist(controller.pawn, target) > 1) // We're not there yet - controller.queue_behavior(/datum/ai_behavior/fetch_seek, BB_CURRENT_PET_TARGET, BB_FETCH_DELIVER_TO) - return SUBTREE_RETURN_FINISH_PLANNING - // If mobs could attack food you would branch here to call `eat_fetched_snack`, however that's a task for the future - controller.queue_behavior(/datum/ai_behavior/pick_up_item, BB_CURRENT_PET_TARGET, BB_SIMPLE_CARRY_ITEM) - return SUBTREE_RETURN_FINISH_PLANNING - - var/obj/item/carried_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM] - if (QDELETED(carried_item)) - return - - var/atom/delivery_target = controller.blackboard[BB_FETCH_DELIVER_TO] - if (QDELETED(delivery_target) || !can_see(controller.pawn, delivery_target, sense_radius)) - // We don't know where to return this to so we're just going to keep it - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return - - // We got something to deliver and someone to deliver it to - controller.queue_behavior(/datum/ai_behavior/deliver_fetched_item, BB_FETCH_DELIVER_TO, BB_SIMPLE_CARRY_ITEM) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/fetch) diff --git a/code/datums/components/pet_commands/obeys_commands.dm b/code/datums/components/pet_commands/obeys_commands.dm index 0e459a60161e..5c4da5f30e24 100644 --- a/code/datums/components/pet_commands/obeys_commands.dm +++ b/code/datums/components/pet_commands/obeys_commands.dm @@ -79,7 +79,7 @@ /datum/component/obeys_commands/proc/on_examine(mob/living/source, mob/user, list/examine_list) SIGNAL_HANDLER - if (IS_DEAD_OR_INCAP(source)) + if (source.incapacitated) return if (!(user in source.ai_controller?.blackboard[BB_FRIENDS_LIST])) return @@ -103,7 +103,7 @@ /datum/component/obeys_commands/proc/display_menu(mob/living/friend) var/mob/living/living_parent = parent - if (IS_DEAD_OR_INCAP(living_parent) || friend.stat != CONSCIOUS) + if (living_parent.incapacitated || IS_UNCONSCIOUS_OR_CRIT(friend)) return if (!(friend in living_parent.ai_controller?.blackboard[BB_FRIENDS_LIST])) return // Not our friend, can't boss us around diff --git a/code/datums/components/pet_commands/pet_command.dm b/code/datums/components/pet_commands/pet_command.dm index 04dde32d5db8..ce58f6499434 100644 --- a/code/datums/components/pet_commands/pet_command.dm +++ b/code/datums/components/pet_commands/pet_command.dm @@ -119,7 +119,7 @@ var/mob/living/parent = weak_parent.resolve() if(isnull(parent) || isnull(parent.ai_controller)) return FALSE - if(IS_DEAD_OR_INCAP(parent)) // Probably can't hear them if we're dead + if(parent.incapacitated) // Probably can't hear them if we're dead return FALSE return TRUE @@ -148,7 +148,6 @@ if (!can_see(parent, potential_target, sense_radius)) return FALSE - parent.ai_controller.CancelActions() set_command_target(parent, potential_target) return TRUE @@ -156,8 +155,11 @@ /datum/pet_command/proc/set_command_active(mob/living/parent, mob/living/commander, radial_command = FALSE) parent.ai_controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - parent.ai_controller.CancelActions() // Stop whatever you're doing and do this instead + var/datum/pet_command/previous = parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] + if(previous && previous != src) + previous.command_ended(parent.ai_controller) parent.ai_controller.set_blackboard_key(BB_ACTIVE_PET_COMMAND, src) + execute_action(parent.ai_controller) // Install the BT override subtree for this command if (command_feedback) parent.balloon_alert_to_viewers("[command_feedback]") // If we get a nicer runechat way to do this, refactor this if(!radial_command) @@ -170,6 +172,12 @@ commander.client?.mouse_override_icon = 'icons/effects/mouse_pointers/pet_paw.dmi' commander.update_mouse_pointer() + +/// Called when this command is replaced by another command or otherwise deactivated. Extend to add cleanup logic. +/datum/pet_command/proc/command_ended(datum/ai_controller/controller) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, null) + return + /datum/pet_command/proc/click_on_target(mob/living/source, atom/target, list/modifiers) SIGNAL_HANDLER if(!can_see(source, target, 9)) @@ -215,7 +223,7 @@ if (!parent) return FALSE - parent.ai_controller.CancelActions() + parent.ai_controller.cancel_current_plan() if(!look_for_target(friend, potential_target) || !set_command_target(parent, potential_target)) return FALSE parent.visible_message(span_warning("[parent] follows [friend]'s gesture towards [potential_target] [pointed_reaction]!")) diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm index 2269f760ba45..ff1dcf007394 100644 --- a/code/datums/components/pet_commands/pet_commands_basic.dm +++ b/code/datums/components/pet_commands/pet_commands_basic.dm @@ -12,14 +12,14 @@ command_feedback = "sits" /datum/pet_command/idle/execute_action(datum/ai_controller/controller) - return SUBTREE_RETURN_FINISH_PLANNING // This cancels further AI planning + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/stay) /datum/pet_command/idle/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to stay idle!" /** * # Pet Command: Stop - * Tells a pet to exit command mode and resume its normal behaviour, which includes regular target-seeking and what have you + * Tells a pet to exit command mode and resume its normal behaviour */ /datum/pet_command/free command_name = "Loose" @@ -29,8 +29,8 @@ command_feedback = "relaxes" /datum/pet_command/free/execute_action(datum/ai_controller/controller) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, null) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return // Just move on to the next planning subtree. /datum/pet_command/free/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to go free!" @@ -45,10 +45,10 @@ radial_icon_state = "follow" speech_commands = list("heel", "follow") callout_type = /datum/callout_option/move - ///the behavior we use to follow - var/follow_behavior = /datum/ai_behavior/pet_follow_friend ///should we activate immediately if we're doing nothing else and gain a friend? var/activate_on_befriend = FALSE + /// BT subtree installed by execute_action; override to use a mob-specific follow tree. + var/follow_subtree = /datum/bt_node/subtree/pet_command/follow /datum/pet_command/follow/set_command_active(mob/living/parent, mob/living/commander) . = ..() @@ -58,8 +58,7 @@ return "signals [living_pet] to follow!" /datum/pet_command/follow/execute_action(datum/ai_controller/controller) - controller.queue_behavior(follow_behavior, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, follow_subtree) /datum/pet_command/follow/add_new_friend(mob/living/tamer) . = ..() @@ -84,8 +83,7 @@ speech_commands = list("play dead") // Don't get too creative here, people talk about dying pretty often /datum/pet_command/play_dead/execute_action(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/play_dead) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/play_dead) /datum/pet_command/play_dead/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to play dead!" @@ -112,14 +110,13 @@ // If we get past this point someone has finally added a non-binary dog /datum/pet_command/good_boy/execute_action(datum/ai_controller/controller) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, null) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) var/mob/living/parent = weak_parent.resolve() if (!parent) - return SUBTREE_RETURN_FINISH_PLANNING - + return new /obj/effect/temp_visual/heart(parent.loc) parent.emote("spin") - return SUBTREE_RETURN_FINISH_PLANNING /** * # Pet Command: Use ability @@ -133,9 +130,9 @@ var/datum/action/cooldown/ability = controller.blackboard[ability_key] if(!ability?.IsAvailable()) return - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key) + controller.set_blackboard_key(BB_PET_ACTIVE_ABILITY, ability) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/untargeted_ability) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING /datum/pet_command/untargeted_ability/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to use an ability!" @@ -155,8 +152,7 @@ pointed_reaction = "and growls" /// Balloon alert to display if providing an invalid target var/refuse_reaction = "shakes head" - /// Attack behaviour to use - var/attack_behaviour = /datum/ai_behavior/basic_melee_attack + var/datum/bt_node/subtree/attack_subtree = /datum/bt_node/subtree/pet_command/attack // Refuse to target things we can't target, chiefly other friends /datum/pet_command/attack/set_command_target(mob/living/parent, atom/target) @@ -168,7 +164,7 @@ var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(living_parent.ai_controller.blackboard[targeting_strategy_key]) if (!targeter) return FALSE - if (!targeter.can_attack(living_parent, target)) + if (!targeter.is_valid_target(living_parent, target)) refuse_target(parent, target) return FALSE return ..() @@ -183,8 +179,7 @@ living_parent.visible_message(span_notice("[living_parent] refuses to attack [target].")) /datum/pet_command/attack/execute_action(datum/ai_controller/controller) - controller.queue_behavior(attack_behaviour, BB_CURRENT_PET_TARGET, targeting_strategy_key) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, attack_subtree) /** * # Breed command. breed with a partner! @@ -195,8 +190,6 @@ requires_pointing = TRUE radial_icon_state = "breed" speech_commands = list("breed", "consummate") - ///the behavior we use to make babies - var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies /datum/pet_command/breed/set_command_target(mob/living/parent, atom/target) if(isnull(target) || !isliving(target)) @@ -213,10 +206,7 @@ return ..() /datum/pet_command/breed/execute_action(datum/ai_controller/controller) - if(is_type_in_list(controller.blackboard[BB_CURRENT_PET_TARGET], controller.blackboard[BB_BABIES_PARTNER_TYPES])) - controller.queue_behavior(reproduce_behavior, BB_CURRENT_PET_TARGET) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/breed) /datum/pet_command/breed/retrieve_command_text(atom/living_pet, atom/target) return isnull(target) ? null : "signals [living_pet] to breed with [target]!" @@ -236,8 +226,6 @@ pointed_reaction = "and growls" /// Blackboard key where a reference to some kind of mob ability is stored var/pet_ability_key - /// The AI behavior to use for the ability - var/ability_behavior = /datum/ai_behavior/pet_use_ability /datum/pet_command/use_ability/execute_action(datum/ai_controller/controller) if (!pet_ability_key) @@ -245,10 +233,9 @@ var/datum/action/cooldown/using_action = controller.blackboard[pet_ability_key] if (QDELETED(using_action)) return - // We don't check if the target exists because we want to 'sit attentively' if we've been instructed to attack but not given one yet - // We also don't check if the cooldown is over because there's no way a pet owner can know that, the behaviour will handle it - controller.queue_behavior(ability_behavior, pet_ability_key, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + // Store the action datum in a fixed key so the targeted_ability subtree can find it + controller.override_blackboard_key(BB_TARGETED_ACTION, using_action) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/targeted_ability) /datum/pet_command/use_ability/retrieve_command_text(atom/living_pet, atom/target) return isnull(target) ? null : "signals [living_pet] to use an ability on [target]!" @@ -260,10 +247,9 @@ callout_type = /datum/callout_option/guard ///the range our owner needs to be in for us to protect him var/protect_range = 9 - ///the behavior we will use when he is attacked - var/protect_behavior = /datum/ai_behavior/basic_melee_attack ///message cooldown to prevent too many people from telling you not to commit suicide COOLDOWN_DECLARE(self_harm_message_cooldown) + var/datum/bt_node/subtree/protect_owner_subtree = /datum/bt_node/subtree/pet_command/protect_owner /datum/pet_command/protect_owner/add_new_friend(mob/living/tamer) RegisterSignal(tamer, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(set_attacking_target)) @@ -274,18 +260,7 @@ UnregisterSignal(unfriended, COMSIG_ATOM_WAS_ATTACKED) /datum/pet_command/protect_owner/execute_action(datum/ai_controller/controller) - var/mob/living/victim = controller.blackboard[BB_CURRENT_PET_TARGET] - if(QDELETED(victim)) - return - var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) - if(!targeter.can_attack(controller.pawn, victim)) - return - // cancel the action if they're below our given crit stat, OR if we're trying to attack ourselves (this can happen on tamed mobs w/ protect subtree rarely) - if(victim.stat > controller.blackboard[BB_TARGET_MINIMUM_STAT] || victim == controller.pawn) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return - controller.queue_behavior(protect_behavior, BB_CURRENT_PET_TARGET, BB_PET_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, protect_owner_subtree) /datum/pet_command/protect_owner/set_command_active(mob/living/parent, mob/living/victim) . = ..() @@ -314,7 +289,7 @@ set_command_active(owner, attacker) /** - * # Fish command: command the mob to fish at the next fishing spot you point at. Requires the profound fisher component + * # Fish command: command the mob to fish at the next fishing spot you point at. */ /datum/pet_command/fish command_name = "Fish" @@ -324,9 +299,10 @@ speech_commands = list("fish") /datum/pet_command/fish/execute_action(datum/ai_controller/controller) - if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) - controller.queue_behavior(/datum/ai_behavior/interact_with_target/fishing, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + if(!controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/stay) + return + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/fish) /datum/pet_command/fish/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to go fish!" @@ -337,8 +313,6 @@ requires_pointing = TRUE radial_icon_state = "move" speech_commands = list("move", "walk") - ///the behavior we use to walk towards targets - var/datum/ai_behavior/walk_behavior = /datum/ai_behavior/travel_towards /datum/pet_command/move/set_command_target(mob/living/parent, atom/target) if(isnull(target) || !can_see(parent, target, 9)) @@ -346,9 +320,7 @@ return ..() /datum/pet_command/move/execute_action(datum/ai_controller/controller) - if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) - controller.queue_behavior(walk_behavior, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/move_to) /datum/pet_command/move/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to move!" diff --git a/code/datums/components/proficient_miner.dm b/code/datums/components/proficient_miner.dm index b37153b38412..14bff407d8fd 100644 --- a/code/datums/components/proficient_miner.dm +++ b/code/datums/components/proficient_miner.dm @@ -15,20 +15,33 @@ /datum/component/proficient_miner/RegisterWithParent() RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump)) + RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack)) /datum/component/proficient_miner/UnregisterFromParent() - UnregisterSignal(parent, COMSIG_MOVABLE_BUMP) + UnregisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_LIVING_UNARMED_ATTACK)) + +/datum/component/proficient_miner/proc/on_unarmed_attack(mob/living/source, atom/target, proximity, modifiers) + SIGNAL_HANDLER + + if(!proximity) + return + try_mine(source, target) /datum/component/proficient_miner/proc/on_bump(atom/movable/source, atom/target) SIGNAL_HANDLER - if(!ismineralturf(target) || last_bumpmine_tick == world.time) + if(last_bumpmine_tick == world.time) + return + try_mine(source, target) + +/datum/component/proficient_miner/proc/try_mine(atom/movable/source, atom/target) + if(!ismineralturf(target)) return var/mob/living/user = null if(isliving(parent)) user = parent - if(user.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return var/turf/closed/mineral/mineral_wall = target diff --git a/code/datums/components/recharging_attacks.dm b/code/datums/components/recharging_attacks.dm index b9127c4e018b..55efbf0fcd8f 100644 --- a/code/datums/components/recharging_attacks.dm +++ b/code/datums/components/recharging_attacks.dm @@ -55,7 +55,7 @@ var/final_refund = attack_refund if(QDELETED(attacked) || (attacked.stat == DEAD && last_stat != DEAD)) //The target is dead and we killed them - full refund final_refund = recharged_action.cooldown_time - else if(attacked.stat > CONSCIOUS && last_stat == CONSCIOUS) //We knocked the target unconscious - partial refund + else if(attacked.stat != STABLE && last_stat == STABLE) //We knocked the target unconscious - partial refund final_refund = crit_refund recharged_action.next_use_time -= final_refund diff --git a/code/datums/components/regenerator.dm b/code/datums/components/regenerator.dm index 9e9fdc09738c..c31fafe655e7 100644 --- a/code/datums/components/regenerator.dm +++ b/code/datums/components/regenerator.dm @@ -111,7 +111,7 @@ var/mob/living/living_parent = parent // Heal bonus for being in crit. Only applies to carbons - var/heal_mod = HAS_TRAIT(living_parent, TRAIT_CRITICAL_CONDITION) ? 2 : 1 + var/heal_mod = living_parent.stat >= SOFT_CRIT ? 2 : 1 var/need_mob_update = FALSE if(brute_per_second) diff --git a/code/datums/components/revenge_ability.dm b/code/datums/components/revenge_ability.dm index 2c52b8cd31a4..aa0f81ded67b 100644 --- a/code/datums/components/revenge_ability.dm +++ b/code/datums/components/revenge_ability.dm @@ -50,7 +50,7 @@ var/distance = get_dist(ability_user, attacker) if (distance < min_range || distance > max_range) return - if (targeting && !targeting.can_attack(victim, attacker)) + if (targeting && !targeting.is_valid_target(victim, attacker)) return INVOKE_ASYNC(ability, TYPE_PROC_REF(/datum/action/cooldown, InterceptClickOn), ability_user, null, (target_self) ? ability_user : attacker) diff --git a/code/datums/components/riding/riding.dm b/code/datums/components/riding/riding.dm index 5b094e65e57b..482ac97ffe0c 100644 --- a/code/datums/components/riding/riding.dm +++ b/code/datums/components/riding/riding.dm @@ -399,7 +399,7 @@ return rider.visible_message( - span_warning("As [disarmer] shoves [rider] around, [rider.p_they()] lose [rider.p_their()] [pick("balance", "grip", "hold")] and fall off [parent]!"), + span_warning("As [disarmer] shoves [rider] around, [rider.p_they()] lose[rider.p_s()] [rider.p_their()] [pick("balance", "grip", "hold")] and fall[rider.p_s()] off [parent]!"), span_warning("As [disarmer] shoves you around, you lose your [pick("balance", "grip", "hold")] and fall off [parent]!") ) if(rider.is_blind()) diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm index 7579ac2a117a..a0fac4cf0af2 100644 --- a/code/datums/components/riding/riding_mob.dm +++ b/code/datums/components/riding/riding_mob.dm @@ -763,7 +763,7 @@ return // Heal the owner and flee whatever might've attacked them - if (new_stat == CONSCIOUS || new_stat == DEAD || old_stat != CONSCIOUS || !raptor.ai_controller) + if (new_stat == STABLE || new_stat == DEAD || old_stat != STABLE || !raptor.ai_controller) ADD_TRAIT(raptor, TRAIT_AI_PAUSED, REF(src)) return @@ -773,7 +773,7 @@ raptor.ai_controller.set_blackboard_key(BB_INJURED_RAPTOR, source) for (var/mob/living/possible_hostile in view(5, raptor)) - if (possible_hostile.stat || possible_hostile.invisibility > raptor.see_invisible || source.faction_check_atom(possible_hostile)) + if (IS_UNCONSCIOUS_OR_CRIT(possible_hostile) || possible_hostile.invisibility > raptor.see_invisible || source.faction_check_atom(possible_hostile)) continue raptor.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEE_TARGET, possible_hostile) break diff --git a/code/datums/components/soul_stealer.dm b/code/datums/components/soul_stealer.dm index 5be54a45be14..b541d2b7de29 100644 --- a/code/datums/components/soul_stealer.dm +++ b/code/datums/components/soul_stealer.dm @@ -62,7 +62,7 @@ return ITEM_INTERACT_SUCCESS /datum/component/soul_stealer/proc/try_capture(mob/living/carbon/human/victim, mob/living/captor) - if(victim.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(victim)) return var/obj/item/soulstone/soulstone = new soulstone_type(parent) soulstone.attack(victim, captor) diff --git a/code/datums/components/spirit_holding.dm b/code/datums/components/spirit_holding.dm index 11ceb778313a..62aba8460044 100644 --- a/code/datums/components/spirit_holding.dm +++ b/code/datums/components/spirit_holding.dm @@ -101,12 +101,12 @@ // Now that all of the important things are in place for our spirit, it's time for them to choose their name. var/valid_input_name = custom_name(awakener) if(valid_input_name) - bound_spirit.fully_replace_character_name(null, "The spirit of [valid_input_name]") + bound_spirit.fully_replace_character_name(null, "The spirit of [valid_input_name]", log_new_name = TRUE) /datum/component/spirit_holding/proc/bind_the_soule(datum/mind/chosen_spirit, mob/awakener, name_override) bound_spirit = new(parent) chosen_spirit.transfer_to(bound_spirit) - bound_spirit.fully_replace_character_name(null, "The spirit of [name_override ? name_override : parent]") + bound_spirit.fully_replace_character_name(null, "The spirit of [name_override || parent]") bound_spirit.get_language_holder().omnitongue = TRUE //Grants omnitongue RegisterSignal(parent, COMSIG_ATOM_RELAYMOVE, PROC_REF(block_buckle_message)) diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index 450eda128761..041040006ef6 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -41,7 +41,7 @@ RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(use_squeak)) RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip)) RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop)) - if(istype(parent, /obj/item/clothing)) + if(isclothing(parent)) RegisterSignal(parent, COMSIG_SHOES_STEP_ACTION, PROC_REF(step_squeak)) else if(isstructure(parent)) RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(use_squeak)) diff --git a/code/datums/components/style/style.dm b/code/datums/components/style/style.dm index 670ef6894dc1..e5a4de576177 100644 --- a/code/datums/components/style/style.dm +++ b/code/datums/components/style/style.dm @@ -339,7 +339,7 @@ return var/mob/living/disrespected = attacked_atom - if(disrespected.stat || disrespected.faction_check_atom(punching_person) || !disrespected.has_faction(FACTION_MINING)) + if(IS_UNCONSCIOUS_OR_CRIT(disrespected) || disrespected.faction_check_atom(punching_person) || !disrespected.has_faction(FACTION_MINING)) return add_action(ACTION_DISRESPECT, 60 * (ismegafauna(disrespected) ? 2 : 1)) @@ -347,7 +347,7 @@ /datum/component/style/proc/on_attack(mob/living/attacking_person, mob/living/attacked_mob) SIGNAL_HANDLER - if(!istype(attacked_mob) || attacked_mob.stat) + if(!istype(attacked_mob) || IS_UNCONSCIOUS_OR_CRIT(attacked_mob)) return var/mob/living/attacked = attacked_mob @@ -383,7 +383,7 @@ /datum/component/style/proc/on_resonator_burst(datum/source, mob/creator, mob/living/hit_living) SIGNAL_HANDLER - if(creator.faction_check_atom(hit_living) || (hit_living.stat != CONSCIOUS) || !hit_living.has_faction(FACTION_MINING)) + if(creator.faction_check_atom(hit_living) || (IS_UNCONSCIOUS_OR_CRIT(hit_living)) || !hit_living.has_faction(FACTION_MINING)) return add_action(ACTION_TRAPPER, 70) @@ -444,7 +444,7 @@ if(died == parent) change_points(-500, use_multiplier = FALSE) return - else if(mob_parent.faction_check_atom(died) || !died.has_faction(FACTION_MINING) || (died.z != mob_parent.z) || !(died in view(mob_parent.client?.view, get_turf(mob_parent)))) + else if(mob_parent.faction_check_atom(died) || !ismining(died) || (died.z != mob_parent.z) || !(died in view(mob_parent.client?.view, get_turf(mob_parent)))) return if(ismegafauna(died)) diff --git a/code/datums/components/style/style_meter.dm b/code/datums/components/style/style_meter.dm index 83a27218cd82..983b0a0343dd 100644 --- a/code/datums/components/style/style_meter.dm +++ b/code/datums/components/style/style_meter.dm @@ -145,7 +145,7 @@ return // If we have an active style meter, we're on someone's face. Use them to check if the dead megafauna could be credited to them... var/mob/mob_parent = style_meter.parent - if(mob_parent.faction_check_atom(died) || !died.has_faction(FACTION_MINING) || (died.z != mob_parent.z) || !(died in view(mob_parent.client?.view, get_turf(mob_parent)))) + if(mob_parent.faction_check_atom(died) || !ismining(died) || (died.z != mob_parent.z) || !(died in view(mob_parent.client?.view, get_turf(mob_parent)))) return if(ismegafauna(died)) diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm index cf587283c1cf..88d935259d2a 100644 --- a/code/datums/components/supermatter_crystal.dm +++ b/code/datums/components/supermatter_crystal.dm @@ -173,14 +173,13 @@ consume(atom_source, dust_arm) qdel(item) return - if(cig.lit || user.combat_mode) + if(user.combat_mode || !cig.attempt_light(user, item, "")) user.visible_message(span_danger("A hideous sound echoes as [item] is ashed out on contact with \the [atom_source]. That didn't seem like a good idea...")) playsound(atom_source, 'sound/effects/supermatter.ogg', 150, TRUE) consume(atom_source, item) radiation_pulse(atom_source, max_range = 3, threshold = 0.1, chance = 50) return else - cig.light() user.visible_message(span_danger("As [user] lights \their [item] on \the [atom_source], silence fills the room..."),\ span_danger("Time seems to slow to a crawl as you touch \the [atom_source] with \the [item].\n\The [item] flashes alight with an eerie energy as you nonchalantly lift your hand away from \the [atom_source]. Damn.")) playsound(atom_source, 'sound/effects/supermatter.ogg', 50, TRUE) diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index 9bdc48520c4d..ca461d139385 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -107,7 +107,7 @@ RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(checkObstacle)) playsound(user, 'sound/items/weapons/thudswoosh.ogg', 40, TRUE, -1) - var/leap_word = isfelinid(user) || HAS_TRAIT(user, TRAIT_TACKLING_TAILED_POUNCE) ? "pounce" : "leap" //If cat, "pounce" instead of "leap". + var/leap_word = HAS_TRAIT(user, TRAIT_CATLIKE_INSTINCT) || HAS_TRAIT(user, TRAIT_TACKLING_TAILED_POUNCE) ? "pounce" : "leap" //If cat, "pounce" instead of "leap". if(can_see(user, clicked_atom, 7)) user.visible_message(span_warning("[user] [leap_word]s at [clicked_atom]!"), span_danger("You [leap_word] at [clicked_atom]!")) else @@ -153,7 +153,7 @@ return var/mob/living/carbon/target = hit - var/tackle_word = isfelinid(user) ? "pounce" : "tackle" //If cat, "pounce" instead of "tackle". + var/tackle_word = HAS_TRAIT(user, TRAIT_CATLIKE_INSTINCT) ? "pounce" : "tackle" //If cat, "pounce" instead of "tackle". var/roll = rollTackle(target) tackling = FALSE diff --git a/code/datums/components/tameable.dm b/code/datums/components/tameable.dm index 2153d54d51e7..9fcdeea36fd7 100644 --- a/code/datums/components/tameable.dm +++ b/code/datums/components/tameable.dm @@ -56,6 +56,9 @@ /datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, obj/item/food, inform_tamer = FALSE) SIGNAL_HANDLER source.tamed(tamer, food)//Run custom behavior if needed + if(isliving(parent)) + var/mob/living/living_parent = parent + living_parent.ai_controller?.set_blackboard_key(BB_TAMED, TRUE) if(isliving(parent) && isliving(tamer)) INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, befriend), tamer) if(inform_tamer) diff --git a/code/datums/components/tippable.dm b/code/datums/components/tippable.dm index fa2ac331ce13..4b59b96012dc 100644 --- a/code/datums/components/tippable.dm +++ b/code/datums/components/tippable.dm @@ -100,7 +100,7 @@ * tipper - the mob tipping the tipped_mob */ /datum/component/tippable/proc/try_tip(mob/living/tipped_mob, mob/tipper) - if(tipped_mob.stat != CONSCIOUS && !HAS_TRAIT(tipped_mob, TRAIT_FORCED_STANDING)) + if(IS_UNCONSCIOUS_OR_CRIT(tipped_mob) && !HAS_TRAIT(tipped_mob, TRAIT_FORCED_STANDING)) return if(pre_tipped_callback?.Invoke(tipper)) diff --git a/code/datums/components/trader/trader.dm b/code/datums/components/trader/trader.dm index 3b2675785aec..c9a95fae0e75 100644 --- a/code/datums/components/trader/trader.dm +++ b/code/datums/components/trader/trader.dm @@ -150,7 +150,7 @@ Can accept both a type path, and an instance of a datum. Type path has priority. /datum/component/trader/proc/check_menu(mob/customer) if(!istype(customer)) return FALSE - if(IS_DEAD_OR_INCAP(customer) || !customer.Adjacent(parent)) + if(customer.incapacitated || !customer.Adjacent(parent)) return FALSE return TRUE @@ -429,7 +429,7 @@ Can accept both a type path, and an instance of a datum. Type path has priority. if(trader.combat_mode) trader.balloon_alert(customer, "in combat!") return FALSE - if(IS_DEAD_OR_INCAP(trader)) + if(trader.incapacitated) trader.balloon_alert(customer, "indisposed!") return FALSE return TRUE diff --git a/code/datums/components/tree_climber.dm b/code/datums/components/tree_climber.dm index 9f506ae516f8..6ec21ac41a90 100644 --- a/code/datums/components/tree_climber.dm +++ b/code/datums/components/tree_climber.dm @@ -15,11 +15,11 @@ ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) /datum/component/tree_climber/RegisterWithParent() - RegisterSignals(parent, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_CLIMB_TREE), PROC_REF(climb_tree)) + RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(climb_tree)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) /datum/component/tree_climber/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_CLIMB_TREE, COMSIG_ATOM_EXAMINE)) + UnregisterSignal(parent, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_ATOM_EXAMINE)) /datum/component/tree_climber/Destroy() if(current_tree) diff --git a/code/datums/components/udder.dm b/code/datums/components/udder.dm index a72a6bbb25f7..986e76db504e 100644 --- a/code/datums/components/udder.dm +++ b/code/datums/components/udder.dm @@ -31,7 +31,7 @@ SIGNAL_HANDLER var/mob/living/milked = parent - if(milked.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(milked)) return //come on now var/udder_filled_percentage = PERCENT(udder.reagents.total_volume / udder.reagents.maximum_volume) @@ -49,7 +49,7 @@ SIGNAL_HANDLER var/mob/living/milked = parent - if(milked.stat == CONSCIOUS && istype(milking_tool, /obj/item/reagent_containers/cup)) + if(!IS_UNCONSCIOUS_OR_CRIT(milked) && istype(milking_tool, /obj/item/reagent_containers/cup)) udder.milk(milking_tool, user) if(on_milk_callback) on_milk_callback.Invoke(udder.reagents.total_volume, udder.reagents.maximum_volume) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 5bd335eca33d..dde2d5f944cc 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -57,6 +57,7 @@ var/infectable_biotypes = MOB_ORGANIC //if the disease can spread on organics, synthetics, or undead var/process_dead = FALSE //if this ticks while the host is dead var/copy_type = null //if this is null, copies will use the type of the instance being copied + var/half_stage = FALSE // Acts as a counter for half stages /datum/disease/Destroy() . = ..() @@ -83,6 +84,9 @@ var/turf/source_turf = get_turf(infectee) log_virus("[key_name(infectee)] was infected by virus: [src.admin_details()] at [loc_name(source_turf)]") + if(severity >= DISEASE_SEVERITY_BIOHAZARD) + SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_TOMBSTONE] = TRUE + /// Updates the spread flags set, ensuring signals are updated as necessary /datum/disease/proc/update_spread_flags(new_flags) if(spread_flags == new_flags) @@ -125,7 +129,7 @@ var/recovery_prob = 0 var/cure_mod var/bad_immune = HAS_TRAIT(affected_mob, TRAIT_IMMUNODEFICIENCY) ? 2 : 1 - var/is_sleeping = HAS_TRAIT_FROM_ONLY(affected_mob, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(/datum/status_effect/incapacitating/sleeping::id)) + var/is_sleeping = !!affected_mob.IsSleeping() if(required_organ) if(!has_required_infectious_organ(affected_mob, required_organ)) @@ -152,8 +156,12 @@ if(stage == max_stages && stage_peaked != TRUE) //mostly a sanity check in case we manually set a virus to max stages stage_peaked = TRUE - if(SPT_PROB(stage_prob * slowdown * bad_immune, seconds_per_tick)) - update_stage(min(stage + 1, max_stages)) + if(SPT_PROB(stage_prob * slowdown * bad_immune * 2, seconds_per_tick)) + if(half_stage) + half_stage = FALSE + update_stage(min(stage + 1, max_stages)) + else if(stage <= max_stages) + half_stage = TRUE if(!(disease_flags & CHRONIC) && disease_flags & CURABLE && bypasses_immunity != TRUE) switch(severity) @@ -206,6 +214,8 @@ if(SANITY_LEVEL_INSANE) recovery_prob += -0.4 + recovery_prob += get_immunity_recovery() + if((HAS_TRAIT(affected_mob, TRAIT_NOHUNGER) || !(affected_mob.satiety < 0 || affected_mob.nutrition < NUTRITION_LEVEL_STARVING)) && is_sleeping) //resting starved won't help, but resting helps var/turf/rest_turf = get_turf(affected_mob) var/is_sleeping_in_darkness = rest_turf.get_lumcount() <= LIGHTING_TILE_IS_DARK @@ -412,6 +422,10 @@ if(SPT_PROB(infectivity * 4, seconds_per_tick)) airborne_spread() +// Increases natural recovery based on prior immunities +/datum/disease/proc/get_immunity_recovery() + return 0 + //Use this to compare severities /proc/get_disease_severity_value(severity) switch(severity) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 3ad9f989b0ae..a43a42bdc3ed 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -87,7 +87,7 @@ for(var/s in symptoms) var/datum/symptom/symptom_datum = s if(symptom_datum.Start(src)) //this will return FALSE if the symptom is neutered - symptom_datum.next_activation = world.time + (rand(symptom_datum.symptom_delay_min SECONDS, symptom_datum.symptom_delay_max SECONDS) * DISEASE_SYMPTOM_FREQUENCY_MODIFIER) + symptom_datum.next_activation = world.time + (rand(symptom_datum.symptom_delay * (1 - symptom_datum.delay_variation) SECONDS, symptom_datum.symptom_delay * (1 + symptom_datum.delay_variation) SECONDS) * DISEASE_SYMPTOM_FREQUENCY_MODIFIER) symptom_datum.on_stage_change(src) for(var/s in symptoms) @@ -502,6 +502,22 @@ visibility_flags &= ~HIDDEN_SCANNER affected_mob.med_hud_set_status() +/datum/disease/advance/cure(add_resistance = TRUE) + if(severity == DISEASE_SEVERITY_UNCURABLE) + return + if(add_resistance == TRUE) + for(var/datum/symptom/each_symptom as anything in symptoms) + if(!each_symptom.neutered && !each_symptom.immunity_proof) + LAZYOR(affected_mob.symptom_resistances, each_symptom.name) + .=..() + +/datum/disease/advance/get_immunity_recovery() + var/recovery_bonus = 0 + for(var/datum/symptom/each_symptom as anything in symptoms) + if((each_symptom.name in affected_mob.symptom_resistances) && !each_symptom.neutered) + recovery_bonus += DISEASE_SYMPTOM_IMMUNITY_RECOVERY_BONUS + return recovery_bonus + /datum/disease/advance/proc/generate_cure_text(cure_count) var/remedies = list() for(var/datum/symptom/each_symptom as anything in symptoms) diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm index ba5a41dc3102..f45a3f418128 100644 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ b/code/datums/diseases/advance/symptoms/beard.dm @@ -17,8 +17,7 @@ transmittable = 1 level = 4 severity = 1 - symptom_delay_min = 18 - symptom_delay_max = 36 + symptom_delay = 27 symptom_cure = /datum/reagent/medicine/mannitol var/list/beard_order = list("Beard (Jensen)", "Beard (Full)", "Beard (Dwarf)", "Beard (Very Long)") diff --git a/code/datums/diseases/advance/symptoms/chills.dm b/code/datums/diseases/advance/symptoms/chills.dm index b31d72d6cf3f..d8abcaa3548f 100644 --- a/code/datums/diseases/advance/symptoms/chills.dm +++ b/code/datums/diseases/advance/symptoms/chills.dm @@ -18,8 +18,7 @@ transmittable = 2 level = 2 severity = 2 - symptom_delay_min = 10 - symptom_delay_max = 30 + symptom_delay = 20 symptom_cure = /datum/reagent/medicine/leporazine threshold_descs = list( "Stage Speed 5" = "Increases the intensity of the cooling; the host can fall below safe temperature levels.", diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index 398df5ebd933..265509134b44 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -18,8 +18,7 @@ level = 3 severity = 3 base_message_chance = 15 - symptom_delay_min = 10 - symptom_delay_max = 30 + symptom_delay = 20 required_organ = ORGAN_SLOT_LUNGS symptom_cure = /datum/reagent/medicine/c2/tirimol cure_color = "orange" @@ -33,8 +32,7 @@ if(!.) return if(A.totalStageSpeed() >= 8) - symptom_delay_min = 7 - symptom_delay_max = 24 + symptom_delay = 15.5 if(A.totalStealth() >= 4) suppress_warning = TRUE @@ -97,8 +95,7 @@ Bonus level = 7 severity = 6 base_message_chance = 15 - symptom_delay_min = 14 - symptom_delay_max = 30 + symptom_delay = 22 required_organ = ORGAN_SLOT_LUNGS symptom_cure = /datum/reagent/toxin/bonehurtingjuice // It'll be funny I swear cure_color = "orange" // The only level 7 symptom without a red color diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index 3e962c090cf6..2431d48456d9 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -17,8 +17,7 @@ level = 4 severity = 2 base_message_chance = 25 - symptom_delay_min = 10 - symptom_delay_max = 30 + symptom_delay = 20 symptom_cure = /datum/reagent/medicine/synaptizine threshold_descs = list( "Resistance 6" = "Causes brain damage over time.", diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index 2648ece75cf9..687d60e1b800 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -17,8 +17,7 @@ level = 1 severity = 1 base_message_chance = 15 - symptom_delay_min = 2 - symptom_delay_max = 15 + symptom_delay = 8.5 required_organ = ORGAN_SLOT_LUNGS threshold_descs = list( "Resistance 11" = "The host will drop small items when coughing.", @@ -46,7 +45,7 @@ if(active_disease.totalResistance() >= 15) //strong enough to stun (occasionally) power = 2 if(active_disease.totalStageSpeed() >= 6) //cough more often - symptom_delay_max = 10 + symptom_delay = 6 /datum/symptom/cough/Activate(datum/disease/advance/active_disease) . = ..() diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index 0b441d67536a..ef0874b5785b 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -17,8 +17,7 @@ level = 4 severity = 4 base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 80 + symptom_delay = 52.5 required_organ = ORGAN_SLOT_EARS symptom_cure = /datum/reagent/medicine/psicodine threshold_descs = list( diff --git a/code/datums/diseases/advance/symptoms/disfiguration.dm b/code/datums/diseases/advance/symptoms/disfiguration.dm index 08f6896d0162..b232decf4e47 100644 --- a/code/datums/diseases/advance/symptoms/disfiguration.dm +++ b/code/datums/diseases/advance/symptoms/disfiguration.dm @@ -16,8 +16,7 @@ transmittable = 1 level = 5 severity = 1 - symptom_delay_min = 25 - symptom_delay_max = 75 + symptom_delay = 50 symptom_cure = /datum/reagent/consumable/milk /datum/symptom/disfiguration/Activate(datum/disease/advance/disease) diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index 9ec7d476fb83..6fd6986f5857 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -18,8 +18,7 @@ level = 4 severity = 2 base_message_chance = 50 - symptom_delay_min = 15 - symptom_delay_max = 30 + symptom_delay = 22.5 symptom_cure = /datum/reagent/medicine/haloperidol cure_color = "yellow" threshold_descs = list( diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index dfdfb37591af..885058381dea 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -19,8 +19,7 @@ level = 2 severity = 2 base_message_chance = 20 - symptom_delay_min = 10 - symptom_delay_max = 30 + symptom_delay = 20 symptom_cure = /datum/reagent/medicine/leporazine threshold_descs = list( "Resistance 5" = "Increases fever intensity, fever can overheat and harm the host.", diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm index 18583bc98187..5931f721efee 100644 --- a/code/datums/diseases/advance/symptoms/fire.dm +++ b/code/datums/diseases/advance/symptoms/fire.dm @@ -18,8 +18,7 @@ level = 6 severity = 5 base_message_chance = 20 - symptom_delay_min = 20 - symptom_delay_max = 75 + symptom_delay = 47.5 symptom_cure = /datum/reagent/medicine/leporazine // See, this one's tricky. You gotta convince them you don't have fever or chills in the same virus. var/infective = FALSE threshold_descs = list( @@ -101,8 +100,7 @@ Bonus level = 7 severity = 6 base_message_chance = 100 - symptom_delay_min = 30 - symptom_delay_max = 90 + symptom_delay = 60 symptom_cure = /datum/reagent/consumable/frostoil cure_color = "red" var/chems = FALSE @@ -119,8 +117,7 @@ Bonus return if(A.totalResistance() >= 9) //intense but sporadic effect power = 2 - symptom_delay_min = 50 - symptom_delay_max = 140 + symptom_delay = 95 if(A.totalStageSpeed() >= 8) //serious boom when wet explosion_power = 2 if(A.totalTransmittable() >= 8) //extra chemicals diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index d767f33fae33..bf7f2e2f4dea 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -19,8 +19,7 @@ Bonus level = 6 severity = 5 base_message_chance = 50 - symptom_delay_min = 15 - symptom_delay_max = 60 + symptom_delay = 37.5 symptom_cure = /datum/reagent/medicine/omnizine cure_color = "orange" threshold_descs = list( @@ -93,8 +92,7 @@ Bonus level = 7 severity = 6 base_message_chance = 50 - symptom_delay_min = 3 - symptom_delay_max = 6 + symptom_delay = 4.5 symptom_cure = /datum/reagent/medicine/earthsblood // Good luck finding THAT. cure_color = "red" var/chems = FALSE diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm index cc16d70ad10c..9ebadf6b1327 100644 --- a/code/datums/diseases/advance/symptoms/genetics.dm +++ b/code/datums/diseases/advance/symptoms/genetics.dm @@ -18,8 +18,7 @@ level = 6 severity = 4 base_message_chance = 50 - symptom_delay_min = 30 - symptom_delay_max = 60 + symptom_delay = 45 symptom_cure = /datum/reagent/acetaldehyde cure_color = "orange" var/excludemuts = NONE @@ -39,8 +38,7 @@ if(A.totalStealth() >= 5) //only give them bad mutations excludemuts = POSITIVE if(A.totalStageSpeed() >= 10) //activate dormant mutations more often at around 1.5x the pace - symptom_delay_min = 20 - symptom_delay_max = 40 + symptom_delay = 30 if(A.totalResistance() >= 8) //mutadone won't save you now mutadone_proof = (NEGATIVE | MINOR_NEGATIVE) if(A.totalResistance() >= 14) //one does not simply escape Nurgle's grasp diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index 1eaa029d96e4..54ef506a710c 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -18,8 +18,7 @@ level = 5 severity = 2 base_message_chance = 25 - symptom_delay_min = 25 - symptom_delay_max = 90 + symptom_delay = 57.5 symptom_cure = /datum/reagent/medicine/psicodine cure_color = "yellow" threshold_descs = list( diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index ea1aca7d2714..2b966ddc2c9f 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -17,8 +17,7 @@ level = 1 severity = 1 base_message_chance = 100 - symptom_delay_min = 15 - symptom_delay_max = 30 + symptom_delay = 22.5 symptom_cure = /datum/reagent/drug/space_drugs threshold_descs = list( "Stage Speed 6" = "Headaches will cause severe pain, that weakens the host.", @@ -35,8 +34,7 @@ if(A.totalStageSpeed() >= 6) //severe pain power = 2 if(A.totalStageSpeed() >= 9) //cluster headaches - symptom_delay_min = 30 - symptom_delay_max = 60 + symptom_delay = 45 power = 3 /datum/symptom/headache/Activate(datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 149d631cff61..3b5abf2a824e 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -8,8 +8,8 @@ transmittable = 0 level = 0 //not obtainable base_message_chance = 20 //here used for the overlays - symptom_delay_min = 1 - symptom_delay_max = 1 + symptom_delay = 1 + delay_variation = 0 var/passive_message = "" //random message to infected but not actively healing people var/healable_bodytypes = BODYTYPE_ORGANIC // What types of body parts we can heal @@ -366,7 +366,7 @@ if(living_host.IsSleeping()) return power * 0.25 //Voluntary unconsciousness yields lower healing. switch(living_host.stat) - if(UNCONSCIOUS, HARD_CRIT) + if(HARD_CRIT) return power * 0.9 if(SOFT_CRIT) return power * 0.5 @@ -575,8 +575,6 @@ stage_speed = 2 transmittable = -3 level = 6 - symptom_delay_min = 1 - symptom_delay_max = 1 passive_message = span_notice("Your skin glows faintly for a moment.") threshold_descs = list( "Resistance 7" = "Increases healing speed.", @@ -617,8 +615,6 @@ transmittable = -1 level = 4 base_message_chance = 0 - symptom_delay_min = 1 - symptom_delay_max = 1 symptom_cure = null power = 2 diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 7615edb1b82d..090718f8c39c 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -16,8 +16,7 @@ transmittable = 1 level = 1 severity = 1 - symptom_delay_min = 5 - symptom_delay_max = 25 + symptom_delay = 15 var/scratch = FALSE threshold_descs = list( "Transmission 6" = "Increases frequency of itching.", @@ -33,8 +32,7 @@ if(!.) return if(active_disease.totalTransmittable() >= 6) //itch more often - symptom_delay_min = 1 - symptom_delay_max = 4 + symptom_delay = 2.5 if(active_disease.totalStageSpeed() >= 7) //scratch scratch = TRUE diff --git a/code/datums/diseases/advance/symptoms/narcolepsy.dm b/code/datums/diseases/advance/symptoms/narcolepsy.dm index da83bb5d5ed5..7520f4165500 100644 --- a/code/datums/diseases/advance/symptoms/narcolepsy.dm +++ b/code/datums/diseases/advance/symptoms/narcolepsy.dm @@ -15,8 +15,7 @@ stage_speed = -2 transmittable = 0 level = 6 - symptom_delay_min = 30 - symptom_delay_max = 85 + symptom_delay = 57.5 severity = 4 symptom_cure = /datum/reagent/medicine/ondansetron cure_color = "yellow" @@ -33,8 +32,7 @@ if(A.totalTransmittable() >= 4) //yawning (mostly just some copy+pasted code from sneezing, with a few tweaks) yawning = TRUE if(A.totalStageSpeed() >= 10) //act more often - symptom_delay_min = 20 - symptom_delay_max = 45 + symptom_delay = 32.5 /datum/symptom/narcolepsy/Activate(datum/disease/advance/A) . = ..() diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm index 3478114bc6b3..48b3c99c00fd 100644 --- a/code/datums/diseases/advance/symptoms/oxygen.dm +++ b/code/datums/diseases/advance/symptoms/oxygen.dm @@ -15,8 +15,8 @@ transmittable = -4 level = 6 base_message_chance = 3 - symptom_delay_min = 1 - symptom_delay_max = 1 + symptom_delay = 1 + delay_variation = 0 required_organ = ORGAN_SLOT_LUNGS threshold_descs = list( "Resistance 8" = "Additionally regenerates lost blood." diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 894b6ffcfec4..9423d07647f4 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -13,8 +13,7 @@ stage_speed = 1 transmittable = -3 level = 5 - symptom_delay_min = 5 - symptom_delay_max = 10 + symptom_delay = 7.5 var/purge_alcohol = FALSE var/trauma_heal_mild = FALSE var/trauma_heal_severe = FALSE @@ -84,8 +83,8 @@ transmittable = 2 level = 4 base_message_chance = 7 - symptom_delay_min = 1 - symptom_delay_max = 1 + symptom_delay = 1 + delay_variation = 0 /datum/symptom/sensory_restoration/Activate(datum/disease/advance/advanced_disease) . = ..() diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm index df07e883f8fd..685aa3ad02c8 100644 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ b/code/datums/diseases/advance/symptoms/shedding.dm @@ -19,8 +19,7 @@ symptom_cure = /datum/reagent/barbers_aid cure_color = "yellow" base_message_chance = 50 - symptom_delay_min = 45 - symptom_delay_max = 90 + symptom_delay = 67.5 /datum/symptom/shedding/Activate(datum/disease/advance/disease) . = ..() diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index 150b4ade87df..5714edc5e47b 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -16,8 +16,7 @@ transmittable = 2 level = 5 severity = 1 - symptom_delay_min = 7 - symptom_delay_max = 14 + symptom_delay = 10.5 symptom_cure = /datum/reagent/water/salt /datum/symptom/polyvitiligo/Activate(datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 194b762cf02d..16c29313313a 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -16,8 +16,8 @@ transmittable = 4 level = 1 severity = 1 - symptom_delay_min = 5 - symptom_delay_max = 35 + symptom_delay = 20 + delay_variation = 0.4 required_organ = ORGAN_SLOT_LUNGS threshold_descs = list( "Transmission 9" = "Increases sneezing range, spreading the virus over 6 meter cone instead of over a 4 meter cone.", diff --git a/code/datums/diseases/advance/symptoms/species.dm b/code/datums/diseases/advance/symptoms/species.dm index 906952b29828..56813ca03323 100644 --- a/code/datums/diseases/advance/symptoms/species.dm +++ b/code/datums/diseases/advance/symptoms/species.dm @@ -8,7 +8,7 @@ */ /datum/symptom/undead_adaptation name = "Necrotic Metabolism" - desc = "The virus is able to thrive and act even within dead hosts." + desc = "The virus is able to thrive and act even within dead hosts. Has no effects on its own." stealth = 2 resistance = -2 stage_speed = 1 @@ -16,6 +16,7 @@ level = 5 severity = 0 symptom_cure = null + immunity_proof = TRUE /datum/symptom/undead_adaptation/OnAdd(datum/disease/advance/A) A.process_dead = TRUE @@ -35,7 +36,7 @@ */ /datum/symptom/inorganic_adaptation name = "Inorganic Biology" - desc = "The virus can survive and replicate even in an inorganic environment, increasing its resistance and infection rate." + desc = "The virus can survive and replicate even in an inorganic environment, increasing its resistance and infection rate. Has no effects on its own." stealth = -1 resistance = 4 stage_speed = -2 @@ -43,6 +44,7 @@ level = 5 severity = 0 symptom_cure = null + immunity_proof = TRUE /datum/symptom/inorganic_adaptation/OnAdd(datum/disease/advance/A) A.infectable_biotypes |= MOB_MINERAL | MOB_ROBOTIC // Plasmamen, golems, and androids. diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 64f110539c20..7da6e6844307 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -28,8 +28,8 @@ var/suppress_warning = FALSE ///Ticks between each activation var/next_activation = 0 - var/symptom_delay_min = 1 - var/symptom_delay_max = 1 + var/symptom_delay = 1 // Measured in seconds, not ticks or life ticks + var/delay_variation = 0.25 // Anywhere from -25% to +25% ///Can be used to multiply virus effects var/power = 1 ///A neutered symptom has no effect, and only affects statistics. @@ -45,6 +45,8 @@ var/cure_color = "green" ///A remedied symptom has no effect and contributes to the cure var/remedied = FALSE + ///Whether or not mobs can gain immunity to this symptom after recovering from a disease with it + var/immunity_proof = FALSE /datum/symptom/New() var/list/S = SSdisease.list_symptoms @@ -55,9 +57,11 @@ CRASH("We couldn't assign an ID!") ///Called when processing of the advance disease that holds this symptom infects a host and upon each Refresh() of that advance disease. -/datum/symptom/proc/Start(datum/disease/advance/A) +/datum/symptom/proc/Start(datum/disease/advance/advanced_disease) if(neutered) return FALSE + if(name in advanced_disease.affected_mob.symptom_resistances) + symptom_delay *= 1.75 return TRUE ///Called when the advance disease is going to be deleted or when the advance disease stops processing. @@ -80,7 +84,7 @@ if(world.time < next_activation) return FALSE else - next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) + next_activation = world.time + rand(symptom_delay * (1 - delay_variation) SECONDS, symptom_delay * (1 + delay_variation) SECONDS) * DISEASE_SYMPTOM_FREQUENCY_MODIFIER return TRUE /datum/symptom/proc/on_stage_change(datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm index dfed4cb7dbe4..ce02eb19e705 100644 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ b/code/datums/diseases/advance/symptoms/viral.dm @@ -8,7 +8,8 @@ */ /datum/symptom/viraladaptation name = "Viral self-adaptation" - desc = "The virus mimics the function of normal body cells, becoming harder to spot and to eradicate, but reducing its speed." + desc = "The virus mimics the function of normal body cells, becoming harder to spot and to eradicate, but reducing its speed. \ + Does nothing by itself." stealth = 3 resistance = 5 stage_speed = -3 @@ -16,6 +17,11 @@ level = 3 symptom_cure = null +/datum/symptom/viraladaptation/OnAdd(datum/disease/advance/our_disease) + . = ..() + our_disease.NeuterSymptom(src) + + /*Viral evolution * Reduces stealth * Greatly reduces resistance @@ -27,10 +33,14 @@ /datum/symptom/viralevolution name = "Viral evolutionary acceleration" desc = "The virus quickly adapts to spread as fast as possible both outside and inside a host. \ - This, however, makes the virus easier to spot, and less able to fight off a cure." + This, however, makes the virus easier to spot, and less able to fight off a cure. Does nothing by itself." stealth = -2 resistance = -3 stage_speed = 5 transmittable = 3 level = 3 symptom_cure = null + +/datum/symptom/viralevolution/OnAdd(datum/disease/advance/our_disease) + . = ..() + our_disease.NeuterSymptom(src) diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index bcf7a0a58363..67ce7653ea22 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -17,8 +17,7 @@ level = 5 severity = 5 base_message_chance = 50 - symptom_delay_min = 25 - symptom_delay_max = 80 + symptom_delay = 52.5 required_organ = ORGAN_SLOT_EYES symptom_cure = /datum/reagent/medicine/oculine cure_color = "yellow" diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm index a14ee8725253..e2218e4cfa17 100644 --- a/code/datums/diseases/advance/symptoms/voice_change.dm +++ b/code/datums/diseases/advance/symptoms/voice_change.dm @@ -18,8 +18,7 @@ level = 6 severity = 2 base_message_chance = 100 - symptom_delay_min = 60 - symptom_delay_max = 120 + symptom_delay = 90 required_organ = ORGAN_SLOT_TONGUE symptom_cure = /datum/reagent/inverse/healing/convermol threshold_descs = list( @@ -38,8 +37,8 @@ suppress_warning = TRUE if(A.totalStageSpeed() >= 7) //faster change of voice base_message_chance = 25 - symptom_delay_min = 25 - symptom_delay_max = 85 + symptom_delay = 55 + delay_variation = 0.35 if(A.totalTransmittable() >= 14) //random language scramble_language = TRUE diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index 7500f9b032a8..8f6318d1de04 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -18,8 +18,7 @@ and your disease can spread via people walking on vomit. level = 3 severity = 3 base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 80 + symptom_delay = 52.5 required_organ = ORGAN_SLOT_STOMACH symptom_cure = /datum/reagent/medicine/ondansetron cure_color = "yellow" diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index c2bcda287b21..a29b72a50793 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -16,8 +16,7 @@ level = 3 severity = 3 base_message_chance = 100 - symptom_delay_min = 15 - symptom_delay_max = 45 + symptom_delay = 30 required_organ = ORGAN_SLOT_STOMACH symptom_cure = /datum/reagent/medicine/diphenhydramine // It's good with terrible stats, it should be harder to cure cure_color = "orange" diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index 0ca3bd084f81..472aea77b623 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -17,9 +17,9 @@ transmittable = -4 level = 5 base_message_chance = 100 - symptom_delay_min = 25 - symptom_delay_max = 50 + symptom_delay = 37.5 symptom_cure = null + immunity_proof = TRUE /datum/symptom/youth/Activate(datum/disease/advance/A) . = ..() diff --git a/code/datums/diseases/floor_diseases/carpellosis.dm b/code/datums/diseases/floor_diseases/carpellosis.dm index e19c070d2c83..271b2802d485 100644 --- a/code/datums/diseases/floor_diseases/carpellosis.dm +++ b/code/datums/diseases/floor_diseases/carpellosis.dm @@ -32,25 +32,25 @@ switch(stage) if(2) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS && affected_mob.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob) && affected_mob.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)) to_chat(affected_mob, span_warning("You want to wag your tail...")) affected_mob.emote("wag") if(3) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("You suddenly feel like swimming in space...")) - else if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + else if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) affected_mob.visible_message("gnashes.", visible_message_flags = EMOTE_MESSAGE) if(4) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) gnash_someone() - else if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + else if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) affected_mob.visible_message("gnashes.", visible_message_flags = EMOTE_MESSAGE) if(5) max_stage_reached = TRUE grant_ability() - if(SPT_PROB(2, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(2, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) gnash_someone() - else if(SPT_PROB(2, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + else if(SPT_PROB(2, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) affected_mob.visible_message("gnashes.", visible_message_flags = EMOTE_MESSAGE) /datum/disease/carpellosis/Destroy() @@ -76,8 +76,8 @@ /datum/disease/carpellosis/proc/find_nearby_human() var/list/surroundings = orange(GNASHING_RANGE, affected_mob) - for(var/mob/human as anything in typecache_filter_list(surroundings, typecacheof(/mob/living/carbon/human))) - if(human.stat != DEAD && !(HAS_TRAIT(human, TRAIT_FAKEDEATH))) + for(var/mob/living/carbon/human/human as anything in typecache_filter_list(surroundings, typecacheof(/mob/living/carbon/human))) + if(!IS_DEAD_OR_FAKING(human)) return human /datum/disease/carpellosis/proc/gnash_someone() diff --git a/code/datums/diseases/floor_diseases/gastritium.dm b/code/datums/diseases/floor_diseases/gastritium.dm index e87555111476..35a3d8f55844 100644 --- a/code/datums/diseases/floor_diseases/gastritium.dm +++ b/code/datums/diseases/floor_diseases/gastritium.dm @@ -25,17 +25,17 @@ if(SPT_PROB(1, seconds_per_tick)) affected_mob.emote("burp") if(3) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("Your stomach makes turbine noises...")) else if(SPT_PROB(1, seconds_per_tick)) affected_mob.emote("burp") if(4) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("You're starting to feel like a burn chamber...")) else if(SPT_PROB(1, seconds_per_tick)) tritium_burp() if(5) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("You feel like you're about to delam...")) else if(SPT_PROB(1, seconds_per_tick)) tritium_burp(hot_chance = TRUE) @@ -48,7 +48,7 @@ burp.temperature = affected_mob.bodytemperature if(hot_chance && prob(tritium_burp_hot_chance)) burp.set_temperature(TRITIUM_MINIMUM_BURN_TEMPERATURE) - if(affected_mob.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("Your throat feels hot!")) affected_mob.visible_message("burps out green gas.", visible_message_flags = EMOTE_MESSAGE) affected_mob.loc.assume_air(burp) diff --git a/code/datums/diseases/floor_diseases/nebula_nausea.dm b/code/datums/diseases/floor_diseases/nebula_nausea.dm index 2fefc3f1eb0f..9628e545f656 100644 --- a/code/datums/diseases/floor_diseases/nebula_nausea.dm +++ b/code/datums/diseases/floor_diseases/nebula_nausea.dm @@ -20,16 +20,16 @@ switch(stage) if(2) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("The colorful beauty of the cosmos seems to have taken a toll on your equilibrium.")) if(3) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("Your stomach swirls with colors unseen by human eyes.")) if(4) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("It feels like you're floating through a maelstrom of celestial colors.")) if(5) - if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS) + if(SPT_PROB(1, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(affected_mob)) to_chat(affected_mob, span_warning("Your stomach has become a turbulent nebula, swirling with kaleidoscopic patterns.")) else affected_mob.vomit(vomit_flags = (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM), vomit_type = /obj/effect/decal/cleanable/vomit/nebula, lost_nutrition = 10, distance = 2) diff --git a/code/datums/diseases/verminous_plague.dm b/code/datums/diseases/verminous_plague.dm index 8e56f7fcc08b..981bfadea015 100644 --- a/code/datums/diseases/verminous_plague.dm +++ b/code/datums/diseases/verminous_plague.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(cursed_vermin_by_stage, list( if (stage == 1 || !created.ai_controller) return - created.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, affected_mob) + created.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, affected_mob) created.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, affected_mob) /datum/disease/verminous_plague/update_stage(new_stage) diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm index 21c8f8828c32..08896ac1ec8c 100644 --- a/code/datums/diseases/wizarditis.dm +++ b/code/datums/diseases/wizarditis.dm @@ -113,11 +113,11 @@ var/mob/living/carbon/human/human_mob = affected_mob - if(prob(chance) && !(human_mob.head?.item_flags & CASTING_CLOTHES)) + if(prob(chance) && !HAS_TRAIT(human_mob.get_item_by_slot(ITEM_SLOT_HEAD), TRAIT_CASTING_CLOTHING)) if(human_mob.dropItemToGround(human_mob.head)) human_mob.equip_to_slot_or_del(new hat_type(human_mob), ITEM_SLOT_HEAD) - if(prob(chance) && !(human_mob.wear_suit?.item_flags & CASTING_CLOTHES)) + if(prob(chance) && !HAS_TRAIT(human_mob.get_item_by_slot(ITEM_SLOT_OCLOTHING), TRAIT_CASTING_CLOTHING)) if(human_mob.dropItemToGround(human_mob.wear_suit)) human_mob.equip_to_slot_or_del(new robe_type(human_mob), ITEM_SLOT_OCLOTHING) diff --git a/code/datums/dna/dna.dm b/code/datums/dna/dna.dm index 6f8c868a2c8e..a7251a6b2c1c 100644 --- a/code/datums/dna/dna.dm +++ b/code/datums/dna/dna.dm @@ -34,7 +34,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) ///The blood type datum, usually a singleton var/datum/blood_type/blood_type ///The type of mutant race the player is if applicable (i.e. potato-man) - var/datum/species/species = new /datum/species/human + var/datum/species/species = /datum/species/human /// Assoc list of feature keys to their value /// Note if you set these manually, and do not update [unique_features] afterwards, it will likely be reset. var/list/features = list(FEATURE_MUTANT_COLOR = COLOR_WHITE) @@ -57,9 +57,12 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) /// Weighted list of lethal meltdowns var/static/list/fatal_meltdowns = list() -/datum/dna/New(mob/living/new_holder) +/datum/dna/New(mob/living/new_holder, datum/species/mob_species) if(istype(new_holder)) holder = new_holder + if(mob_species) + species = mob_species + species = new species /datum/dna/Destroy() if (iscarbon(holder)) @@ -132,6 +135,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(!length(actual_mutation.sources)) if(!actual_mutation.on_acquiring(holder)) + to_chat(holder, span_warning("You feel your genes resisting something.")) qdel(actual_mutation) return actual_mutation.setup() @@ -473,11 +477,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(allowed_sources) dna.add_mutation(mutation, allowed_sources) -/mob/living/carbon/proc/create_dna() - dna = new /datum/dna(src) - if(!dna.species) - var/rando_race = pick(get_selectable_species()) - dna.species = new rando_race() +/mob/living/carbon/proc/create_dna(datum/species/species) + dna = new /datum/dna(src, species) //proc used to update the mob's appearance after its dna UI has been changed //2025: Im unsure if dna is meant to be living, carbon, or human level.. there's contradicting stuff and bugfixes going back 8 years diff --git a/code/datums/dog_fashion.dm b/code/datums/dog_fashion.dm index 1c27df608229..ef95b1bce7a8 100644 --- a/code/datums/dog_fashion.dm +++ b/code/datums/dog_fashion.dm @@ -45,13 +45,13 @@ dressup_doggy.speak_emote = string_list(speak_emote) ///Applies random speech modifiers to the dog -/datum/dog_fashion/proc/apply_to_speech(datum/ai_planning_subtree/random_speech/speech) +/datum/dog_fashion/proc/apply_to_speech(list/speech_data) if(LAZYLEN(emote_see)) - speech.emote_see = string_list(emote_see) + speech_data[BB_EMOTE_SEE] = string_list(emote_see) if(LAZYLEN(emote_hear)) - speech.emote_hear = string_list(emote_hear) + speech_data[BB_EMOTE_HEAR] = string_list(emote_hear) if(LAZYLEN(speak)) - speech.speak = string_list(speak) + speech_data[BB_EMOTE_SAY] = string_list(speak) /** * Generates the icon overlay for the equipped item diff --git a/code/datums/eigenstate.dm b/code/datums/eigenstate.dm index 390d79bde950..90b76662a050 100644 --- a/code/datums/eigenstate.dm +++ b/code/datums/eigenstate.dm @@ -53,7 +53,8 @@ GLOBAL_DATUM_INIT(closet_teleport_controller, /datum/closet_teleport_controller, target.alpha = 200 do_sparks(3, FALSE, target) - visible_atom.visible_message("The items shimmer and fizzle, turning a shade of violet blue.") + if(!subtle) + visible_atom.visible_message("The items shimmer and fizzle, turning a shade of violet blue.") id_counter++ return TRUE @@ -90,6 +91,9 @@ GLOBAL_DATUM_INIT(closet_teleport_controller, /datum/closet_teleport_controller, /datum/closet_teleport_controller/proc/use_eigenlinked_atom(atom/object_sent_from, atom/movable/thing_to_send) SIGNAL_HANDLER + var/tp_flags = SEND_SIGNAL(object_sent_from, COMSIG_CLOSET_TELEPORTER_PRE_SENDING, thing_to_send) + if(tp_flags & CLOSET_TELEPORT_BLOCKED) + return FALSE var/id = eigen_id[object_sent_from] if(!id) stack_trace("[object_sent_from] attempted to eigenlink to something that didn't have a valid id!") @@ -106,7 +110,7 @@ GLOBAL_DATUM_INIT(closet_teleport_controller, /datum/closet_teleport_controller, if(!eigen_target) stack_trace("No eigen target set for the eigenstate component!") return FALSE - if(check_teleport_valid(thing_to_send, eigen_target, TELEPORT_CHANNEL_QUANTUM)) + if((tp_flags & CLOSET_TELEPORT_FORCED) || check_teleport_valid(thing_to_send, eigen_target, TELEPORT_CHANNEL_QUANTUM)) thing_to_send.forceMove(eigen_target) else if(!subtle) diff --git a/code/datums/elements/ai_flee_while_injured.dm b/code/datums/elements/ai_flee_while_injured.dm index eca709dbee5d..c478181244ea 100644 --- a/code/datums/elements/ai_flee_while_injured.dm +++ b/code/datums/elements/ai_flee_while_injured.dm @@ -36,11 +36,9 @@ if (source.ai_controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) if (current_health_percentage > start_fleeing_below) return - source.ai_controller.CancelActions() source.ai_controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, FALSE) return if (current_health_percentage < stop_fleeing_at) return - source.ai_controller.CancelActions() // Stop fleeing go back to whatever you were doing source.ai_controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, TRUE) diff --git a/code/datums/elements/ai_retaliate.dm b/code/datums/elements/ai_retaliate.dm index 4fcb51581438..c4c4266e2a4e 100644 --- a/code/datums/elements/ai_retaliate.dm +++ b/code/datums/elements/ai_retaliate.dm @@ -18,7 +18,7 @@ . = ..() UnregisterSignal(source, COMSIG_ATOM_WAS_ATTACKED) -/// Add an attacking atom to a blackboard list of things which attacked us +/// Add an attacking atom to a blackboard list of things which attacked us. /datum/element/ai_retaliate/proc/on_attacked(mob/victim, atom/attacker) SIGNAL_HANDLER diff --git a/code/datums/elements/ai_target_damagesource.dm b/code/datums/elements/ai_target_damagesource.dm index a1f0ea8b2a51..7ef04b9e4cde 100644 --- a/code/datums/elements/ai_target_damagesource.dm +++ b/code/datums/elements/ai_target_damagesource.dm @@ -22,5 +22,5 @@ if (!victim.ai_controller) return - victim.ai_controller.CancelActions() - victim.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, attacker) + victim.ai_controller.cancel_current_plan() + victim.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, attacker) diff --git a/code/datums/elements/art.dm b/code/datums/elements/art.dm index 93e156081d9d..1bc5bcc197c1 100644 --- a/code/datums/elements/art.dm +++ b/code/datums/elements/art.dm @@ -30,7 +30,7 @@ msg = "[source.p_Theyre()] fair to middling, enough to be called an \"art object\"." if (0 to BAD_ART) user.add_mood_event("artbad", /datum/mood_event/artbad) - msg = "Wow, [source.p_they()] sucks." + msg = "Wow, [source.p_they()] suck[source.p_s()]." user.visible_message(span_notice("[user] stops and looks intently at [source]."), \ span_notice("You appraise [source]... [msg]")) @@ -60,7 +60,7 @@ msg = "What \a [pick("masterpiece", "chef-d'oeuvre")] [source.p_theyre()]. So [pick("subversive", "revolutionary", "unitizing", "egalitarian")]!" else user.add_mood_event("artbad", /datum/mood_event/artbad) - msg = "Wow, [source.p_they()] sucks." + msg = "Wow, [source.p_they()] suck[source.p_s()]." user.visible_message(span_notice("[user] stops to inspect [source]."), \ span_notice("You appraise [source], inspecting the fine craftsmanship of the proletariat... [msg]")) @@ -84,7 +84,7 @@ msg = "What \a [pick("masterpiece", "chef-d'oeuvre")] [source.p_theyre()]. So [pick("relatable", "down to earth", "true", "real")]!" else user.add_mood_event("artbad", /datum/mood_event/artbad) - msg = "Wow, [source.p_they()] sucks." + msg = "Wow, [source.p_they()] suck[source.p_s()]." user.visible_message(span_notice("[user] stops to inspect [source]."), \ span_notice("You appraise [source], inspecting the fine craftsmanship of the proletariat... [msg]")) diff --git a/code/datums/elements/basic_eating.dm b/code/datums/elements/basic_eating.dm index 8b011823e16f..5521ea1d9b7a 100644 --- a/code/datums/elements/basic_eating.dm +++ b/code/datums/elements/basic_eating.dm @@ -53,7 +53,7 @@ if(user.combat_mode || !is_type_in_list(possible_food, food_types)) return NONE var/mob/living/living_source = source - if(living_source.stat != CONSCIOUS) + if(IS_UNCONSCIOUS(living_source)) return NONE return try_eating(source, possible_food, user) ? ITEM_INTERACT_SUCCESS : NONE @@ -138,4 +138,3 @@ movable_target.forceMove(eater) else qdel(final_target) - diff --git a/code/datums/elements/befriend_petting.dm b/code/datums/elements/befriend_petting.dm index 085a0651376e..a49d6fa7ad6f 100644 --- a/code/datums/elements/befriend_petting.dm +++ b/code/datums/elements/befriend_petting.dm @@ -39,7 +39,7 @@ var/additional_text = HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "It looks like [owner.p_theyre()] sleeping." : "[owner.p_They()] seem[owner.p_s()] to be dead." to_chat(user, span_warning("[owner] feels cold to the touch. [additional_text]")) return - if (owner.stat != CONSCIOUS) + if (IS_UNCONSCIOUS(owner)) return if (!prob(befriend_chance)) return diff --git a/code/datums/elements/blessed_turf.dm b/code/datums/elements/blessed_turf.dm new file mode 100644 index 000000000000..bf97abd12ff8 --- /dev/null +++ b/code/datums/elements/blessed_turf.dm @@ -0,0 +1,36 @@ +#define BLESSING_APPEARANCE_KEY "blessing" +/// Attached to blessed turfs, prevents jaunting, revenant movement and cult teleportation +/datum/element/blessed_turf + +/datum/element/blessed_turf/Attach(datum/target, invisible = FALSE) + . = ..() + if (!isturf(target)) + return ELEMENT_INCOMPATIBLE + + var/turf/target_turf = target + ADD_TRAIT(target_turf, TRAIT_TURF_BLESSED, ELEMENT_TRAIT(type)) + RegisterSignal(target_turf, COMSIG_ATOM_INTERCEPT_TELEPORTING, PROC_REF(block_cult_teleport)) + + if (invisible) + return + + var/image/blessing_icon = image(icon = 'icons/effects/effects.dmi', icon_state = "blessed", layer = ABOVE_NORMAL_TURF_LAYER, loc = target_turf) + SET_PLANE_EXPLICIT(blessing_icon, GAME_PLANE, target_turf) + blessing_icon.alpha = 64 + blessing_icon.appearance_flags = RESET_ALPHA | RESET_COLOR | KEEP_APART + target_turf.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/blessed_aware, BLESSING_APPEARANCE_KEY, blessing_icon) + +/datum/element/blessed_turf/Detach(datum/source, ...) + . = ..() + var/turf/target_turf = source + REMOVE_TRAIT(target_turf, TRAIT_TURF_BLESSED, ELEMENT_TRAIT(type)) + UnregisterSignal(target_turf, COMSIG_ATOM_INTERCEPT_TELEPORTING) + target_turf.remove_alt_appearance(BLESSING_APPEARANCE_KEY) + +/datum/element/blessed_turf/proc/block_cult_teleport(datum/source, channel, turf/origin) + SIGNAL_HANDLER + + if(channel == TELEPORT_CHANNEL_CULT) + return COMPONENT_INTERCEPT_TELEPORT + +#undef BLESSING_APPEARANCE_KEY diff --git a/code/datums/elements/kill_achievement.dm b/code/datums/elements/kill_achievement.dm index 6ec8854d0f7d..54939f375cb9 100644 --- a/code/datums/elements/kill_achievement.dm +++ b/code/datums/elements/kill_achievement.dm @@ -47,7 +47,7 @@ for (var/mob/living/player in SSmobs.clients_by_zlevel[our_loc.z]) var/turf/player_turf = get_turf(player) - if (player.stat || get_dist(player_turf, source) > achievement_range) + if (IS_UNCONSCIOUS_OR_CRIT(player) || get_dist(player_turf, source) > achievement_range) continue for (var/achievement_type in achievement_types) diff --git a/code/datums/elements/on_hit_effect.dm b/code/datums/elements/on_hit_effect.dm index da37dab9ce37..9abcd6aaedd2 100644 --- a/code/datums/elements/on_hit_effect.dm +++ b/code/datums/elements/on_hit_effect.dm @@ -16,6 +16,8 @@ RegisterSignal(target, COMSIG_ITEM_AFTERATTACK, PROC_REF(item_afterattack)) else if(isanimal_or_basicmob(target)) RegisterSignal(target, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(hostile_attackingtarget)) + else if(ishuman(target)) + RegisterSignal(target, COMSIG_HUMAN_PUNCHED, PROC_REF(human_punched)) else if(isprojectile(target)) RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_projectile_self_hit)) else @@ -46,6 +48,12 @@ on_hit(attacker, attacker, target, attacker.zone_selected) + +/datum/element/on_hit_effect/proc/human_punched(mob/living/carbon/human/attacker, mob/living/carbon/human/target, damage, damage_type, obj/item/bodypart/hit, ...) + SIGNAL_HANDLER + + on_hit(attacker, attacker, target, hit.body_zone) + /datum/element/on_hit_effect/proc/on_projectile_hit(datum/fired_from, atom/movable/firer, atom/target, angle, body_zone) SIGNAL_HANDLER on_hit(fired_from, firer, target, body_zone) diff --git a/code/datums/elements/pet_bonus.dm b/code/datums/elements/pet_bonus.dm index d4834b79b7e2..dc0a216455a7 100644 --- a/code/datums/elements/pet_bonus.dm +++ b/code/datums/elements/pet_bonus.dm @@ -29,7 +29,7 @@ /datum/element/pet_bonus/proc/on_attack_hand(mob/living/pet, mob/living/petter, list/modifiers) SIGNAL_HANDLER - if(pet.stat != CONSCIOUS || petter.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) + if(IS_UNCONSCIOUS(pet) || petter.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)) return new /obj/effect/temp_visual/heart(get_turf(pet)) diff --git a/code/datums/elements/pet_collar.dm b/code/datums/elements/pet_collar.dm index f98767629e7e..5c50de17e145 100644 --- a/code/datums/elements/pet_collar.dm +++ b/code/datums/elements/pet_collar.dm @@ -80,7 +80,7 @@ var/icon_tag = "" - if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(source)) icon_tag = "_dead" else if(collar_resting_icon_state && source.resting) icon_tag = "_rest" diff --git a/code/datums/elements/simple_flying.dm b/code/datums/elements/simple_flying.dm index e6d8f42db838..c82bdb649367 100644 --- a/code/datums/elements/simple_flying.dm +++ b/code/datums/elements/simple_flying.dm @@ -23,7 +23,7 @@ /datum/element/simple_flying/proc/on_stat_change(mob/living/target, new_stat) SIGNAL_HANDLER - if(new_stat == CONSCIOUS) + if(new_stat == STABLE) ADD_TRAIT(target, TRAIT_MOVE_FLYING, ELEMENT_TRAIT(type)) else REMOVE_TRAIT(target, TRAIT_MOVE_FLYING, ELEMENT_TRAIT(type)) diff --git a/code/datums/elements/skittish.dm b/code/datums/elements/skittish.dm index da1b76ecfb54..94636e6b3cc9 100644 --- a/code/datums/elements/skittish.dm +++ b/code/datums/elements/skittish.dm @@ -17,7 +17,7 @@ /datum/element/skittish/proc/Bump(mob/living/scooby, atom/target) SIGNAL_HANDLER - if(scooby.stat != CONSCIOUS || scooby.move_intent != MOVE_INTENT_RUN) + if(IS_UNCONSCIOUS_OR_CRIT(scooby) || scooby.move_intent != MOVE_INTENT_RUN) return if(!istype(target, /obj/structure/closet)) diff --git a/code/datums/elements/slapcrafting.dm b/code/datums/elements/slapcrafting.dm index 18c375887d96..9f4ba4218b45 100644 --- a/code/datums/elements/slapcrafting.dm +++ b/code/datums/elements/slapcrafting.dm @@ -74,7 +74,7 @@ var/list/result_to_recipe = list() - var/final_recipe = valid_recipes[1] + var/datum/crafting_recipe/final_recipe = valid_recipes[1] var/string_chosen_recipe if(length(valid_recipes) > 1) for(var/datum/crafting_recipe/recipe as anything in valid_recipes) @@ -92,22 +92,17 @@ if(string_chosen_recipe) final_recipe = result_to_recipe[string_chosen_recipe] + if(ispath(final_recipe)) + var/recipe_path = final_recipe + final_recipe = GLOB.cooking_recipes_by_typepath[recipe_path] || GLOB.crafting_recipes_by_typepath[recipe_path] + if(!final_recipe) + CRASH("Recipe not located in cooking or crafting recipes: [recipe_path]") - var/datum/crafting_recipe/actual_recipe = final_recipe - - if(istype(actual_recipe, /datum/crafting_recipe/food)) - actual_recipe = locate(final_recipe) in GLOB.cooking_recipes - else - actual_recipe = locate(final_recipe) in GLOB.crafting_recipes - - if(!actual_recipe) - CRASH("Recipe not located in cooking or crafting recipes: [final_recipe]") - - var/atom/final_result = initial(actual_recipe.result) + var/atom/final_result = initial(final_recipe.result) to_chat(user, span_notice("You start crafting \a [initial(final_result.name)]...")) - var/error_string = craft_sheet.construct_item(user, actual_recipe) + var/error_string = craft_sheet.construct_item(user, final_recipe) if(istext(error_string)) to_chat(user, span_warning("Crafting failed[error_string]")) @@ -198,4 +193,3 @@ to_chat(user, boxed_message(span_notice(tool_list))) qdel(cur_recipe) - diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm index 4ae70bb50953..c58ed1631487 100644 --- a/code/datums/elements/spooky.dm +++ b/code/datums/elements/spooky.dm @@ -24,44 +24,42 @@ /datum/element/spooky/proc/spectral_attack(datum/source, mob/living/carbon/target, mob/user) SIGNAL_HANDLER - if(ishuman(user) && !isskeleton(user)) //this weapon wasn't meant for mortals. - var/mob/living/carbon/human/human_user = user - if(rattle_bones(human_user, stam_dam_mult = stam_damage_mult * 2)) - to_chat(human_user, span_userdanger("Your ears weren't meant for this spectral sound.")) - INVOKE_ASYNC(src, PROC_REF(spectral_change), human_user, user, source) + if(!HAS_TRAIT(user, TRAIT_SPOOKY_INSTRUMENT_PLAYER)) //this weapon wasn't meant for mortals. + if(rattle_bones(user, stam_dam_mult = stam_damage_mult * 2)) + to_chat(user, span_userdanger("Your ears weren't meant for this spectral sound.")) + if(ishuman(user)) + INVOKE_ASYNC(src, PROC_REF(spectral_change), user, user, source) return to_chat(target, span_userdanger("DOOT stat_allowed) - if(!intentional) - return FALSE - switch(user.stat) - if(SOFT_CRIT) - to_chat(user, span_warning("You cannot [key] while in a critical condition!")) - if(UNCONSCIOUS, HARD_CRIT) - to_chat(user, span_warning("You cannot [key] while unconscious!")) - if(DEAD) - to_chat(user, span_warning("You cannot [key] while dead!")) + if(IS_UNCONSCIOUS(user) && !(can_use_flags & EMOTE_CANUSE_UNCONSCIOUS)) + if(intentional) + to_chat(user, span_warning("You cannot [key] while unconscious!")) return FALSE - if(hands_use_check && HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) - if(!intentional) - return FALSE - to_chat(user, span_warning("You cannot use your hands to [key] right now!")) + if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) && (can_use_flags & EMOTE_CANUSE_REQUIRE_HANDS)) + if(intentional) + to_chat(user, span_warning("You cannot use your hands to [key] right now!")) return FALSE + switch(user.stat) + if(SOFT_CRIT) + if(!(can_use_flags & EMOTE_CANUSE_SOFTCRIT)) + if(intentional) + to_chat(user, span_warning("You cannot [key] while in a critical condition!")) + return FALSE + if(HARD_CRIT) + if(!(can_use_flags & EMOTE_CANUSE_HARDCRIT)) + if(intentional) + to_chat(user, span_warning("You cannot [key] while in a critical condition!")) + return FALSE + if(DEAD) + if(!(can_use_flags & EMOTE_CANUSE_DEAD)) + if(intentional) + to_chat(user, span_warning("You cannot [key] while dead!")) + return FALSE + if(HAS_TRAIT(user, TRAIT_EMOTEMUTE)) return FALSE @@ -435,7 +446,7 @@ return TRUE /mob/manual_emote(text, log_emote = null) - if (stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(src)) return FALSE if (isnull(log_emote)) log_emote = !isnull(client) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index b234481babc3..10fde7b8dae6 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -75,7 +75,7 @@ if(SEND_SIGNAL(teleatom, COMSIG_MOVABLE_TELEPORTING, destination, channel)) return FALSE - if(SEND_SIGNAL(destturf, COMSIG_ATOM_INTERCEPT_TELEPORTING, channel, curturf)) + if(SEND_SIGNAL(destturf, COMSIG_ATOM_INTERCEPT_TELEPORTING, channel, curturf) & COMPONENT_INTERCEPT_TELEPORT) return FALSE if(isobserver(teleatom)) diff --git a/code/datums/hud.dm b/code/datums/hud.dm index 398dfa42f461..a3e8070e57ae 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -39,6 +39,7 @@ GLOBAL_LIST_INIT(trait_to_hud, list( /// Assoc list of traits that block other traits' huds to list of hud (traits) that they block GLOBAL_LIST_INIT(trait_blockers_to_hud, list( TRAIT_BLOCK_SECHUD = list(TRAIT_SECURITY_HUD, TRAIT_SECURITY_HUD_ID_ONLY), + TRAIT_BLOCK_MEDHUD = list(TRAIT_MEDICAL_HUD, TRAIT_MEDICAL_HUD_SENSOR_ONLY), )) /datum/atom_hud diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index d4bd2f0ddf94..f1500d023f43 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -71,6 +71,10 @@ var/use_sound_tokens = FALSE ///The sound token instance for this looping sound. var/datum/sound_token/sound_token_instance + ///Opt-out of native sound repeat even if this loop would otherwise qualify for it. Relevant if you need to know timings about when the loop ends for example. + var/never_native_repeat = FALSE + ///Whether we're currently using native sound.repeat instead of re-firing on the SSsound_loops timer. + var/native_repeat_active = FALSE /datum/looping_sound/New( _parent, @@ -105,7 +109,7 @@ /datum/looping_sound/proc/start(on_behalf_of) if(on_behalf_of) set_parent(on_behalf_of) - if(timer_id) + if(timer_id || loop_started) return if(!use_sound_tokens && !sound_channel && reserve_random_channel) @@ -124,12 +128,14 @@ stop_current() if(null_parent) set_parent(null) - if(!timer_id) + if(!timer_id && !loop_started) return on_stop() - deltimer(timer_id, SSsound_loops) - timer_id = null + if(timer_id) + deltimer(timer_id, SSsound_loops) + timer_id = null loop_started = FALSE + native_repeat_active = FALSE if(reserved_channel) sound_channel = null @@ -139,6 +145,12 @@ /// The proc that handles starting the actual core sound loop. /datum/looping_sound/proc/start_sound_loop() loop_started = TRUE + if(can_native_repeat()) + native_repeat_active = TRUE + play(resolve_single_sound(), repeat_sound = TRUE) + if(max_loops) + timer_id = addtimer(CALLBACK(src, PROC_REF(stop)), mid_length * max_loops, TIMER_CLIENT_TIME | TIMER_DELETE_ME | TIMER_STOPPABLE, SSsound_loops) + return sound_loop() timer_id = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP | TIMER_DELETE_ME, SSsound_loops) @@ -163,6 +175,8 @@ */ /datum/looping_sound/proc/set_mid_length(new_mid) mid_length = new_mid + if(native_repeat_active) // Native repeat is driven by the sound file's own length, not mid_length + return if(!timer_id) return updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary), timer_subsystem = SSsound_loops) @@ -173,15 +187,17 @@ * Arguments: * * soundfile - The soundfile we want to play. * * volume_override - The volume we want to play the sound at, overriding the `volume` variable. + * * repeat_sound - Whether the sound should loop natively via sound.repeat (token path only). + * * delete_when_finished - Whether a freshly created sound token should self-delete once the sound ends (token path only). */ -/datum/looping_sound/proc/play(soundfile, volume_override) +/datum/looping_sound/proc/play(soundfile, volume_override, repeat_sound = FALSE, delete_when_finished = FALSE) if(use_sound_tokens) if(sound_token_instance) sound_token_instance.set_volume(volume_override || volume, FALSE) // Don't update, we'll do that after - sound_token_instance.update_sound(soundfile, TRUE) + sound_token_instance.update_sound(soundfile, TRUE, repeat_sound) else - sound_token_instance = new /datum/sound_token(parent, soundfile, SOUND_RANGE + extra_range, volume_override || volume, falloff_exponent, falloff_distance) + sound_token_instance = new /datum/sound_token(parent, soundfile, SOUND_RANGE + extra_range, volume_override || volume, falloff_exponent, falloff_distance, _delete_on_end = delete_when_finished, _repeating = repeat_sound) return var/sound/sound_to_play = sound(soundfile) sound_to_play.channel = sound_channel || SSsounds.random_available_channel() @@ -201,7 +217,6 @@ ignore_walls = ignore_walls, falloff_distance = falloff_distance, use_reverb = use_reverb, - channel = sound_channel || SSsounds.random_available_channel() ) /// Returns the sound we should now be playing. @@ -247,6 +262,24 @@ +/// Returns the lone soundfile mid_sounds resolves to, or null if it can pick between more than one file. +/datum/looping_sound/proc/resolve_single_sound() + if(isfile(mid_sounds) || istext(mid_sounds)) + return mid_sounds + if(islist(mid_sounds) && length(mid_sounds) == 1) + var/only_sound = mid_sounds[1] + if(isfile(only_sound) || istext(only_sound)) + return only_sound + return null + +/// Whether this loop qualifies for native sound.repeat instead of re-firing on a timer every mid_length. +/datum/looping_sound/proc/can_native_repeat() + if(!use_sound_tokens || never_native_repeat) + return FALSE + if(chance || mid_length_vary || each_once || in_order) + return FALSE + return !!resolve_single_sound() + /// A proc that's there to handle delaying the main sounds if there's a start_sound, and simply starting the sound loop in general. /datum/looping_sound/proc/on_start() var/start_wait = 0 @@ -269,7 +302,7 @@ /// Simple proc that's executed when the looping sound is stopped, so that the `end_sound` can be played, if there's one. /datum/looping_sound/proc/on_stop() if(end_sound && loop_started) - play(end_sound, end_volume) + play(end_sound, end_volume, delete_when_finished = TRUE) /// A simple proc to change who our parent is set to, also handling registering and unregistering the QDELETING signals on the parent. /datum/looping_sound/proc/set_parent(new_parent) @@ -281,7 +314,7 @@ /// A simple proc that lets us know whether the sounds are currently active or not. /datum/looping_sound/proc/is_active() - return !!timer_id + return loop_started || !!timer_id /// A simple proc to handle the deletion of the parent, so that it does not force it to hard-delete. /datum/looping_sound/proc/handle_parent_del(datum/source) diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm index 517dc752944d..7694873eb645 100644 --- a/code/datums/looping_sounds/item_sounds.dm +++ b/code/datums/looping_sounds/item_sounds.dm @@ -18,7 +18,7 @@ /datum/looping_sound/tape_recorder_hiss mid_sounds = list('sound/items/taperecorder/taperecorder_hiss_mid.ogg' = 1) - start_sound = list('sound/items/taperecorder/taperecorder_hiss_start.ogg' = 1) + start_sound = 'sound/items/taperecorder/taperecorder_hiss_start.ogg' volume = 10 /datum/looping_sound/heartbeat @@ -35,11 +35,11 @@ use_sound_tokens = TRUE /datum/looping_sound/chainsaw - start_sound = list('sound/items/weapons/chainsaw_start.ogg' = 1) + start_sound = 'sound/items/weapons/chainsaw_start.ogg' start_length = 0.85 SECONDS mid_sounds = list('sound/items/weapons/chainsaw_loop.ogg' = 1) mid_length = 0.85 SECONDS - end_sound = list('sound/items/weapons/chainsaw_stop.ogg' = 1) + end_sound = 'sound/items/weapons/chainsaw_stop.ogg' end_volume = 35 volume = 40 ignore_walls = FALSE @@ -54,13 +54,13 @@ volume = 5 /datum/looping_sound/tesla_cannon - start_sound = list('sound/items/weapons/gun/tesla/tesla_start.ogg' = 1) + start_sound = 'sound/items/weapons/gun/tesla/tesla_start.ogg' start_volume = 100 start_length = 200 MILLISECONDS mid_sounds = list('sound/items/weapons/gun/tesla/tesla_loop.ogg' = 1) mid_length = 3.8 SECONDS volume = 100 - end_sound = list('sound/items/weapons/gun/tesla/power_breaker_fan.ogg' = 1) + end_sound = 'sound/items/weapons/gun/tesla/power_breaker_fan.ogg' end_volume = 15 ignore_walls = FALSE reserve_random_channel = TRUE diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index a8549ab1418b..110d1dc5592a 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -11,8 +11,11 @@ volume = 20 /datum/looping_sound/supermatter - mid_sounds = list('sound/machines/sm/loops/calm.ogg') - mid_length = 6 SECONDS + start_sound = 'sound/machines/sm/loops/calm_start.ogg' + start_length = 1.6 SECONDS + mid_sounds = 'sound/machines/sm/loops/calm.ogg' + mid_length = 4 SECONDS + end_sound = 'sound/machines/sm/loops/calm_end.ogg' volume = 40 extra_range = 25 falloff_exponent = 10 @@ -150,10 +153,9 @@ start_sound = 'sound/machines/gravgen/grav_gen_start.ogg' start_length = 1 SECONDS mid_sounds = list( - 'sound/machines/gravgen/grav_gen_mid1.ogg' = 12, - 'sound/machines/gravgen/grav_gen_mid2.ogg' = 1, + 'sound/machines/gravgen/grav_gen_mid1.ogg' ) - mid_length = 1.1 SECONDS + mid_length = 2.4 SECONDS end_sound = 'sound/machines/gravgen/grav_gen_end.ogg' extra_range = 8 vary = TRUE diff --git a/code/datums/looping_sounds/weather.dm b/code/datums/looping_sounds/weather.dm index 1dd3df668b31..f960aef2e87d 100644 --- a/code/datums/looping_sounds/weather.dm +++ b/code/datums/looping_sounds/weather.dm @@ -54,6 +54,7 @@ pressure_affected = FALSE reserve_random_channel = TRUE use_sound_tokens = TRUE + never_native_repeat = TRUE /datum/looping_sound/snowstorm mid_sounds = list( diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 75420fcd8952..ab2d5843f5b8 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -102,7 +102,7 @@ /datum/martial_art/boxing/proc/tussle(mob/living/attacker, mob/living/defender) if(honorable_boxer) //Being a good sport, you never hit someone on the ground or already knocked down. It shows you're the better person. - if(defender.body_position == LYING_DOWN && defender.get_stamina_loss() >= 100 || defender.IsUnconscious()) //If they're in stamcrit or unconscious, don't bloody punch them + if((defender.body_position == LYING_DOWN && defender.get_stamina_loss() >= 100) || IS_UNCONSCIOUS(defender)) //If they're in stamcrit or unconscious, don't bloody punch them attacker.balloon_alert(attacker, "unsportsmanlike behaviour!") return FALSE diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm index 46993c93e02e..0ad8e2b3ed6a 100644 --- a/code/datums/martial/cqc.dm +++ b/code/datums/martial/cqc.dm @@ -121,11 +121,11 @@ return TRUE /datum/martial_art/cqc/proc/Kick(mob/living/attacker, mob/living/defender) - if(defender.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(defender)) return FALSE attacker.do_attack_animation(defender) - if(defender.body_position == LYING_DOWN && !defender.IsUnconscious() && defender.get_stamina_loss() >= 100) + if(defender.body_position == LYING_DOWN && !IS_UNCONSCIOUS(defender) && defender.get_stamina_loss() >= 100) log_combat(attacker, defender, "knocked out (Head kick)(CQC)") defender.visible_message( span_danger("[attacker] kicks [defender]'s head, knocking [defender.p_them()] out!"), @@ -155,7 +155,7 @@ var/atom/throw_target = get_edge_target_turf(defender, attacker.dir) defender.throw_at(throw_target, 1, 14, attacker) defender.apply_damage(10, attacker.get_attack_type()) - if(defender.body_position == LYING_DOWN && !defender.IsUnconscious()) + if(defender.body_position == LYING_DOWN && !IS_UNCONSCIOUS(defender)) defender.adjust_stamina_loss(45) log_combat(attacker, defender, "kicked (CQC)") @@ -179,7 +179,7 @@ /datum/martial_art/cqc/proc/Restrain(mob/living/attacker, mob/living/defender) if(restraining_mob?.resolve()) return FALSE - if(defender.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(defender)) return FALSE log_combat(attacker, defender, "restrained (CQC)") @@ -198,7 +198,7 @@ return TRUE /datum/martial_art/cqc/proc/Consecutive(mob/living/attacker, mob/living/defender) - if(defender.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(defender)) return FALSE attacker.do_attack_animation(defender) @@ -339,7 +339,7 @@ return MARTIAL_ATTACK_SUCCESS attacker.do_attack_animation(defender, ATTACK_EFFECT_DISARM) - if(prob(65) && (defender.stat == CONSCIOUS || !defender.IsParalyzed() || !restraining_mob?.resolve())) + if(prob(65) && (!IS_UNCONSCIOUS_OR_CRIT(defender) || !defender.IsParalyzed() || !restraining_mob?.resolve())) var/obj/item/disarmed_item = defender.get_active_held_item() if(disarmed_item && defender.temporarilyRemoveItemFromInventory(disarmed_item)) defender.dropItemToGround(disarmed_item) diff --git a/code/datums/martial/junglearts.dm b/code/datums/martial/junglearts.dm index 475ebe1cc11c..bc963e1eca01 100644 --- a/code/datums/martial/junglearts.dm +++ b/code/datums/martial/junglearts.dm @@ -18,7 +18,7 @@ if(1) atk_verb = "dragged" var/obj/item/organ/tail/tail = attacker.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL) - if(isnull(tail) && defender.stat != CONSCIOUS || defender.IsParalyzed()) + if(isnull(tail) && IS_UNCONSCIOUS_OR_CRIT(defender) || defender.IsParalyzed()) return MARTIAL_ATTACK_INVALID attacker.do_attack_animation(defender, ATTACK_EFFECT_CLAW) diff --git a/code/datums/martial/kaza_ruk.dm b/code/datums/martial/kaza_ruk.dm index 1d1fc00c4455..b264022dfe13 100644 --- a/code/datums/martial/kaza_ruk.dm +++ b/code/datums/martial/kaza_ruk.dm @@ -122,7 +122,7 @@ return FALSE /datum/martial_art/kaza_ruk/proc/low_sweep(mob/living/attacker, mob/living/defender) - if(defender.stat != CONSCIOUS || defender.IsParalyzed()) + if(IS_UNCONSCIOUS_OR_CRIT(defender) || defender.IsParalyzed()) return MARTIAL_ATTACK_INVALID if(HAS_TRAIT(attacker, TRAIT_PACIFISM)) return MARTIAL_ATTACK_INVALID // Does 5 damage, so we can't let pacifists leg sweep. diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 589a6975f209..321005730528 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -163,7 +163,7 @@ var/grab_log_description = "grabbed" attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH) playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1) - if(defender.stat != DEAD && !defender.IsUnconscious() && defender.get_stamina_loss() >= 80) //We put our target to sleep. + if(!IS_UNCONSCIOUS(defender) && defender.get_stamina_loss() >= 80) //We put our target to sleep. defender.visible_message( span_danger("[attacker] carefully pinch a nerve in [defender]'s neck, knocking them out cold!"), span_userdanger("[attacker] pinches something in your neck, and you fall unconscious!"), @@ -437,7 +437,7 @@ if(!isliving(target)) return ..() var/mob/living/carbon/C = target - if(C.stat) + if(IS_UNCONSCIOUS_OR_CRIT(C)) to_chat(user, span_warning("It would be dishonorable to attack a foe while they cannot retaliate.")) return if(LAZYACCESS(modifiers, RIGHT_CLICK)) @@ -458,7 +458,7 @@ H.Paralyze(8 SECONDS) if(H.staminaloss && !H.IsSleeping()) var/total_health = (H.health - H.staminaloss) - if(total_health <= HEALTH_THRESHOLD_CRIT && !H.stat) + if(total_health <= HEALTH_THRESHOLD_CRIT && !IS_UNCONSCIOUS_OR_CRIT(H)) H.visible_message(span_warning("[user] delivers a heavy hit to [H]'s head, knocking [H.p_them()] out cold!"), \ span_userdanger("You're knocked unconscious by [user]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, user) to_chat(user, span_danger("You deliver a heavy hit to [H]'s head, knocking [H.p_them()] out cold!")) diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index 35dc80dab951..5a6cec76d147 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -214,7 +214,7 @@ If you make a derivative work from this code, you must include this notification playsound(attacker.loc, SFX_SWING_HIT, 50, TRUE) var/turf/T = get_edge_target_turf(attacker, attacker.dir) if (T && isturf(T)) - if (!defender.stat) + if (!IS_UNCONSCIOUS_OR_CRIT(defender)) defender.emote("scream") defender.throw_at(T, 10, 4, attacker, TRUE, TRUE, callback = CALLBACK(defender, TYPE_PROC_REF(/mob/living, Paralyze), 20)) log_combat(attacker, defender, "has thrown with wrestling") @@ -314,7 +314,7 @@ If you make a derivative work from this code, you must include this notification span_userdanger("You're [fluff]ed by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker) to_chat(attacker, span_danger("You [fluff] [defender]!")) playsound(attacker.loc, SFX_SWING_HIT, 50, TRUE) - if (!defender.stat) + if (!IS_UNCONSCIOUS_OR_CRIT(defender)) defender.emote("scream") defender.Paralyze(4 SECONDS) @@ -443,7 +443,7 @@ If you make a derivative work from this code, you must include this notification attacker.emote("scream") if (falling == 1) - if (prob(33) || defender.stat) + if (prob(33) || IS_UNCONSCIOUS_OR_CRIT(defender)) EX_ACT(defender, EXPLODE_LIGHT) else defender.adjust_brute_loss(rand(20,30)) diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index 94b36ecbff0b..f6f6ecf3cdac 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -77,7 +77,7 @@ if(isobj(source) && !isstack(source) && (source.material_flags & MATERIAL_AFFECT_STATISTICS)) source.RemoveElement(/datum/element/can_shatter, shard_type, round(mat_amount / SHEET_MATERIAL_AMOUNT * multiplier), SFX_SHATTER) -/// Has no special properties. Could be good against vampires in the future perhaps. +/// Gives weapons an edge against vampires and possibly other mobs with the UNHOLY_BANEABLE trait. /datum/material/silver name = "silver" desc = "A precious metal known for being hated by oversized bats and dogs." @@ -91,7 +91,7 @@ MATERIAL_ELECTRICAL = 9, MATERIAL_THERMAL = 4, MATERIAL_CHEMICAL = 4, - MATERIAL_VAMPIRES_BANE = 8, + MATERIAL_UNHOLY_BANE = 8, ) sheet_type = /obj/item/stack/sheet/mineral/silver ore_type = /obj/item/stack/ore/silver diff --git a/code/datums/materials/properties/optional.dm b/code/datums/materials/properties/optional.dm index 4b5d1aef83b6..72c7c57ba9d6 100644 --- a/code/datums/materials/properties/optional.dm +++ b/code/datums/materials/properties/optional.dm @@ -120,18 +120,18 @@ if (isliving(target)) target.adjust_fire_stacks(source.get_property(id)) -/// Deals additional burn damage to vampires, property value times material volume determines damage -/datum/material_property/vampires_bane - name = "Vampires' Bane" - id = MATERIAL_VAMPIRES_BANE +/// Deals additional burn damage to mobs with the UNHOLY_BANEABLE trait (just vampires right now), property times material volume value determines damage +/datum/material_property/unholy_bane + name = "Unholy Creatures' Bane" + id = MATERIAL_UNHOLY_BANE -/datum/material_property/vampires_bane/get_descriptor(value) - return "vampires' bane" +/datum/material_property/unholy_bane/get_descriptor(value) + return "unholy creatures' bane" -/datum/material_property/vampires_bane/get_tooltip(value) - return "Deals [value * 0.5] to [value * 2] additional burn damage to vampires on contact (based on material volume)" +/datum/material_property/unholy_bane/get_tooltip(value) + return "Deals [value * 0.5] to [value * 2] additional burn damage to unholy creatures such as vampires (based on material volume) on contact" -/datum/material_property/vampires_bane/attach_to(datum/material/material) +/datum/material_property/unholy_bane/attach_to(datum/material/material) . = ..() material.track_flags |= MATERIAL_TRACK_CONTACT | MATERIAL_TRACK_IMPACT var/static/list/interaction_signals = list( @@ -142,10 +142,10 @@ ) RegisterSignals(material, interaction_signals, PROC_REF(on_contact)) -/datum/material_property/vampires_bane/proc/on_contact(datum/material/source, atom/object, mob/living/target, mob/living/user, def_zone, skin_contact) +/datum/material_property/unholy_bane/proc/on_contact(datum/material/source, atom/object, mob/living/target, mob/living/user, def_zone, skin_contact) SIGNAL_HANDLER - if (!isvampire(target)) + if (!HAS_TRAIT(target, TRAIT_UNHOLY_BANEABLE)) return var/burn_damage = source.get_property(id) * clamp((object.custom_materials[source] / (2 * SHEET_MATERIAL_AMOUNT)), 0.5, 2) diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm index 45ba1464e899..493fd7dfa044 100644 --- a/code/datums/mind/_mind.dm +++ b/code/datums/mind/_mind.dm @@ -69,7 +69,10 @@ /// If TRUE, the mob will always be considered "escaped" if they are alive and not exiled. var/force_escaped = FALSE - var/list/learned_recipes //List of learned recipe TYPES. + /// List of crafting recipes learned, references to the singleton datums + VAR_FINAL/list/learned_crafting_recipes + /// List of cooking recipes learned, references to the singleton datums + VAR_FINAL/list/learned_cooking_recipes ///List of skills the user has received a reward for. Should not be used to keep track of currently known skills. Lazy list because it shouldnt be filled often var/list/skills_rewarded diff --git a/code/datums/mood.dm b/code/datums/mood.dm index d9c031151671..aac4e5f92769 100644 --- a/code/datums/mood.dm +++ b/code/datums/mood.dm @@ -386,7 +386,7 @@ if(user != mob_parent) return - if(user.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(user)) return print_mood(user) diff --git a/code/datums/mood_events/death.dm b/code/datums/mood_events/death.dm index 3101b3dea96e..cacfe063d7f8 100644 --- a/code/datums/mood_events/death.dm +++ b/code/datums/mood_events/death.dm @@ -260,7 +260,7 @@ stack_trace("Xeno Host [owner] missing embryo organ despite having XENO_HOST trait. What the fuck?") return - if(owner.stat != CONSCIOUS) // if the carrier is sleeping then presumably the embryo's hivemind isn't affected + if(IS_UNCONSCIOUS_OR_CRIT(owner)) // if the carrier is sleeping then presumably the embryo's hivemind isn't affected return // You feel a lot worse if you're conscious and see a xenomorph die while implanted because the hivemind feels the loss of their sister diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 60f6b09aed12..f952e207ad49 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -106,7 +106,7 @@ timeout = 2 MINUTES /datum/mood_event/table/add_effects() - if(isfelinid(owner)) //Holy snowflake batman! + if(HAS_TRAIT(owner, TRAIT_CATLIKE_INSTINCT)) //Holy snowflake batman! var/mob/living/carbon/human/feline = owner feline.wag_tail(3 SECONDS) description = "They want to play on the table!" @@ -305,7 +305,7 @@ /datum/mood_event/too_slow/add_effects(param) var/people_laughing_at_you = 1 // start with 1 in case they're on the same tile or something for(var/mob/living/carbon/iter_carbon in oview(owner, 7)) - if(iter_carbon.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(iter_carbon)) people_laughing_at_you++ if(people_laughing_at_you > 7) break @@ -526,7 +526,7 @@ /datum/mood_event/bapped/add_effects() // Felinids apparently hate being hit over the head with cardboard - if(isfelinid(owner)) + if(HAS_TRAIT(owner, TRAIT_CATLIKE_INSTINCT)) mood_change = -2 /datum/mood_event/encountered_evil @@ -623,18 +623,12 @@ mood_change = -4 timeout = 4 MINUTES -/datum/mood_event/splattered_with_blood/can_effect_mob(datum/mood/home, mob/living/who, ...) - if(isvampire(who)) - return FALSE - - return ..() - /datum/mood_event/splattered_with_blood/add_effects(...) if(HAS_TRAIT(owner, TRAIT_CULT_HALO)) mood_change = 2 description = "Blood, blood! The Geometer will be pleased." return - if(HAS_TRAIT(owner, TRAIT_MORBID) || HAS_TRAIT(owner, TRAIT_EVIL)) + if(HAS_TRAIT(owner, TRAIT_MORBID) || HAS_TRAIT(owner, TRAIT_EVIL) || HAS_TRAIT(owner, TRAIT_DRINKS_BLOOD)) mood_change = 0 description = "I just got coated in blood. Fascinating!" return @@ -660,3 +654,8 @@ description = "I hear a voice whispering, and I don't like what it says." mood_change = -3 timeout = 30 SECONDS + +/datum/mood_event/cement + description = span_warning("I was forced to eat cement...") + mood_change = -6 + timeout = 4 MINUTES diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index e72a6446bfe1..6027b961679f 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -58,7 +58,7 @@ description = "[friend.name] is way too nice for this station." return - description = "[friend.name] is great to be around, [friend.p_they()] makes me feel so happy!" + description = "[friend.name] is great to be around, [friend.p_they()] make[friend.p_s()] me feel so happy!" /datum/mood_event/warmhug description = "Warm cozy hugs are the best!" @@ -544,7 +544,7 @@ if(!morbid) description = "It felt nice to pet \the [fish]." else - description = "I caress \the [fish] as [fish.p_they()] squirms under my touch, blissfully unaware of how cruel this world is." + description = "I caress \the [fish] as [fish.p_they()] squirm[fish.p_s()] under my touch, blissfully unaware of how cruel this world is." /datum/mood_event/kobun description = "You are all loved by the Universe. I’m not alone, and you aren’t either." diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm index 8398413d07a5..d55098fdfa12 100644 --- a/code/datums/mutations/_mutations.dm +++ b/code/datums/mutations/_mutations.dm @@ -1,7 +1,9 @@ /// Negatives that are virtually harmless and mostly just funny (language) // Set to 0 because munchkinning via miscommunication = bad -#define NEGATIVE_STABILITY_MINI 0 +#define NEGATIVE_STABILITY_NONE 0 +/// Ostensibly negatives but not an active hinderance, at least not to you +#define NEGATIVE_STABILITY_MINI -10 /// Negatives that are slightly annoying (unused) #define NEGATIVE_STABILITY_MINOR -20 /// Negatives that present an uncommon or weak, consistent hindrance to gameplay (cough, paranoia) @@ -117,7 +119,7 @@ return copy /datum/mutation/proc/on_acquiring(mob/living/carbon/human/acquirer) - if(!acquirer || !istype(acquirer) || acquirer.stat == DEAD || (src in acquirer.dna.mutations)) + if(!istype(acquirer) || acquirer.stat == DEAD || (src in acquirer.dna.mutations)) return FALSE if(LAZYLEN(species_allowed) && !LAZYFIND(species_allowed, acquirer.dna.species.id)) return FALSE @@ -128,8 +130,10 @@ for(var/datum/mutation/mewtayshun as anything in acquirer.dna.mutations) //check for conflicting powers if(!(mewtayshun.type in conflicts) && !(type in mewtayshun.conflicts)) continue - to_chat(acquirer, span_warning("You feel your genes resisting something.")) return FALSE + if(!can_acquire(acquirer)) + return FALSE + owner = acquirer dna = acquirer.dna LAZYADD(dna.mutations, src) @@ -146,6 +150,10 @@ owner.add_traits(mutation_traits, GENETIC_MUTATION) return TRUE +/// Checks if the mob can acquire the mutation, returning FALSE prevents it +/datum/mutation/proc/can_acquire(mob/living/carbon/human/acquirer) + return TRUE + /datum/mutation/proc/get_visual_indicator() return @@ -168,7 +176,7 @@ /datum/mutation/proc/on_species_change(datum/source, datum/species/new_species) SIGNAL_HANDLER - if(!LAZYFIND(species_allowed, new_species.id)) + if(!LAZYFIND(species_allowed, new_species.id) || !can_acquire(owner)) dna.remove_mutation(src, src.sources) // shouldn't have this mutation at all /datum/mutation/proc/on_update_overlay(datum/source, list/overlay_list) @@ -192,13 +200,13 @@ modified_power.cooldown_time = initial(modified_power.cooldown_time) * GET_MUTATION_ENERGY(src) return modified_power -/datum/mutation/proc/remove_chromosome() - stabilizer_coeff = initial(stabilizer_coeff) - synchronizer_coeff = initial(synchronizer_coeff) - power_coeff = initial(power_coeff) - energy_coeff = initial(energy_coeff) - can_chromosome = initial(can_chromosome) - chromosome_name = null +/// Called before we apply a chromosome +/datum/mutation/proc/pre_apply_chromosome() + return + +/// Called after we apply a chromosome +/datum/mutation/proc/post_apply_chromosome() + setup() /datum/mutation/proc/grant_power() if(!ispath(power_path) || !owner) diff --git a/code/datums/mutations/active.dm b/code/datums/mutations/active.dm index 6cb5478602eb..f1580acc9d11 100644 --- a/code/datums/mutations/active.dm +++ b/code/datums/mutations/active.dm @@ -1,6 +1,6 @@ /datum/mutation/adrenaline_rush name = "Adrenaline Rush" - desc = "Allows the host to trigger their body's adrenaline response at will." + desc = "The subject gains the ability to trigger their body's adrenaline response at will." quality = POSITIVE text_gain_indication = span_notice("You feel pumped up!") instability = POSITIVE_INSTABILITY_MODERATE diff --git a/code/datums/mutations/adaptation.dm b/code/datums/mutations/adaptation.dm index 2abdd7fa192e..fe11d6da78bf 100644 --- a/code/datums/mutations/adaptation.dm +++ b/code/datums/mutations/adaptation.dm @@ -1,6 +1,6 @@ /datum/mutation/adaptation name = "Adaptation" - desc = "A strange mutation that renders the host immune to damage from extreme temperatures. Does not protect from vacuums." + desc = "The subject gains immunity to damage from extreme temperatures. Does not protect from vacuums." quality = POSITIVE difficulty = 16 text_gain_indication = span_notice("Your body feels normal!") @@ -16,7 +16,7 @@ /datum/mutation/adaptation/cold name = "Cold Adaptation" - desc = "A strange mutation that renders the host immune to damage from low temperature environments. It also prevents the host from slipping on ice." + desc = "The subject gains immunity to damage from low temperature environments, as well as stability when navigating on ice." text_gain_indication = span_notice("Your body feels refreshingly cold.") instability = POSITIVE_INSTABILITY_MODERATE mutation_traits = list(TRAIT_RESISTCOLD, TRAIT_NO_SLIP_ICE) @@ -25,7 +25,7 @@ /datum/mutation/adaptation/heat name = "Heat Adaptation" - desc = "A strange mutation that renders the host immune to damage from high temperature, including being set alight, though the flame itself still burns clothing. It also seems to make the host resist ash storms." + desc = "The subject gains immunity to damage from high temperatures (including fire), as well as resistance to ash storms." text_gain_indication = span_notice("Your body feels invigoratingly warm.") instability = POSITIVE_INSTABILITY_MODERATE mutation_traits = list(TRAIT_RESISTHEAT, TRAIT_ASHSTORM_IMMUNE) @@ -34,7 +34,8 @@ /datum/mutation/adaptation/thermal name = "Thermal Adaptation" - desc = "A strange mutation that renders the host immune to damage from both low and high temperature environments. Does not protect from high or low pressure environments." + desc = "The subject gains immunity to damage from both low and high temperature environments. \ + Does not protect from high or low pressure environments, nor does it provide stability on ice or resistance to ash storms." difficulty = 32 text_gain_indication = span_notice("Your body feels pleasantly room temperature.") instability = POSITIVE_INSTABILITY_MAJOR @@ -44,7 +45,8 @@ /datum/mutation/adaptation/pressure name = "Pressure Adaptation" - desc = "A strange mutation that renders the host immune to damage from both low and high pressure environments. Does not protect from temperature, including the cold of space." + desc = "The subject gains immunity to damage from both low and high pressure environments. \ + Does not protect from temperature, including the cold of space." text_gain_indication = span_notice("Your body feels impressively pressurized.") instability = POSITIVE_INSTABILITY_MODERATE mutation_icon_state = "pressure" diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm index ce494bb37534..78495bbce11d 100644 --- a/code/datums/mutations/antenna.dm +++ b/code/datums/mutations/antenna.dm @@ -1,6 +1,6 @@ /datum/mutation/antenna name = "Antenna" - desc = "The affected person sprouts an antenna. This is known to allow them to access common radio channels passively." + desc = "The subject sprouts an antenna, which is known to allow them to access common radio channels without any external devices." quality = POSITIVE text_gain_indication = span_notice("You feel an antenna sprout from your forehead.") text_lose_indication = span_notice("Your antenna shrinks back down.") @@ -37,7 +37,7 @@ /datum/mutation/mindreader name = "Mind Reader" - desc = "The affected person can look into the recent memories of others." + desc = "The subject can look into the recent memories of others." quality = POSITIVE text_gain_indication = span_notice("You hear distant voices at the corners of your mind.") text_lose_indication = span_notice("The distant voices fade.") @@ -144,7 +144,7 @@ INVOKE_ASYNC(src, PROC_REF(read_mind), examiner, examining) /datum/action/cooldown/spell/pointed/mindread/proc/read_mind(mob/living/examiner, mob/living/examined) - if(examined.stat >= UNCONSCIOUS || isnull(examined.mind) || (examined.mob_biotypes & MOB_ROBOTIC)) + if(IS_UNCONSCIOUS(examined) || isnull(examined.mind) || (examined.mob_biotypes & MOB_ROBOTIC)) return var/antimagic = examined.can_block_magic(antimagic_flags, charge_cost = 0) diff --git a/code/datums/mutations/aoe_moodlet.dm b/code/datums/mutations/aoe_moodlet.dm new file mode 100644 index 000000000000..7293af280518 --- /dev/null +++ b/code/datums/mutations/aoe_moodlet.dm @@ -0,0 +1,126 @@ +/datum/mutation/aoe_moodlet + name = "Extremely Unsightly" + desc = "The subject's body - and especially their face - is unnaturally unsightly, causing a negative reaction from those around them. \ + Wearing a mask will dampen the effect." + quality = NEGATIVE + instability = NEGATIVE_STABILITY_MINI + text_gain_indication = span_warning("You feel pretty in your own way.") + text_lose_indication = span_notice("You feel less self-conscious.") + difficulty = 12 + +/datum/mutation/aoe_moodlet/on_acquiring(mob/living/carbon/human/acquirer) + . = ..() + if(!.) + return + RegisterSignal(acquirer, COMSIG_ATOM_EXAMINE, PROC_REF(on_examined)) + RegisterSignal(acquirer, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + +/datum/mutation/aoe_moodlet/on_losing(mob/living/carbon/human/owner) + . = ..() + if(.) + return + UnregisterSignal(owner, COMSIG_ATOM_EXAMINE) + UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + +/datum/mutation/aoe_moodlet/proc/on_examined(datum/source, mob/examiner, ...) + SIGNAL_HANDLER + + if(examiner == owner || !isliving(examiner) || HAS_TRAIT(owner, TRAIT_FACE_COVERED)) + return + apply_other_moodlet(examiner) + +/datum/mutation/aoe_moodlet/proc/on_moved(datum/source, ...) + SIGNAL_HANDLER + + var/obj/structure/mirror/mirror = locate() in owner.loc + if(isnull(mirror)) + return + apply_own_moodlet() + +/datum/mutation/aoe_moodlet/on_life(seconds_per_tick) + if(HAS_TRAIT(owner, TRAIT_FACE_COVERED)) + return // this is applied by newspapers, and i think it would be funny if newspapers specifically blocked 100% of the effect. + + for(var/mob/living/nearby in viewers(3, owner)) + if(nearby == owner) + continue + apply_other_moodlet(nearby) + +/datum/mutation/aoe_moodlet/proc/apply_own_moodlet() + owner.add_mood_event(type, /datum/mood_event/seen_ugly_mutation/self, owner) + +/datum/mutation/aoe_moodlet/proc/apply_other_moodlet(mob/living/other) + other.add_mood_event(type, get_moodlet(), owner) + +/datum/mutation/aoe_moodlet/proc/get_moodlet() + return (owner.obscured_slots & HIDEFACE) ? /datum/mood_event/seen_ugly_mutation/weak : /datum/mood_event/seen_ugly_mutation + +/datum/mutation/aoe_moodlet/positive + name = "Extremely Comely" + desc = "The subject's body - and especially their face - is unnaturally comely, causing a positive reaction from those around them. \ + Wearing a mask will dampen the effect." + quality = POSITIVE + instability = POSITIVE_INSTABILITY_MINOR + text_gain_indication = span_notice("You feel pretty in your own way.") + text_lose_indication = span_notice("You feel less self-conscious.") + difficulty = 12 + +/datum/mutation/aoe_moodlet/positive/apply_own_moodlet() + owner.add_mood_event(type, /datum/mood_event/seen_pretty_mutation/self, owner) + +/datum/mutation/aoe_moodlet/positive/get_moodlet() + return (owner.obscured_slots & HIDEFACE) ? /datum/mood_event/seen_pretty_mutation/weak : /datum/mood_event/seen_pretty_mutation + +// Negative version +/datum/mood_event/seen_ugly_mutation + description = "Something is seriously wrong with that guy!" + mood_change = -3 + timeout = 2 MINUTES + +/datum/mood_event/seen_ugly_mutation/can_effect_mob(datum/mood/home, mob/living/who, ...) + if(HAS_PERSONALITY(who, /datum/personality/compassionate) || HAS_PERSONALITY(who, /datum/personality/empathetic)) + return FALSE + + return ..() + +/datum/mood_event/seen_ugly_mutation/add_effects(mob/mutant) + description = "Something is seriously wrong with [mutant || "that guy"]!" + +/datum/mood_event/seen_ugly_mutation/weak + description = "Something looks wrong about that guy." + mood_change = -1 + +/datum/mood_event/seen_ugly_mutation/weak/add_effects(mob/mutant) + description = "Something looks wrong about [mutant || "that guy"]." + +/datum/mood_event/seen_ugly_mutation/self + description = "...Is THAT what I look like?!" + mood_change = -2 + timeout = 5 MINUTES + +// Positive version +/datum/mood_event/seen_pretty_mutation + description = "That person looks pretty good - must be a new haircut or something." + mood_change = 3 + timeout = 2 MINUTES + +/datum/mood_event/seen_pretty_mutation/can_effect_mob(datum/mood/home, mob/living/who, ...) + if(HAS_PERSONALITY(who, /datum/personality/misanthropic) || HAS_PERSONALITY(who, /datum/personality/callous)) + return FALSE + + return ..() + +/datum/mood_event/seen_pretty_mutation/add_effects(mob/mutant) + description = "[mutant || "That person"] looks pretty good - must be a new haircut or something." + +/datum/mood_event/seen_pretty_mutation/weak + description = "That person looks alright - it must be the lighting in here." + mood_change = 1 + +/datum/mood_event/seen_pretty_mutation/weak/add_effects(mob/mutant) + description = "[mutant || "That person"] looks alright - it must be the lighting in here." + +/datum/mood_event/seen_pretty_mutation/self + description = "I look pretty good!" + mood_change = 2 + timeout = 5 MINUTES diff --git a/code/datums/mutations/autotomy.dm b/code/datums/mutations/autotomy.dm index da34bfd46606..d6a211c8e4c0 100644 --- a/code/datums/mutations/autotomy.dm +++ b/code/datums/mutations/autotomy.dm @@ -1,6 +1,6 @@ /datum/mutation/self_amputation name = "Autotomy" - desc = "Allows a creature to voluntary discard a random appendage." + desc = "The subject gains the ability to voluntarily discard a random appendage." quality = POSITIVE text_gain_indication = span_notice("Your joints feel loose.") instability = POSITIVE_INSTABILITY_MINOR diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index efdab6a92d2a..e97673e0a72e 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -3,7 +3,7 @@ //Epilepsy gives a very small chance to have a seizure every life tick, knocking you unconscious. /datum/mutation/epilepsy name = "Epilepsy" - desc = "A genetic defect that sporadically causes seizures." + desc = "The subject sporadically suffers from seizures." instability = NEGATIVE_STABILITY_MODERATE quality = NEGATIVE text_gain_indication = span_danger("You get a headache.") @@ -15,7 +15,7 @@ trigger_seizure() /datum/mutation/epilepsy/proc/trigger_seizure() - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return owner.visible_message(span_danger("[owner] starts having a seizure!"), span_userdanger("You have a seizure!")) owner.Unconscious(200 * GET_MUTATION_POWER(src)) @@ -51,7 +51,7 @@ //Unstable DNA induces random mutations! /datum/mutation/bad_dna name = "Unstable DNA" - desc = "Strange mutation that causes the holder to randomly mutate." + desc = "The subject will randomly mutate other mutations or features." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_danger("You feel strange.") @@ -82,7 +82,7 @@ //Cough gives you a chronic cough that causes you to drop items. /datum/mutation/cough name = "Cough" - desc = "A chronic cough." + desc = "The subject has a chronic cough." instability = NEGATIVE_STABILITY_MODERATE quality = MINOR_NEGATIVE text_gain_indication = span_danger("You start coughing.") @@ -90,7 +90,7 @@ power_coeff = 1 /datum/mutation/cough/on_life(seconds_per_tick) - if(SPT_PROB(2.5 * GET_MUTATION_SYNCHRONIZER(src), seconds_per_tick) && owner.stat == CONSCIOUS) + if(SPT_PROB(2.5 * GET_MUTATION_SYNCHRONIZER(src), seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(owner)) owner.drop_all_held_items() owner.emote("cough") if(GET_MUTATION_POWER(src) > 1) @@ -100,14 +100,14 @@ /datum/mutation/paranoia name = "Paranoia" - desc = "Subject is easily terrified, and may suffer from hallucinations." + desc = "The subject is easily terrified, and may suffer from hallucinations." instability = NEGATIVE_STABILITY_MODERATE quality = NEGATIVE text_gain_indication = span_danger("You feel screams echo through your mind...") text_lose_indication = span_notice("The screaming in your mind fades.") /datum/mutation/paranoia/on_life(seconds_per_tick) - if(SPT_PROB(2.5, seconds_per_tick) && owner.stat == CONSCIOUS) + if(SPT_PROB(2.5, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(owner)) owner.emote("scream") if(prob(25)) owner.adjust_hallucinations(40 SECONDS) @@ -115,7 +115,7 @@ //Dwarfism shrinks your body and lets you pass tables. /datum/mutation/dwarfism name = "Dwarfism" - desc = "A mutation believed to be the cause of dwarfism." + desc = "The subject's cells are more compact, making the subject appear smaller." quality = POSITIVE difficulty = 16 instability = POSITIVE_INSTABILITY_MINOR @@ -137,7 +137,7 @@ /datum/mutation/acromegaly name = "Acromegaly" - desc = "A mutation believed to be the cause of acromegaly, or 'being unusually tall'." + desc = "The subject's cells stack on top of one another, making the subject appear unusually tall." quality = MINOR_NEGATIVE difficulty = 16 instability = NEGATIVE_STABILITY_MODERATE @@ -176,7 +176,7 @@ /datum/mutation/gigantism name = "Gigantism" //negative version of dwarfism - desc = "The cells within the subject spread out to cover more area, making the subject appear larger." + desc = "The subject's cells are more spread out, making the subject appear larger." quality = MINOR_NEGATIVE difficulty = 12 conflicts = list(/datum/mutation/dwarfism) @@ -199,7 +199,7 @@ //Clumsiness has a very large amount of small drawbacks depending on item. /datum/mutation/clumsy name = "Clumsiness" - desc = "A genome that inhibits certain brain functions, causing the holder to appear clumsy. Honk!" + desc = "The subject's brain functions are impaired, causing them to exhibit clown-like behavior." instability = NEGATIVE_STABILITY_MAJOR quality = MINOR_NEGATIVE text_gain_indication = span_danger("You feel lightheaded.") @@ -208,14 +208,14 @@ //Tourettes causes you to randomly stand in place and shout. /datum/mutation/tourettes name = "Tourette's Syndrome" - desc = "A chronic twitch that forces the user to scream bad words." //definitely needs rewriting + desc = "The subject has a chronic twitching disorder, causing them to involuntarily shout or twitch." quality = NEGATIVE - instability = 0 + instability = NEGATIVE_STABILITY_NONE text_gain_indication = span_danger("You twitch.") synchronizer_coeff = 1 /datum/mutation/tourettes/on_life(seconds_per_tick) - if(SPT_PROB(5 * GET_MUTATION_SYNCHRONIZER(src), seconds_per_tick) && owner.stat == CONSCIOUS && !owner.IsStun()) + if(SPT_PROB(5 * GET_MUTATION_SYNCHRONIZER(src), seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(owner) && !owner.IsStun()) switch(rand(1, 3)) if(1) owner.emote("twitch") @@ -230,7 +230,7 @@ //Deafness makes you deaf. /datum/mutation/deaf name = "Deafness" - desc = "The holder of this genome is completely deaf." + desc = "The subject is completely deaf and cannot hear anything." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_danger("You can't seem to hear anything.") @@ -274,7 +274,7 @@ /datum/mutation/glow name = "Glowy" - desc = "You permanently emit a light with a random color and intensity." + desc = "The subject's skin emits a soft glow, illuminating the area around them." quality = POSITIVE text_gain_indication = span_notice("Your skin begins to glow softly.") instability = POSITIVE_INSTABILITY_MINI @@ -311,7 +311,7 @@ /datum/mutation/glow/anti name = "Anti-Glow" - desc = "Your skin seems to attract and absorb nearby light creating 'darkness' around you." + desc = "The subject's skin absorbs light, making the area around them darker." text_gain_indication = span_notice("The light around you seems to disappear.") conflicts = list(/datum/mutation/glow) instability = POSITIVE_INSTABILITY_MINOR @@ -323,7 +323,7 @@ /datum/mutation/strong name = "Strength" - desc = "The user's muscles slightly expand. Commonly seen in top-ranking boxers." + desc = "The subject's muscles slightly expand, improving general strength and workout efficiency." quality = POSITIVE text_gain_indication = span_notice("You feel strong.") instability = POSITIVE_INSTABILITY_MINOR @@ -332,7 +332,7 @@ /datum/mutation/stimmed name = "Stimmed" - desc = "The user's chemical balance is more robust. This mutation is known to slightly improve workout efficiency." + desc = "The subject's chemical balance is more robust, improving workout efficiency." quality = POSITIVE instability = POSITIVE_INSTABILITY_MINI text_gain_indication = span_notice("You feel stimmed.") @@ -341,7 +341,7 @@ /datum/mutation/insulated name = "Insulated" - desc = "The affected person does not conduct electricity." + desc = "The subject does not conduct electricity." quality = POSITIVE text_gain_indication = span_notice("Your fingertips go numb.") text_lose_indication = span_notice("Your fingertips regain feeling.") @@ -351,7 +351,7 @@ /datum/mutation/fire name = "Fiery Sweat" - desc = "The user's skin will randomly combust, but is generally a lot more resilient to burning." + desc = "The subject's skin will randomly combust, though ultimately becomes more resilient to burning." quality = NEGATIVE text_gain_indication = span_warning("You feel hot.") text_lose_indication = span_notice("You feel a lot cooler.") @@ -378,7 +378,7 @@ /datum/mutation/badblink name = "Spatial Instability" - desc = "The victim of the mutation has a very weak link to spatial reality, and may be displaced. Often causes extreme nausea." + desc = "The subject has a very weak link to spatial reality, and may be displaced. Often causes extreme nausea." quality = NEGATIVE text_gain_indication = span_warning("The space around you twists sickeningly.") text_lose_indication = span_notice("The space around you settles back to normal.") @@ -408,7 +408,7 @@ /datum/mutation/acidflesh name = "Acidic Flesh" - desc = "Subject has acidic chemicals building up underneath the skin. This is often lethal." + desc = "The subject has acidic chemicals building up underneath the skin. This is often lethal." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_userdanger("A horrible burning sensation envelops you as your flesh turns to acid!") @@ -429,7 +429,7 @@ /datum/mutation/spastic name = "Spastic" - desc = "Subject suffers from muscle spasms." + desc = "The subject suffers from muscle spasms." instability = NEGATIVE_STABILITY_MODERATE quality = NEGATIVE text_gain_indication = span_warning("You flinch.") @@ -449,7 +449,7 @@ /datum/mutation/extrastun name = "Two Left Feet" - desc = "A mutation that replaces the right foot with another left foot. Symptoms include kissing the floor when taking a step." + desc = "The subject's right foot is replaced with another left foot. Symptoms include kissing the floor when taking a step." instability = NEGATIVE_STABILITY_MODERATE quality = NEGATIVE text_gain_indication = span_warning("Your right foot feels... left.") @@ -481,7 +481,8 @@ /datum/mutation/martyrdom name = "Internal Martyrdom" - desc = "A mutation that makes the body destruct when near death. Not damaging, but very, VERY disorienting." + desc = "The subject violently tears itself apart when near death. The process is irreversible. \ + The effect is not known to damage anything nearby, but is very, VERY disorienting when observed." instability = NEGATIVE_STABILITY_MAJOR // free stability >:) locked = TRUE quality = POSITIVE //not that cloning will be an option a lot but generally lets keep this around i guess? @@ -503,7 +504,7 @@ /datum/mutation/martyrdom/proc/bloody_shower(datum/source, new_stat) SIGNAL_HANDLER - if(new_stat != HARD_CRIT) + if(new_stat < HARD_CRIT) return var/list/organs = owner.get_organs_for_zone(BODY_ZONE_HEAD, TRUE) @@ -529,7 +530,8 @@ /datum/mutation/headless name = "H.A.R.S." - desc = "A mutation that makes the body reject the head, the brain receding into the chest. Stands for Head Allergic Rejection Syndrome. Warning: Removing this mutation is very dangerous, though it will regenerate non-vital head organs." + desc = "Short for \"Head Allergic Rejection Syndrome\", the subject's body rejects the head, causing its brain to recede into the chest. \ + Reversing this mutation is very dangerous, though it will regenerate non-vital head organs." instability = NEGATIVE_STABILITY_MAJOR difficulty = 12 //pretty good for traitors quality = NEGATIVE //holy shit no eyes or tongue or ears @@ -572,7 +574,8 @@ brain.zone = initial(brain.zone) brain.Insert(owner, special = TRUE, movement_flags = NO_ID_TRANSFER) - owner.dna.species.regenerate_organs(owner, replace_current = FALSE, excluded_zones = list(BODY_ZONE_CHEST)) //replace_current needs to be FALSE to prevent weird adding and removing mutation healing + var/list/excluded_zones = GLOB.all_body_zones - BODY_ZONE_HEAD + owner.dna.species.regenerate_organs(owner, replace_current = FALSE, excluded_zones = excluded_zones) //replace_current needs to be FALSE to prevent weird adding and removing mutation healing owner.apply_damage(damage = 50, damagetype = BRUTE, def_zone = BODY_ZONE_HEAD) //and this to DISCOURAGE organ farming, or at least not make it free. owner.visible_message(span_warning("[owner]'s head returns with a sickening crunch!"), span_warning("Your head regrows with a sickening crack! Ouch.")) new /obj/effect/gibspawner/generic(get_turf(owner), owner) @@ -586,7 +589,7 @@ // You bleed faster but regenerate blood faster /datum/mutation/bloodier name = "Hypermetabolic Blood" - desc = "The subject's blood is hypermetabolic, causing it to be produced at a much faster rate." + desc = "The subject's becomes hypermetabolic, causing it to produce blood at a much faster rate." quality = POSITIVE instability = POSITIVE_INSTABILITY_MINOR text_gain_indication = span_notice("You can feel your heartbeat pick up.") @@ -659,18 +662,10 @@ conflicts = list(/datum/mutation/rock_eater) locked = TRUE -/datum/mutation/rock_absorber/on_losing(mob/living/carbon/human/owner) - . = ..() - if(. || QDELING(owner) || HAS_TRAIT(owner, TRAIT_ROCK_METAMORPHIC)) - return - owner.remove_status_effect(/datum/status_effect/golem) - owner.remove_status_effect(/datum/status_effect/golem_lightbulb) - // Soft crit is disabed /datum/mutation/inexorable name = "Inexorable" - desc = "Your body can push on beyond the limits of normal human endurance. \ - However, pushing it too far can cause severe damage to your body." + desc = "The subject's body can push on beyond the limits of normal human endurance, though the process causes internal damage to the body." quality = POSITIVE instability = POSITIVE_INSTABILITY_MODERATE text_gain_indication = span_notice("You feel inexorable.") @@ -691,17 +686,17 @@ if(.) return UnregisterSignal(owner, COMSIG_LIVING_HEALTH_UPDATE) - REMOVE_TRAIT(owner, TRAIT_SOFTSPOKEN, REF(src)) + REMOVE_TRAIT(owner, TRAIT_FORCE_WHISPER, REF(src)) /datum/mutation/inexorable/proc/check_health(...) SIGNAL_HANDLER - if(owner.health > owner.crit_threshold || owner.stat != CONSCIOUS) - REMOVE_TRAIT(owner, TRAIT_SOFTSPOKEN, REF(src)) + if(owner.health > owner.crit_threshold || IS_UNCONSCIOUS_OR_CRIT(owner)) + REMOVE_TRAIT(owner, TRAIT_FORCE_WHISPER, REF(src)) else - ADD_TRAIT(owner, TRAIT_SOFTSPOKEN, REF(src)) + ADD_TRAIT(owner, TRAIT_FORCE_WHISPER, REF(src)) /datum/mutation/inexorable/on_life(seconds_per_tick) - if(owner.health > owner.crit_threshold || owner.stat != CONSCIOUS || HAS_TRAIT(owner, TRAIT_STASIS)) + if(owner.health > owner.crit_threshold || IS_UNCONSCIOUS_OR_CRIT(owner) || HAS_TRAIT(owner, TRAIT_STASIS)) return if(HAS_TRAIT(owner, TRAIT_NOCRITDAMAGE) && owner.health <= owner.hardcrit_threshold + 10) return @@ -713,3 +708,215 @@ owner.adjust_brute_loss(0.5 * seconds_per_tick * GET_MUTATION_SYNCHRONIZER(src), forced = TRUE) // Offsets suffocation but not entirely owner.adjust_oxy_loss(-0.5 * seconds_per_tick, forced = TRUE) + +/datum/mutation/limb_regeneration + name = "Regeneration" + desc = "The subject's body is able to regenerate lost limbs or organs while sleeping, given ample rest and nutrients." + quality = POSITIVE + instability = POSITIVE_INSTABILITY_MODERATE + text_gain_indication = span_notice("You feel a strange tingling.") + text_lose_indication = span_notice("The strange tingling feeling fades.") + difficulty = 20 + synchronizer_coeff = 1 + power_coeff = 1 + + /// Threshold of nutrition required to regenerate limbs/organs + var/nutrition_threshold = NUTRITION_LEVEL_FED * 0.85 + /// If TRUE we notified the user they could probably use the mutation if they eat/slept first - comes with a bonus to trigger chance + VAR_PRIVATE/notified_of_ability = FALSE + +/datum/mutation/limb_regeneration/can_acquire(mob/living/carbon/human/acquirer) + return !HAS_TRAIT(acquirer, TRAIT_NOHUNGER) + +/datum/mutation/limb_regeneration/on_life(seconds_per_tick) + if(owner.stat >= HARD_CRIT) + return + if(!SPT_PROB(5 * (notified_of_ability ? 4 : 1) * (GET_MUTATION_POWER(src) ** 2), seconds_per_tick)) + return + + var/list/missing_limbs = owner.get_missing_limbs() + var/list/missing_important_organs = owner.get_missing_organs(include_appendix = (GET_MUTATION_POWER(src) > 1)) // appendix can regrow if you have power + var/list/missing_special_organs = list() + for(var/organ_type in owner.dna?.species?.mutant_organs) + if(!owner.get_organ_by_type(organ_type) && should_visual_organ_apply_to(organ_type, owner)) + missing_special_organs += organ_type + + if(!length(missing_limbs) && !length(missing_important_organs) && !length(missing_special_organs)) + return + + if(owner.nutrition <= nutrition_threshold) + if(IS_UNCONSCIOUS(owner) && !notified_of_ability) + to_chat(owner, span_green("You feel a strange tingling, as if your body is trying to do something - though you feel like you could use a meal first.")) + notified_of_ability = TRUE + return + if(!IS_UNCONSCIOUS(owner)) + if(owner.nutrition > nutrition_threshold && !notified_of_ability) + to_chat(owner, span_green("You feel a strange tingling, as if your body is trying to do something - though you feel like you could use a nap first.")) + notified_of_ability = TRUE + return + + notified_of_ability = FALSE + // "core organs" and limbs are prioritized + if(length(missing_important_organs) || length(missing_limbs)) + if(length(missing_important_organs) && (prob(50) || !length(missing_limbs))) + var/replacement_type = owner.dna.species.get_mutant_organ_type_for_slot(pick(missing_important_organs)) + var/obj/item/organ/replacement = new replacement_type() + replacement.Insert(owner, special = TRUE) + to_chat(owner, span_green("The tingingling feeling builds to a climax, until ultimately you feel a new [replacement] where your old one was!")) + else + var/replacing_zone = pick(missing_limbs) + owner.regenerate_limb(replacing_zone) + var/obj/item/bodypart/replacement = owner.get_bodypart(replacing_zone) + to_chat(owner, span_green("The tingling feeling builds to a climax, until ultimately you feel a new [replacement.plaintext_zone] where your old one was!")) + owner.visible_message(span_warning("[owner]'s [replacement.plaintext_zone] reforms, making a loud, grotesque sound!"), ignored_mobs = list(owner)) + owner.adjust_nutrition(-NUTRITION_LEVEL_FULL * 0.5 * GET_MUTATION_SYNCHRONIZER(src)) + playsound(owner, 'sound/effects/magic/demon_consume.ogg', 33, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return + + // "cosmetic" organs are second priority and cheaper to regenerate + if(length(missing_special_organs)) + var/replacement_type = pick(missing_special_organs) + var/obj/item/organ/replacement = new replacement_type() + replacement.Insert(owner, special = TRUE) + to_chat(owner, span_green("The tingling feeling builds to a climax, until ultimately you feel a new [replacement] where your old one was!")) + if(replacement.organ_flags & ORGAN_EXTERNAL) + owner.visible_message(span_warning("[owner]'s [replacement] reforms, making a loud, grotesque sound!"), ignored_mobs = list(owner)) + owner.adjust_nutrition(-NUTRITION_LEVEL_FULL * 0.3 * GET_MUTATION_SYNCHRONIZER(src)) + playsound(owner, 'sound/effects/magic/demon_consume.ogg', 33, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return + + stack_trace("Limb regeneration failed to find a missing limb or organ to regenerate for [owner] despite passing the checks") + +/datum/mutation/chemical_allergy + name = "Chemical Allergy" + desc = "The subject's body is allergic to almost all forms of medicine or drug - causing a temporary flux of genetic instability, among other side effects." + quality = NEGATIVE + instability = NEGATIVE_STABILITY_MAJOR + text_gain_indication = span_warning("You feel a strange irritation in your skin.") + text_lose_indication = span_notice("The irritation in your skin subsides.") + difficulty = 20 + + /// Typecache of reagents that trigger the allergy + var/static/list/danger_reagents + /// If we're currently experiencing negative effects + VAR_PRIVATE/was_experiencing_allergy = FALSE + +/datum/mutation/chemical_allergy/New() + . = ..() + if(length(danger_reagents)) + return + + danger_reagents = typecacheof(list( + /datum/reagent/medicine, + /datum/reagent/drug, + )) - typecacheof(list( + /datum/reagent/medicine/mannitol, + /datum/reagent/medicine/mutadone, + )) - GLOB.allergy_reagent_blacklist + +/datum/mutation/chemical_allergy/on_acquiring(mob/living/carbon/human/acquirer) + . = ..() + if(!.) + return + RegisterSignal(acquirer, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan)) + +/datum/mutation/chemical_allergy/on_losing(mob/living/carbon/human/owner) + . = ..() + if(.) + return + UnregisterSignal(owner, COMSIG_LIVING_HEALTHSCAN) + +/datum/mutation/chemical_allergy/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat) + SIGNAL_HANDLER + + render_list += "" + render_list += conditional_tooltip("Subject has [name]", "Subject will react negatively to most forms of medicine - \ + avoid administering chemicals as a part of treatment unless absolutely necessary.", tochat) + render_list += "
" + +/datum/mutation/chemical_allergy/on_life(seconds_per_tick) + if(!has_danger_reagent()) + if(was_experiencing_allergy) + was_experiencing_allergy = FALSE + instability *= 2 + owner.dna.update_instability() + return + if(!SPT_PROB(80, seconds_per_tick)) + return + if(!was_experiencing_allergy) + was_experiencing_allergy = TRUE + instability *= 0.5 // halves the negative instability it rewards, which could push you into danger territory! + owner.dna.update_instability() + + if(SPT_PROB(66, seconds_per_tick)) + owner.set_stutter_if_lower(4 SECONDS) + if(SPT_PROB(33, seconds_per_tick)) + owner.adjust_disgust(24, DISGUST_LEVEL_VERYDISGUSTED) + owner.adjust_tox_loss(pick(2, 3, 4), forced = TRUE) + if(SPT_PROB(12, seconds_per_tick)) + owner.adjust_jitter_up_to(6 SECONDS, 36 SECONDS) + owner.adjust_dizzy_up_to(6 SECONDS, 36 SECONDS) + owner.adjust_blood_volume(-pick(12, 16, 20, 24), minimum = BLOOD_VOLUME_OKAY) + if(SPT_PROB(6, seconds_per_tick)) + owner.adjust_confusion_up_to(4 SECONDS, 12 SECONDS) + owner.losebreath = max(owner.losebreath, 4) + +/datum/mutation/chemical_allergy/proc/has_danger_reagent() + for(var/datum/reagent/reagent as anything in owner.reagents?.reagent_list) + if(!is_type_in_typecache(reagent, danger_reagents)) + continue + if(!reagent.metabolizing || reagent.volume < 1) + continue + return TRUE + + return FALSE + +/datum/mutation/venomous_strikes + name = "Venomous" + desc = "The subject's body produces a minor toxin that can be injected into others through scratches or bites. \ + They will also filter out the same variety of toxin from their own bloodstream should they be exposed to it themselves." + quality = POSITIVE + instability = POSITIVE_INSTABILITY_MODERATE + text_gain_indication = span_notice("You teeth and nails feel sharper.") + text_lose_indication = span_notice("You teeth and nails feel duller.") + /// Type of chem to inject + var/venom_type = /datum/reagent/toxin + /// Amount of chem to inject per attack + var/venom_amount = 3 + +/datum/mutation/venomous_strikes/on_acquiring(mob/living/carbon/human/owner) + text_gain_indication = (owner.get_active_hand()?.unarmed_attack_effect == ATTACK_EFFECT_CLAW) ? span_notice("You teeth and claws feel sharper.") : initial(text_gain_indication) + . = ..() + if(!.) + return + apply_venom() + +/datum/mutation/venomous_strikes/on_losing(mob/living/carbon/human/owner) + text_lose_indication = (owner.get_active_hand()?.unarmed_attack_effect == ATTACK_EFFECT_CLAW) ? span_notice("You teeth and claws feel duller.") : initial(text_lose_indication) + . = ..() + if(.) + return + remove_venom() + +/datum/mutation/venomous_strikes/vv_edit_var(var_name, var_value) + if(var_name != NAMEOF(src, venom_type) && var_name != NAMEOF(src, venom_amount)) + return ..() + + remove_venom() + . = ..() + apply_venom() + +/datum/mutation/venomous_strikes/pre_apply_chromosome() + apply_venom() + +/datum/mutation/venomous_strikes/post_apply_chromosome() + remove_venom() + +/datum/mutation/venomous_strikes/proc/apply_venom() + owner.AddElement(/datum/element/venomous, venom_type, venom_amount * GET_MUTATION_POWER(src), INJECT_CHECK_IGNORE_SPECIES) + +/datum/mutation/venomous_strikes/proc/remove_venom() + owner.RemoveElement(/datum/element/venomous, venom_type, venom_amount * GET_MUTATION_POWER(src), INJECT_CHECK_IGNORE_SPECIES) + +/datum/mutation/venomous_strikes/on_life(seconds_per_tick) + owner.reagents.remove_reagent(venom_type, round(venom_amount * 0.33, CHEMICAL_VOLUME_ROUNDING) * seconds_per_tick) diff --git a/code/datums/mutations/chameleon.dm b/code/datums/mutations/chameleon.dm index 660e672c682f..330f2f006e29 100644 --- a/code/datums/mutations/chameleon.dm +++ b/code/datums/mutations/chameleon.dm @@ -1,7 +1,7 @@ //Chameleon causes the owner to slowly become transparent when not moving. /datum/mutation/chameleon name = "Chameleon" - desc = "A genome that causes the holder's skin to become transparent over time." + desc = "The subject's skin becomes transparent over time while not moving." quality = POSITIVE difficulty = 16 text_gain_indication = span_notice("You feel one with your surroundings.") @@ -22,7 +22,7 @@ //Upgraded mutation of the base variant, used for changelings. No instability and better power_coeff /datum/mutation/chameleon/changeling - instability = 0 + instability = NEGATIVE_STABILITY_NONE power_coeff = 2.5 locked = TRUE diff --git a/code/datums/mutations/cold.dm b/code/datums/mutations/cold.dm index 340f5e9e10b2..39a08a0f8487 100644 --- a/code/datums/mutations/cold.dm +++ b/code/datums/mutations/cold.dm @@ -1,6 +1,6 @@ /datum/mutation/geladikinesis name = "Geladikinesis" - desc = "Allows the user to concentrate moisture and sub-zero forces into snow." + desc = "The subject can solidify moisture in the air into snow at will." quality = POSITIVE text_gain_indication = span_notice("Your hand feels cold.") instability = POSITIVE_INSTABILITY_MINOR @@ -22,7 +22,7 @@ /datum/mutation/cryokinesis name = "Cryokinesis" - desc = "Draws negative energy from the sub-zero void to freeze surrounding temperatures at subject's will." + desc = "The subject can draw negative energy from the void to fire a bolt of freezing energy at will." quality = POSITIVE //upsides and downsides text_gain_indication = span_notice("Your hand feels cold.") instability = POSITIVE_INSTABILITY_MODERATE diff --git a/code/datums/mutations/fire_breath.dm b/code/datums/mutations/fire_breath.dm index 2e8ee669e07c..b14c4cd75d3e 100644 --- a/code/datums/mutations/fire_breath.dm +++ b/code/datums/mutations/fire_breath.dm @@ -1,6 +1,6 @@ /datum/mutation/firebreath name = "Fire Breath" - desc = "An ancient mutation that gives lizards breath of fire." + desc = "The subject can breathe fire at will." quality = POSITIVE difficulty = 12 locked = TRUE diff --git a/code/datums/mutations/hot.dm b/code/datums/mutations/hot.dm index 38e88d6b5741..b93b9d6bc81b 100644 --- a/code/datums/mutations/hot.dm +++ b/code/datums/mutations/hot.dm @@ -1,6 +1,6 @@ /datum/mutation/cindikinesis name = "Cindikinesis" - desc = "Allows the user to concentrate nearby heat into a pile of ash. Wow. Very interesting." + desc = "The subject can draw heat out of the air to produce a pile of ash at will." quality = POSITIVE text_gain_indication = span_notice("Your hand feels warm.") instability = POSITIVE_INSTABILITY_MINOR @@ -23,7 +23,7 @@ /datum/mutation/pyrokinesis name = "Pyrokinesis" - desc = "Draws positive energy from the surroundings to heat surrounding temperatures at subject's will." + desc = "The subject can draw positive energy from the environment to fire a bolt of hot energy at will." quality = POSITIVE text_gain_indication = span_notice("Your hand feels hot!") instability = POSITIVE_INSTABILITY_MODERATE diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm index 6fd350263093..94d854697fdb 100644 --- a/code/datums/mutations/hulk.dm +++ b/code/datums/mutations/hulk.dm @@ -1,7 +1,8 @@ //Hulk turns your skin green, makes you strong, and allows you to shrug off stun effect. /datum/mutation/hulk name = "Hulk" - desc = "A poorly understood genome that causes the holder's muscles to expand, inhibit speech and gives the person a bad skin condition." + desc = "The subject's muscles expand drastically, granting superhuman strength and resilience, but inhibit speech in the process. \ + This heightened muscle density is more vulnerable to the cold, and cannot be maintained if critically injured." quality = POSITIVE locked = TRUE difficulty = 16 @@ -242,7 +243,7 @@ var/turf/T = get_edge_target_turf(the_hulk, the_hulk.dir) if(!isturf(T)) return - if(!yeeted_person.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(yeeted_person)) yeeted_person.emote("scream") yeeted_person.throw_at(T, 10, 6, the_hulk, TRUE, TRUE) log_combat(the_hulk, yeeted_person, "has thrown by tail") @@ -251,7 +252,7 @@ name = "Hulk (Magical)" species_allowed = null //yes skeleton/lizard hulk - note that species that dont have skintone changing (like skellies) get custom handling health_req = 0 - instability = 0 + instability = NEGATIVE_STABILITY_NONE scream_delay = 2.5 SECONDS // halved to be more annoying (spell doesn't last long anyways) no_recoil = FALSE mutation_traits = list( @@ -263,7 +264,7 @@ /datum/mutation/hulk/superhuman name = "Hulk (Super)" health_req = 0 - instability = 0 + instability = NEGATIVE_STABILITY_NONE no_recoil = FALSE mutation_traits = list( TRAIT_CHUNKYFINGERS, @@ -281,7 +282,7 @@ /datum/mutation/hulk/ork name = "Ork" - desc = "A mutation caused by a mixup of hulk genes which severely impacts speech centers in owners' brains." + desc = "A variant of the hulk mutation that is also known to inhibit the subject's brain functions." text_gain_indication = span_notice("You feel significantly dumber!") bodypart_color = COLOR_ASSISTANT_OLIVE conflicts = list(/datum/mutation/hulk) diff --git a/code/datums/mutations/olfaction.dm b/code/datums/mutations/olfaction.dm index fda296fe0e13..b01052608a80 100644 --- a/code/datums/mutations/olfaction.dm +++ b/code/datums/mutations/olfaction.dm @@ -1,6 +1,6 @@ /datum/mutation/olfaction name = "Transcendent Olfaction" - desc = "Your sense of smell is comparable to that of a canine." + desc = "The subject's sense of smell is comparable to that of a canine." quality = POSITIVE difficulty = 12 text_gain_indication = span_notice("Smells begin to make more sense...") diff --git a/code/datums/mutations/passive.dm b/code/datums/mutations/passive.dm index c5ab54f66f3c..0ec5626280aa 100644 --- a/code/datums/mutations/passive.dm +++ b/code/datums/mutations/passive.dm @@ -1,6 +1,6 @@ /datum/mutation/biotechcompat name = "Biotech Compatibility" - desc = "Subject is more compatibile with biotechnology such as skillchips." + desc = "The subject is compatible with biotechnology such as skillchips." quality = POSITIVE instability = POSITIVE_INSTABILITY_MINI @@ -16,7 +16,7 @@ /datum/mutation/clever name = "Clever" - desc = "Causes the subject to feel just a little bit smarter. Most effective in specimens with low levels of intelligence." + desc = "The subject's intelligence level is raised to that of the average assistant." quality = POSITIVE instability = POSITIVE_INSTABILITY_MODERATE // literally makes you on par with station equipment text_gain_indication = span_danger("You feel a little bit smarter.") diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm index f784e7ce39c3..64e932bad09d 100644 --- a/code/datums/mutations/radioactive.dm +++ b/code/datums/mutations/radioactive.dm @@ -1,6 +1,6 @@ /datum/mutation/radioactive name = "Radioactivity" - desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings." + desc = "The subject emits deadly beta radiation, affecting both the host and their surroundings." quality = NEGATIVE text_gain_indication = span_warning("You can feel it in your bones!") instability = NEGATIVE_STABILITY_MAJOR diff --git a/code/datums/mutations/reach.dm b/code/datums/mutations/reach.dm index 2c93ec8e92c8..fbe2217f401a 100644 --- a/code/datums/mutations/reach.dm +++ b/code/datums/mutations/reach.dm @@ -1,7 +1,7 @@ ///Telekinesis lets you interact with objects from range, and gives you a light blue halo around your head. /datum/mutation/telekinesis name = "Telekinesis" - desc = "A strange mutation that allows the holder to interact with objects through thought." + desc = "The subject gains the ability to interact with objects through thought." quality = POSITIVE difficulty = 18 text_gain_indication = span_notice("You feel smarter!") @@ -34,7 +34,8 @@ /datum/mutation/elastic_arms name = "Elastic Arms" - desc = "Subject's arms have become elastic, allowing them to stretch up to a meter away. However, this elasticity makes it difficult to wear gloves, handle complex tasks, or grab large objects." + desc = "The subject's arms become elastic, allowing them to stretch up to a meter away. \ + However, this elasticity makes it difficult to wear gloves, handle complex tasks, or grab large objects." quality = POSITIVE instability = POSITIVE_INSTABILITY_MAJOR text_gain_indication = span_warning("You feel armstrong!") diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm index 26f85ed0d2ca..30ccf3575c97 100644 --- a/code/datums/mutations/sight.dm +++ b/code/datums/mutations/sight.dm @@ -1,7 +1,7 @@ //Nearsightedness restricts your vision by several tiles. /datum/mutation/nearsight name = "Near Sightness" - desc = "The holder of this mutation has poor eyesight." + desc = "The subject has poor eyesight, and cannot see distant objects." instability = NEGATIVE_STABILITY_MODERATE quality = MINOR_NEGATIVE text_gain_indication = span_danger("You can't see very well.") @@ -20,7 +20,7 @@ ///Blind makes you blind. Who knew? /datum/mutation/blind name = "Blindness" - desc = "Renders the subject completely blind." + desc = "The subject is completely blind and cannot see anything." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_danger("You can't seem to see anything.") @@ -39,7 +39,8 @@ ///Thermal Vision lets you see mobs through walls /datum/mutation/thermal name = "Thermal Vision" - desc = "The user of this genome can visually perceive the unique human thermal signature." + desc = "The subject gains the ability to temporarily focus their eyes, allowing them to perceive human thermal signatures. \ + However, the process is known to cause eye damage on repeat use." quality = POSITIVE difficulty = 18 text_gain_indication = span_notice("You can see the heat rising off of your skin...") @@ -55,9 +56,7 @@ return // Something went wront and we still have the thermal vision from our power, no cheating. - if(HAS_TRAIT_FROM(owner, TRAIT_THERMAL_VISION, GENETIC_MUTATION)) - REMOVE_TRAIT(owner, TRAIT_THERMAL_VISION, GENETIC_MUTATION) - owner.update_sight() + REMOVE_TRAIT(owner, TRAIT_THERMAL_VISION, GENETIC_MUTATION) /datum/mutation/thermal/setup() . = ..() @@ -84,7 +83,6 @@ /datum/action/cooldown/spell/thermal_vision/Remove(mob/living/remove_from) REMOVE_TRAIT(remove_from, TRAIT_THERMAL_VISION, GENETIC_MUTATION) - remove_from.update_sight() return ..() /datum/action/cooldown/spell/thermal_vision/is_valid_target(atom/cast_on) @@ -93,7 +91,6 @@ /datum/action/cooldown/spell/thermal_vision/cast(mob/living/cast_on) . = ..() ADD_TRAIT(cast_on, TRAIT_THERMAL_VISION, GENETIC_MUTATION) - cast_on.update_sight() to_chat(cast_on, span_info("You focus your eyes intensely, as your vision becomes filled with heat signatures.")) addtimer(CALLBACK(src, PROC_REF(deactivate), cast_on), thermal_duration) @@ -102,7 +99,6 @@ return REMOVE_TRAIT(cast_on, TRAIT_THERMAL_VISION, GENETIC_MUTATION) - cast_on.update_sight() to_chat(cast_on, span_info("You blink a few times, your vision returning to normal as a dull pain settles in your eyes.")) if(iscarbon(cast_on)) @@ -112,7 +108,7 @@ ///X-ray Vision lets you see through walls. /datum/mutation/xray name = "X Ray Vision" - desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, which might be fun for later //hmb + desc = "The subject gains the ability to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, which might be fun for later //hmb text_gain_indication = span_notice("The walls suddenly disappear!") instability = POSITIVE_INSTABILITY_MAJOR locked = TRUE @@ -122,19 +118,17 @@ if(!.) return ADD_TRAIT(owner, TRAIT_XRAY_VISION, GENETIC_MUTATION) - owner.update_sight() /datum/mutation/xray/on_losing(mob/living/carbon/human/owner) if(..()) return REMOVE_TRAIT(owner, TRAIT_XRAY_VISION, GENETIC_MUTATION) - owner.update_sight() ///Laser Eyes lets you shoot lasers from your eyes! /datum/mutation/laser_eyes name = "Laser Eyes" - desc = "Reflects concentrated light back from the eyes." + desc = "The subject gains the ability to reflect concentrated light back from the eyes." quality = POSITIVE locked = TRUE difficulty = 16 @@ -179,7 +173,7 @@ /datum/mutation/illiterate name = "Illiterate" - desc = "Causes a severe case of Aphasia that prevents reading or writing." + desc = "The subject loses the ability to read or write." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_danger("You feel unable to read or write.") @@ -195,3 +189,22 @@ if(..()) return REMOVE_TRAIT(owner, TRAIT_ILLITERATE, GENETIC_MUTATION) + +/datum/mutation/night_vision + name = "Night Vision" + desc = "The subject can see in the dark." + quality = POSITIVE + instability = POSITIVE_INSTABILITY_MAJOR + text_gain_indication = span_notice("The darkness of the corners of the room fades away.") + text_lose_indication = span_notice("The darkness of the corners of the room returns.") + +/datum/mutation/night_vision/on_acquiring(mob/living/carbon/human/owner) + . = ..() + if(!.) + return + ADD_TRAIT(owner, TRAIT_NIGHT_VISION, GENETIC_MUTATION) + +/datum/mutation/night_vision/on_losing(mob/living/carbon/human/owner) + if(..()) + return + REMOVE_TRAIT(owner, TRAIT_NIGHT_VISION, GENETIC_MUTATION) diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm index a681e98428ae..eea077d6f4ef 100644 --- a/code/datums/mutations/speech.dm +++ b/code/datums/mutations/speech.dm @@ -5,8 +5,8 @@ /datum/mutation/nervousness name = "Nervousness" - desc = "Causes the holder to stutter." - instability = NEGATIVE_STABILITY_MINI + desc = "The subject gains a nervous stutter." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_danger("You feel nervous.") @@ -16,8 +16,8 @@ /datum/mutation/wacky name = "Wacky" - desc = "You are not a clown. You are the entire circus." - instability = NEGATIVE_STABILITY_MINI + desc = "The subject not only becomes a clown, but instead the entire circus." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_sans(span_notice("You feel an off sensation in your voicebox.")) text_lose_indication = span_notice("The off sensation passes.") @@ -135,7 +135,7 @@ /datum/mutation/mute name = "Mute" - desc = "Completely inhibits the vocal section of the brain." + desc = "The subject's vocal section of the brain is completely inhibited." instability = NEGATIVE_STABILITY_MAJOR quality = NEGATIVE text_gain_indication = span_danger("You feel unable to express yourself at all.") @@ -154,7 +154,7 @@ /datum/mutation/unintelligible name = "Unintelligible" - desc = "Partially inhibits the vocal center of the brain, severely distorting speech." + desc = "The subject's vocal section of the brain is partially inhibited, distorting speech." instability = NEGATIVE_STABILITY_MODERATE quality = NEGATIVE text_gain_indication = span_danger("You can't seem to form any coherent thoughts!") @@ -173,8 +173,9 @@ /datum/mutation/swedish name = "Swedish" - desc = "A horrible mutation originating from the distant past. Thought to be eradicated after the incident in 2037." - instability = NEGATIVE_STABILITY_MINI + desc = "A horrible mutation originating from the distant past, thought to be eradicated after the incident in 2037. \ + The subject's speech becomes difficult to understand, and is often mistaken for a foreign language." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_notice("You feel Swedish, however that works.") text_lose_indication = span_notice("The feeling of Swedishness passes.") @@ -186,8 +187,9 @@ /datum/mutation/chav name = "Chav" - desc = "Unknown" - instability = NEGATIVE_STABILITY_MINI + desc = "An unknown mutation that causes the subject to speak in a very specific dialect. \ + The subject's speech becomes difficult to understand, and is often mistaken for a foreign language." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_notice("Ye feel like a reet prat like, innit?") text_lose_indication = span_notice("You no longer feel like being rude and sassy.") @@ -198,8 +200,9 @@ /datum/mutation/elvis name = "Elvis" - desc = "A terrifying mutation named after its 'patient-zero'." - instability = NEGATIVE_STABILITY_MINI + desc = "A terrifying mutation named after its 'patient-zero'. \ + The subject begins to speak in strange rhythms, and often breaks into dance." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_notice("You feel pretty good, honeydoll.") text_lose_indication = span_notice("You feel a little less conversation would be great.") @@ -221,7 +224,7 @@ /datum/mutation/stoner name = "Stoner" - desc = "A common mutation that severely decreases intelligence." + desc = "The subject's intelligence levels drop significantly, causing them to speak in words that only others on a similar level can understand." quality = NEGATIVE text_gain_indication = span_notice("You feel...totally chill, man!") text_lose_indication = span_notice("You feel like you have a better sense of time.") @@ -240,8 +243,9 @@ /datum/mutation/medieval name = "Medieval" - desc = "A horrible mutation originating from the distant past, thought to have once been a common gene in all of old world Europe." - instability = NEGATIVE_STABILITY_MINI + desc = "A horrible mutation originating from the distant past, thought to have once been a common gene in all of old Terran \"Europe\". \ + The subject gains a verbal tick that causes them to speak very particularly." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_notice("You feel like seeking the holy grail!") text_lose_indication = span_notice("You no longer feel like seeking anything.") @@ -282,8 +286,9 @@ /datum/mutation/piglatin name = "Pig Latin" - desc = "Historians say back in the 2020's humanity spoke entirely in this mystical language." - instability = NEGATIVE_STABILITY_MINI + desc = "Historians say five centuries ago, humanity spoke entirely in this mystical language. \ + The subject becomes forced to speak in said language, which is often difficult to understand." + instability = NEGATIVE_STABILITY_NONE quality = MINOR_NEGATIVE text_gain_indication = span_notice("Omethingsay eelsfay offyay.") text_lose_indication = span_notice("The off sensation passes.") diff --git a/code/datums/mutations/telepathy.dm b/code/datums/mutations/telepathy.dm index 5e296c38c0ed..9b16b60d3130 100644 --- a/code/datums/mutations/telepathy.dm +++ b/code/datums/mutations/telepathy.dm @@ -1,6 +1,6 @@ /datum/mutation/telepathy name = "Telepathy" - desc = "A rare mutation that allows the user to telepathically communicate to others." + desc = "The subject gains the abililty to telepathically communicate to others." quality = POSITIVE text_gain_indication = span_notice("You can hear your own voice echoing in your mind!") text_lose_indication = span_notice("You don't hear your mind echo anymore.") diff --git a/code/datums/mutations/tongue_spike.dm b/code/datums/mutations/tongue_spike.dm index 4d908b6f9594..95be63fb6884 100644 --- a/code/datums/mutations/tongue_spike.dm +++ b/code/datums/mutations/tongue_spike.dm @@ -1,6 +1,6 @@ /datum/mutation/tongue_spike name = "Tongue Spike" - desc = "Allows a creature to voluntary shoot their tongue out as a deadly weapon." + desc = "The subject gains the ability to voluntary shoot their tongue out as a deadly weapon." quality = POSITIVE text_gain_indication = span_notice("Your feel like you can throw your voice.") instability = POSITIVE_INSTABILITY_MINI // worthless. also serves as a bit of a hint that it's not good @@ -89,7 +89,7 @@ /datum/mutation/tongue_spike/chem name = "Chem Spike" - desc = "Allows a creature to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals." + desc = "The subject gains the ability to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals." quality = POSITIVE text_gain_indication = span_notice("Your feel like you can really connect with people by throwing your voice.") instability = POSITIVE_INSTABILITY_MINOR // slightly less worthless. slightly. diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm index 8ce75e746c58..81890d12474e 100644 --- a/code/datums/mutations/touch.dm +++ b/code/datums/mutations/touch.dm @@ -1,6 +1,6 @@ /datum/mutation/shock name = "Shock Touch" - desc = "The affected can channel excess electricity through their hands without shocking themselves, allowing them to shock others. Mostly harmless! Mostly... " + desc = "The subject gains the ability to channel excess ambient electricity through their hands, allowing them to shock others in a mostly harmless manner." quality = POSITIVE locked = TRUE difficulty = 16 @@ -84,7 +84,7 @@ /datum/mutation/lay_on_hands name = "Mending Touch" - desc = "The affected can lay their hands on other people to transfer a small amount of their injuries to themselves." + desc = "The subject gains the ability to lay their hands on others to transfer a small amount of their injuries to themselves." quality = POSITIVE locked = FALSE difficulty = 16 diff --git a/code/datums/mutations/void_magnet.dm b/code/datums/mutations/void_magnet.dm index aef24d19629d..322bd2f9be54 100644 --- a/code/datums/mutations/void_magnet.dm +++ b/code/datums/mutations/void_magnet.dm @@ -1,6 +1,6 @@ /datum/mutation/void name = "Void Magnet" - desc = "A rare genome that attracts odd forces not usually observed." + desc = "The subject attracts odd forces which are not usually observed." quality = MINOR_NEGATIVE //upsides and downsides text_gain_indication = span_notice("You feel a heavy, dull force just beyond the walls watching you.") instability = POSITIVE_INSTABILITY_MODERATE // useful, but has large drawbacks diff --git a/code/datums/mutations/webbing.dm b/code/datums/mutations/webbing.dm index 22395cd53174..9f4891aea896 100644 --- a/code/datums/mutations/webbing.dm +++ b/code/datums/mutations/webbing.dm @@ -1,7 +1,7 @@ //spider webs /datum/mutation/webbing name = "Webbing Production" - desc = "Allows the user to lay webbing, and travel through it." + desc = "The subject gains the ability to lay webbing, and travel through it." quality = POSITIVE text_gain_indication = span_notice("Your skin feels webby.") instability = POSITIVE_INSTABILITY_MODERATE // useful until you're lynched diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index d1290292ddda..12c412340dd8 100644 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -229,7 +229,7 @@ if(accessory) var/obj/item/clothing/under/U = user.w_uniform - if(U) + if(istype(U)) U.attach_accessory(SSwardrobe.provide_type(accessory, user)) else if(!visuals_only) WARNING("Unable to equip accessory [accessory] in outfit [name]. No uniform present!") diff --git a/code/datums/personality/_personality.dm b/code/datums/personality/_personality.dm index e5dc2da50562..9e421555a439 100644 --- a/code/datums/personality/_personality.dm +++ b/code/datums/personality/_personality.dm @@ -84,7 +84,7 @@ /datum/personality/process(seconds_per_tick) for(var/mob/living/subject as anything in SSpersonalities.processing_personalities[src]) - if(subject.stat >= UNCONSCIOUS || HAS_TRAIT(subject, TRAIT_NO_TRANSFORM)) + if(IS_UNCONSCIOUS(subject) || HAS_TRAIT(subject, TRAIT_NO_TRANSFORM)) continue if(on_tick(subject, seconds_per_tick) != PROCESS_KILL) continue diff --git a/code/datums/personality/reaction_to_groups.dm b/code/datums/personality/reaction_to_groups.dm index 825ce6ca14df..d5bfe4eeba02 100644 --- a/code/datums/personality/reaction_to_groups.dm +++ b/code/datums/personality/reaction_to_groups.dm @@ -47,7 +47,7 @@ subject.add_mood_event("paranoia_personality", /datum/mood_event/paranoid/large_group) /datum/personality/paranoid/proc/is_dangerous_mob(mob/living/subject, mob/living/carbon/human/target) - if(target.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(target)) return FALSE if(target.invisibility > subject.see_invisible || target.alpha < 20) return FALSE diff --git a/code/datums/proximity_monitor/fields/ai_target_tracking.dm b/code/datums/proximity_monitor/fields/ai_target_tracking.dm index 46cde22aaffc..5c596e1dfd75 100644 --- a/code/datums/proximity_monitor/fields/ai_target_tracking.dm +++ b/code/datums/proximity_monitor/fields/ai_target_tracking.dm @@ -2,13 +2,13 @@ /datum/proximity_monitor/advanced/ai_target_tracking edge_is_a_field = TRUE /// The ai behavior who owns us - var/datum/ai_behavior/find_potential_targets/owning_behavior + var/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/owning_behavior /// The ai controller we're using var/datum/ai_controller/controller /// The target key we're trying to fill var/target_key - /// The targeting strategy KEY we're using - var/targeting_strategy_key + /// The targeting_strategy var value from the owning behavior either a typepath or a BB key string + var/targeting_strategy /// The hiding location key we're using var/hiding_location_key @@ -20,30 +20,31 @@ // Initially, run the check manually // If that fails, set up a field and have it manage the behavior fully -/datum/proximity_monitor/advanced/ai_target_tracking/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, datum/ai_behavior/find_potential_targets/owning_behavior, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) +/datum/proximity_monitor/advanced/ai_target_tracking/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, datum/bt_node/ai_behavior/acquire_target/update_combat_targets/owning_behavior, datum/ai_controller/controller, target_key, targeting_strategy, hiding_location_key) . = ..() src.owning_behavior = owning_behavior src.controller = controller src.target_key = target_key - src.targeting_strategy_key = targeting_strategy_key + src.targeting_strategy = targeting_strategy src.hiding_location_key = hiding_location_key - src.filter = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) + + if(ispath(targeting_strategy)) + src.filter = GET_TARGETING_STRATEGY(targeting_strategy) + else + src.filter = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy]) + RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_SET(targeting_strategy), PROC_REF(targeting_datum_changed)) + RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_CLEARED(targeting_strategy), PROC_REF(targeting_datum_cleared)) + RegisterSignal(controller, COMSIG_QDELETING, PROC_REF(controller_deleted)) - RegisterSignal(controller, COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS, PROC_REF(controller_think)) RegisterSignal(controller, COMSIG_AI_CONTROLLER_POSSESSED_PAWN, PROC_REF(pawn_changed)) - RegisterSignal(controller, AI_CONTROLLER_BEHAVIOR_QUEUED(owning_behavior.type), PROC_REF(behavior_requeued)) - RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_SET(targeting_strategy_key), PROC_REF(targeting_datum_changed)) - RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_CLEARED(targeting_strategy_key), PROC_REF(targeting_datum_cleared)) recalculate_field(full_recalc = TRUE) /datum/proximity_monitor/advanced/ai_target_tracking/Destroy() . = ..() - if(!QDELETED(controller) && owning_behavior) - controller.modify_cooldown(owning_behavior, owning_behavior.get_cooldown(controller)) owning_behavior = null controller = null target_key = null - targeting_strategy_key = null + targeting_strategy = null hiding_location_key = null filter = null @@ -75,24 +76,13 @@ SIGNAL_HANDLER qdel(src) -/// React to controller planning -/datum/proximity_monitor/advanced/ai_target_tracking/proc/controller_think(datum/ai_controller/source, list/datum/ai_behavior/old_behaviors, list/datum/ai_behavior/new_behaviors) - SIGNAL_HANDLER - // If our parent was forgotten, nuke ourselves - if(!new_behaviors[owning_behavior]) - qdel(src) - -/datum/proximity_monitor/advanced/ai_target_tracking/proc/behavior_requeued(datum/source, list/new_arguments) - SIGNAL_HANDLER - check_new_args(arglist(new_arguments)) - /// Ensure our args and locals are up to date -/datum/proximity_monitor/advanced/ai_target_tracking/proc/check_new_args(target_key, targeting_strategy_key, hiding_location_key) +/datum/proximity_monitor/advanced/ai_target_tracking/proc/check_new_args(target_key, targeting_strategy, hiding_location_key) var/update_filter = FALSE if(src.target_key != target_key) src.target_key = target_key - if(src.targeting_strategy_key != targeting_strategy_key) - src.targeting_strategy_key = targeting_strategy_key + if(src.targeting_strategy != targeting_strategy) + src.targeting_strategy = targeting_strategy update_filter = TRUE if(src.hiding_location_key != hiding_location_key) src.hiding_location_key = hiding_location_key @@ -101,7 +91,10 @@ /datum/proximity_monitor/advanced/ai_target_tracking/proc/targeting_datum_changed(datum/source) SIGNAL_HANDLER - filter = controller.blackboard[targeting_strategy_key] + if(ispath(targeting_strategy)) + filter = GET_TARGETING_STRATEGY(targeting_strategy) + else + filter = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy]) // Filter changed, need to do a full reparse // Fucking 9 * 9 out here I stg for(var/turf/in_field as anything in field_turfs + edge_turfs) diff --git a/code/datums/proximity_monitor/fields/heretic_arena.dm b/code/datums/proximity_monitor/fields/heretic_arena.dm index 3f81dd0c58ee..1b4bdbde476d 100644 --- a/code/datums/proximity_monitor/fields/heretic_arena.dm +++ b/code/datums/proximity_monitor/fields/heretic_arena.dm @@ -163,16 +163,22 @@ GLOBAL_LIST_EMPTY(heretic_arenas) human_head?.worn_head_offset?.apply_offset(crown_overlay) owner.add_overlay(crown_overlay) owner.remove_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id)) + crit_count++ // The mansus celebrates your efforts if(IS_HERETIC(owner)) - owner.heal_overall_damage(60, 60, 60) - owner.adjust_tox_loss(-60, forced = TRUE) // Slime heretics everywhere... - owner.adjust_oxy_loss(-60) + // enemies are given 17 force 0 ap blades, heretics have 50 armor: meaning 30 healing reverses about 4 hits of damage + owner.heal_overall_damage(round(30 / crit_count, DAMAGE_PRECISION), round(30 / crit_count, DAMAGE_PRECISION)) + owner.adjust_tox_loss(round(-20 / crit_count, DAMAGE_PRECISION), forced = TRUE) // Slime heretics everywhere... + owner.adjust_oxy_loss(round(-20 / crit_count, DAMAGE_PRECISION), forced = TRUE) if(iscarbon(owner)) var/mob/living/carbon/carbon_owner = owner for(var/datum/wound/wound as anything in carbon_owner.all_wounds) wound.remove_wound() + if(crit_count == 3) + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(owner) + var/datum/heretic_knowledge/spell/wolves_among_sheep/our_knowledge = our_heretic.get_knowledge(__IMPLIED_TYPE__) + our_knowledge?.add_charges(1) if(arena_victor) // No need to spam if we've already killed at least 1 person return @@ -194,6 +200,8 @@ GLOBAL_LIST_EMPTY(heretic_arenas) tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = null + /// Tracks number of people crit + var/crit_count = 0 /// Tracks the last person who dealt damage to this mob var/datum/weakref/last_attacker /// If our mob is free to leave, set to true @@ -202,7 +210,7 @@ GLOBAL_LIST_EMPTY(heretic_arenas) var/mutable_appearance/crown_overlay /datum/status_effect/arena_tracker/on_apply() - RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(on_enter_crit)) + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_enter_crit)) RegisterSignal(owner, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(on_impact_zone)) RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(damage_taken)) owner.add_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id)) @@ -217,14 +225,16 @@ GLOBAL_LIST_EMPTY(heretic_arenas) return TRUE /datum/status_effect/arena_tracker/on_remove() - UnregisterSignal(owner, list(SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), COMSIG_MOB_APPLY_DAMAGE)) + UnregisterSignal(owner, list(COMSIG_MOB_STATCHANGE, COMSIG_MOB_APPLY_DAMAGE)) owner.remove_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id)) owner.cut_overlay(crown_overlay) crown_overlay = null // If our last attacker is an arena participant, we let them know they've scored a critical hit -/datum/status_effect/arena_tracker/proc/on_enter_crit(mob/owner) +/datum/status_effect/arena_tracker/proc/on_enter_crit(mob/owner, new_stat, old_stat) SIGNAL_HANDLER + if(new_stat < SOFT_CRIT) + return if(!last_attacker) return // Safety check in case they somehow enter crit with *nobody* attacking them var/mob/living/our_attacker = last_attacker.resolve() diff --git a/code/datums/proximity_monitor/fields/timestop.dm b/code/datums/proximity_monitor/fields/timestop.dm index 8411e2c3bb3f..92fbf12356ef 100644 --- a/code/datums/proximity_monitor/fields/timestop.dm +++ b/code/datums/proximity_monitor/fields/timestop.dm @@ -223,7 +223,7 @@ hostile_victim.LoseTarget() else if(isbasicmob(victim)) var/mob/living/basic/basic_victim = victim - basic_victim.ai_controller?.set_ai_status(AI_STATUS_OFF) + basic_victim.ai_controller?.force_ai_off() /datum/proximity_monitor/advanced/timestop/proc/unfreeze_mob(mob/living/victim) victim.AdjustStun(-20, ignore_canstun = TRUE) @@ -234,7 +234,7 @@ animal_victim.toggle_ai(initial(animal_victim.AIStatus)) else if(isbasicmob(victim)) var/mob/living/basic/basic_victim = victim - basic_victim.ai_controller?.reset_ai_status() + basic_victim.ai_controller?.clear_forced_off() //you don't look quite right, is something the matter? /datum/proximity_monitor/advanced/timestop/proc/into_the_negative_zone(atom/A) diff --git a/code/datums/quirks/negative_quirks/addict.dm b/code/datums/quirks/negative_quirks/addict.dm index 556452bc3607..264d2bce4364 100644 --- a/code/datums/quirks/negative_quirks/addict.dm +++ b/code/datums/quirks/negative_quirks/addict.dm @@ -221,7 +221,7 @@ /datum/quirk/item_quirk/addict/alcoholic/remove() UnregisterSignal(quirk_holder, COMSIG_MOB_REAGENT_TICK) -/datum/quirk/item_quirk/addict/alcoholic/proc/check_brandy(mob/source, datum/reagent/booze) +/datum/quirk/item_quirk/addict/alcoholic/proc/check_brandy(mob/source, datum/reagent/booze, seconds_per_tick, metabolization_rate) SIGNAL_HANDLER //we don't care if it is not alcohol diff --git a/code/datums/quirks/negative_quirks/allergic.dm b/code/datums/quirks/negative_quirks/allergic.dm index 440e8f6bf0b7..457fc46d7b8f 100644 --- a/code/datums/quirks/negative_quirks/allergic.dm +++ b/code/datums/quirks/negative_quirks/allergic.dm @@ -1,3 +1,28 @@ +/// List of medicine reagents that we generally always want to be usable for a mob +GLOBAL_LIST_INIT(allergy_reagent_blacklist, typecacheof(list( + /datum/reagent/medicine/adminordrazine, + /datum/reagent/medicine/albuterol, + /datum/reagent/medicine/changelingadrenaline, + /datum/reagent/medicine/changelinghaste, + /datum/reagent/medicine/coagulant/banana_peel, + /datum/reagent/medicine/coagulant/seraka_extract, + /datum/reagent/medicine/cordiolis_hepatico, + /datum/reagent/medicine/diphenhydramine, + /datum/reagent/medicine/epinephrine, + /datum/reagent/medicine/insulin, + /datum/reagent/medicine/muscle_stimulant, + /datum/reagent/medicine/oculine/flumpuline, + /datum/reagent/medicine/omnizine/godblood, + /datum/reagent/medicine/omnizine/protozine, + /datum/reagent/medicine/regen_jelly, + /datum/reagent/medicine/salglu_solution, + /datum/reagent/medicine/sansufentanyl, + /datum/reagent/medicine/strange_reagent, + /datum/reagent/medicine/synaphydramine, + /datum/reagent/medicine/synaptizine/synaptizinevirusfood, + /datum/reagent/medicine/syndicate_nanites, +))) + /datum/quirk/item_quirk/allergic name = "Extreme Medicine Allergy" desc = "Ever since you were a kid, you've been allergic to certain chemicals..." @@ -11,23 +36,10 @@ mail_goodies = list(/obj/item/reagent_containers/hypospray/medipen) // epinephrine medipen stops allergic reactions no_process_traits = list(TRAIT_STASIS) var/list/allergies = list() - var/list/blacklist = list( - /datum/reagent/medicine/c2, - /datum/reagent/medicine/epinephrine, - /datum/reagent/medicine/adminordrazine, - /datum/reagent/medicine/adminordrazine/quantum_heal, - /datum/reagent/medicine/omnizine/godblood, - /datum/reagent/medicine/cordiolis_hepatico, - /datum/reagent/medicine/synaphydramine, - /datum/reagent/medicine/diphenhydramine, - /datum/reagent/medicine/sansufentanyl, - /datum/reagent/medicine/salglu_solution, - /datum/reagent/medicine/albuterol, - ) var/allergy_string /datum/quirk/item_quirk/allergic/add(client/client_source) - var/list/chem_list = subtypesof(/datum/reagent/medicine) - blacklist + var/list/chem_list = valid_subtypesof(/datum/reagent/medicine) - GLOB.allergy_reagent_blacklist var/list/allergy_chem_names = list() for(var/i in 0 to 5) var/datum/reagent/medicine/chem_type = pick_n_take(chem_list) @@ -50,9 +62,9 @@ /datum/quirk/item_quirk/allergic/post_add() quirk_holder.add_mob_memory(/datum/memory/key/quirk_allergy, allergy_string = allergy_string) - to_chat(quirk_holder, span_boldnotice("You are allergic to [allergy_string], make sure not to consume any of these!")) + to_chat(quirk_holder, span_boldnotice("You are allergic to [allergy_string] - make sure not to consume any of these!")) -/datum/quirk/item_quirk/allergic/proc/block_metab(mob/living/carbon/source, datum/reagent/chem, seconds_per_tick) +/datum/quirk/item_quirk/allergic/proc/block_metab(mob/living/carbon/source, datum/reagent/chem, seconds_per_tick, metabolization_ratio) SIGNAL_HANDLER if(!is_type_in_list(chem, allergies)) diff --git a/code/datums/quirks/negative_quirks/erratic.dm b/code/datums/quirks/negative_quirks/erratic.dm index 88fca1974369..f819aa1bc195 100644 --- a/code/datums/quirks/negative_quirks/erratic.dm +++ b/code/datums/quirks/negative_quirks/erratic.dm @@ -29,7 +29,7 @@ /datum/quirk/erratic/process(seconds_per_tick) if(!COOLDOWN_FINISHED(src, randomize_cooldown)) return - if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS) || quirk_holder.stat >= UNCONSCIOUS) + if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS) || IS_UNCONSCIOUS(quirk_holder)) return COOLDOWN_START(src, randomize_cooldown, rand(6, 12) MINUTES) diff --git a/code/datums/quirks/negative_quirks/glass_jaw.dm b/code/datums/quirks/negative_quirks/glass_jaw.dm index 2ea82b96c8ec..0dcb0900b7b3 100644 --- a/code/datums/quirks/negative_quirks/glass_jaw.dm +++ b/code/datums/quirks/negative_quirks/glass_jaw.dm @@ -45,10 +45,10 @@ //blunt items are more likely to knock out, but sharp ones are still capable of doing it if(prob(CEILING(damage * (sharpness & (SHARP_EDGED|SHARP_POINTY) ? 0.65 : 1), 1))) //don't display the message if little mac is already KO'd - if(!source.IsUnconscious()) + if(!IS_UNCONSCIOUS(source)) source.visible_message( span_warning("[source] gets knocked out!"), span_userdanger("You get knocked out!"), vision_distance = COMBAT_MESSAGE_RANGE, - ) + ) source.Unconscious(3 SECONDS) diff --git a/code/datums/quirks/negative_quirks/photophobia.dm b/code/datums/quirks/negative_quirks/photophobia.dm index 40bf0b2b3641..4681770f1d31 100644 --- a/code/datums/quirks/negative_quirks/photophobia.dm +++ b/code/datums/quirks/negative_quirks/photophobia.dm @@ -53,10 +53,7 @@ /datum/quirk/photophobia/proc/on_holder_moved(mob/living/source, atom/old_loc, dir, forced) SIGNAL_HANDLER - if(quirk_holder.stat != CONSCIOUS || quirk_holder.IsSleeping() || quirk_holder.IsUnconscious()) - return - - if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS)) + if(IS_UNCONSCIOUS(quirk_holder) || HAS_TRAIT(quirk_holder, TRAIT_FEARLESS)) return var/mob/living/carbon/human/human_holder = quirk_holder diff --git a/code/datums/quirks/negative_quirks/softspoken.dm b/code/datums/quirks/negative_quirks/softspoken.dm index 41be5f1aca00..c704d80b8d94 100644 --- a/code/datums/quirks/negative_quirks/softspoken.dm +++ b/code/datums/quirks/negative_quirks/softspoken.dm @@ -3,7 +3,7 @@ desc = "You are soft-spoken, and your voice is hard to hear." icon = FA_ICON_COMMENT value = -2 - mob_trait = TRAIT_SOFTSPOKEN + mob_trait = TRAIT_FORCE_WHISPER gain_text = span_danger("You feel like you're speaking more quietly.") lose_text = span_notice("You feel like you're speaking louder.") medical_record_text = "Patient is soft-spoken and difficult to hear." diff --git a/code/datums/quirks/positive_quirks/spacer.dm b/code/datums/quirks/positive_quirks/spacer.dm index a26e949c2991..470dd321c411 100644 --- a/code/datums/quirks/positive_quirks/spacer.dm +++ b/code/datums/quirks/positive_quirks/spacer.dm @@ -17,8 +17,6 @@ /obj/item/storage/pill_bottle/ondansetron, /obj/item/reagent_containers/applicator/pill/gravitum, ) - /// How high spacers get bumped up to - var/modded_height = HUMAN_HEIGHT_TALLEST /// How long on a planet before we get averse effects var/planet_period = 3 MINUTES /// TimerID for time spend on a planet @@ -36,6 +34,10 @@ VAR_FINAL/drift_mod = 0.75 /datum/quirk/spacer_born/add(client/client_source) + var/modded_height = GLOB.spacer_height_choices[client_source?.prefs?.read_preference(/datum/preference/choiced/spacer_height)] + if(isnull(modded_height)) + modded_height = GLOB.spacer_height_choices[pick(GLOB.spacer_height_choices)] + if(isdummy(quirk_holder)) var/mob/living/carbon/human/dummy_quirker = quirk_holder dummy_quirker.set_mob_height(modded_height) diff --git a/code/datums/shuttles/emergency.dm b/code/datums/shuttles/emergency.dm index aeb896e54dea..bd3e3270abcd 100644 --- a/code/datums/shuttles/emergency.dm +++ b/code/datums/shuttles/emergency.dm @@ -523,4 +523,16 @@ /datum/map_template/shuttle/emergency/zeta/prerequisites_met() return SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_ALIENTECH] +/datum/map_template/shuttle/emergency/tombstone + suffix = "tombstone" + name = "The NTSS Tombstone" + description = "Specifically commissioned for cleanups involving bio hazards, zombie outbreaks, or flesh-eating plagues. Features burial plots, reinforced morgue trays, and a crematorium. It's safe, airtight, and ensures you won't bite your coworkers during transit!" + admin_notes = "The aft of the ship has a miasma tank behind the curator room. May get released by crew. There are also random spawners in the crypt graves that has a chance to spawn a one use spectral musical instrument." + credit_cost = CARGO_CRATE_VALUE * 15 + occupancy_limit = "40" + prerequisites = "This shuttle requires a biohazard outbreak to occur before it can be purchased." + +/datum/map_template/shuttle/emergency/tombstone/prerequisites_met() + return SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_TOMBSTONE] + #undef EMAG_LOCKED_SHUTTLE_COST diff --git a/code/datums/sound_token.dm b/code/datums/sound_token.dm index 036d45f7fbeb..5412ce937224 100644 --- a/code/datums/sound_token.dm +++ b/code/datums/sound_token.dm @@ -36,8 +36,10 @@ var/datum/cell_tracker/cell_tracker ///Should we destroy the datum when the sound is done? var/delete_on_end = FALSE + ///Do we repeat the sound using sound.repeat? + var/repeating = FALSE -/datum/sound_token/New(atom/_source, _sound, _range = 10, _volume = 50, _falloff_exponent = SOUND_FALLOFF_EXPONENT, _falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, _allowed_listeners, _sound_duration_override, _delete_on_end) +/datum/sound_token/New(atom/_source, _sound, _range = 10, _volume = 50, _falloff_exponent = SOUND_FALLOFF_EXPONENT, _falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, _allowed_listeners, _sound_duration_override, _delete_on_end, _repeating) source = _source RegisterSignal(source, COMSIG_QDELETING, PROC_REF(source_deleted)) RegisterSignal(source, COMSIG_MOVABLE_MOVED, PROC_REF(source_moved)) @@ -50,6 +52,7 @@ sound_duration_override = _sound_duration_override if(_delete_on_end) delete_on_end = _delete_on_end + repeating = _repeating if(_allowed_listeners) for(var/allowed_mob in _allowed_listeners) @@ -69,14 +72,16 @@ /datum/sound_token/Destroy(force, ...) for(var/listener in listeners) remove_listener(listener) - listeners = null source = null return ..() ///Lets us update the sound to a new one. -/datum/sound_token/proc/update_sound(_sound, start_playing = FALSE) +/datum/sound_token/proc/update_sound(_sound, start_playing = FALSE, _repeating = null) + if(!isnull(_repeating)) + repeating = _repeating sound = sound(_sound) + sound.repeat = repeating if(!sound_channel) sound_channel = SSsounds.reserve_sound_channel_for_datum(src) sound.channel = sound_channel @@ -84,7 +89,7 @@ start_time = REALTIMEOFDAY if(start_playing) force_update_all_listeners(FALSE) - if(delete_on_end) + if(delete_on_end && !repeating) addtimer(CALLBACK(src, PROC_REF(on_sound_ended)), sound_duration, TIMER_UNIQUE | TIMER_OVERRIDE) @@ -251,6 +256,8 @@ var/freq_factor = (sound.frequency || 100) / 100 var/pitch_factor = (sound.pitch || 100) / 100 var/offset = elapsed * freq_factor * pitch_factor + if(repeating && sound_duration) + offset %= sound_duration return offset ///Update tracked cells; happens on movement. We need to check if anyone is now out of cell range and kick them out. diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index 7231d47e17b0..5b6887c066c9 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -229,7 +229,7 @@ /datum/station_trait/revenge_of_pun_pun/proc/arm_monke() SIGNAL_HANDLER - var/mob/living/carbon/human/species/monkey/punpun/punpun = GLOB.the_one_and_only_punpun + var/mob/living/punpun = GLOB.the_one_and_only_punpun if(!punpun) return var/weapon_type = pick_weight(weapon_types) diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 16feace65c43..e59aace6d624 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -24,6 +24,18 @@ // This station trait modifies the atmosphere, which is too far past the time admins are able to revert it can_revert = FALSE +/// Upgrades pun-pun into a big ass gorrilla. +/datum/station_trait/pun_pun_gym_day + name = "Pun Pun Gym Day" + trait_type = STATION_TRAIT_NEUTRAL + weight = 1 + cost = STATION_TRAIT_COST_LOW + show_in_report = TRUE + report_message = "Following a clerical error in our primate requisition form, your bar's simian has been upgraded to a bigger primate. We have elected not to correct this." + trait_to_give = STATION_TRAIT_PUN_PUN_GYM_DAY + blacklist = list(/datum/station_trait/job/pun_pun) //We should make these two work together later, itd be funny. + + /datum/station_trait/spider_infestation name = "Spider Infestation" trait_type = STATION_TRAIT_NEUTRAL diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm index 8b4de47d9ea5..b0cf4afc72ef 100644 --- a/code/datums/status_effects/_status_effect.dm +++ b/code/datums/status_effects/_status_effect.dm @@ -66,26 +66,14 @@ update_shown_duration() if(duration != STATUS_EFFECT_PERMANENT || tick_interval != STATUS_EFFECT_NO_TICK) //don't process if we don't care - switch(processing_speed) - if(STATUS_EFFECT_FAST_PROCESS) - START_PROCESSING(SSfastprocess, src) - if(STATUS_EFFECT_NORMAL_PROCESS) - START_PROCESSING(SSprocessing, src) - if(STATUS_EFFECT_PRIORITY) - START_PROCESSING(SSpriority_effects, src) + start_processing() update_particles() SEND_SIGNAL(owner, COMSIG_LIVING_STATUS_APPLIED, src) return TRUE /datum/status_effect/Destroy() - switch(processing_speed) - if(STATUS_EFFECT_FAST_PROCESS) - STOP_PROCESSING(SSfastprocess, src) - if(STATUS_EFFECT_NORMAL_PROCESS) - STOP_PROCESSING(SSprocessing, src) - if(STATUS_EFFECT_PRIORITY) - STOP_PROCESSING(SSpriority_effects, src) + stop_processing() if(owner) linked_alert = null owner.clear_alert(id) @@ -239,6 +227,46 @@ if(var_name == NAMEOF(src, show_duration)) update_shown_duration() +/// Stops ticking. Entirely stops processing if the effect is permanent. +/datum/status_effect/proc/stop_ticking() + // If we have a set duration, we can't stop processing as duration is also handled in process + if(duration != STATUS_EFFECT_PERMANENT) + time_until_next_tick = STATUS_EFFECT_NO_TICK + return + + // But if we have are permanent, there is no reason to keep processing if we don't tick + stop_processing() + +/// Stops processing, removing it from relevant subsystems +/datum/status_effect/proc/stop_processing() + switch(processing_speed) + if(STATUS_EFFECT_FAST_PROCESS) + STOP_PROCESSING(SSfastprocess, src) + if(STATUS_EFFECT_NORMAL_PROCESS) + STOP_PROCESSING(SSprocessing, src) + if(STATUS_EFFECT_PRIORITY) + STOP_PROCESSING(SSpriority_effects, src) + +/// (Re)starts ticking, also (re)starting processing if the effect is permanent +/datum/status_effect/proc/start_ticking() + // If we have a set duration, we assume we're processing already, so just reset the timer + if(duration != STATUS_EFFECT_PERMANENT) + time_until_next_tick = tick_interval + return + + // But if we are permanent, we probably need to start processing + start_processing() + +/// (Re)starts processing, adding it to relevant subsystems +/datum/status_effect/proc/start_processing() + switch(processing_speed) + if(STATUS_EFFECT_FAST_PROCESS) + START_PROCESSING(SSfastprocess, src) + if(STATUS_EFFECT_NORMAL_PROCESS) + START_PROCESSING(SSprocessing, src) + if(STATUS_EFFECT_PRIORITY) + START_PROCESSING(SSpriority_effects, src) + /// Alert base type for status effect alerts /atom/movable/screen/alert/status_effect name = "Curse of Mundanity" diff --git a/code/datums/status_effects/buffs/stun_absorption.dm b/code/datums/status_effects/buffs/stun_absorption.dm index 393a6031e83a..6481f5284adf 100644 --- a/code/datums/status_effects/buffs/stun_absorption.dm +++ b/code/datums/status_effects/buffs/stun_absorption.dm @@ -133,7 +133,7 @@ /// Simply checks if the owner of the effect is in a valid state to absorb stuns. /datum/status_effect/stun_absorption/proc/can_absorb_stun() - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return FALSE if(seconds_of_stuns_absorbed > max_seconds_of_stuns_blocked) return FALSE diff --git a/code/datums/status_effects/buffs/xray.dm b/code/datums/status_effects/buffs/xray.dm index cdeb909d6234..a88e8f96b3bb 100644 --- a/code/datums/status_effects/buffs/xray.dm +++ b/code/datums/status_effects/buffs/xray.dm @@ -10,12 +10,10 @@ /datum/status_effect/temporary_xray/on_apply() ADD_TRAIT(owner, TRAIT_XRAY_VISION, TRAIT_STATUS_EFFECT(id)) - owner.update_sight() return TRUE /datum/status_effect/temporary_xray/on_remove() REMOVE_TRAIT(owner, TRAIT_XRAY_VISION, TRAIT_STATUS_EFFECT(id)) - owner.update_sight() /datum/status_effect/temporary_xray/eldritch // Heretic subtype that plays a sound and screen alert alert_type = /atom/movable/screen/alert/status_effect/temporary_xray diff --git a/code/datums/status_effects/debuffs/choke.dm b/code/datums/status_effects/debuffs/choke.dm index 7f87fba7abda..ec9f85afbec7 100644 --- a/code/datums/status_effects/debuffs/choke.dm +++ b/code/datums/status_effects/debuffs/choke.dm @@ -238,7 +238,7 @@ if(iscarbon(victim)) var/mob/living/carbon/carbon_victim = victim - if(!carbon_victim.appears_alive()) + if(IS_DEAD_OR_FAKING(carbon_victim)) victim.balloon_alert(aggressor, "too late...") return FALSE diff --git a/code/datums/status_effects/debuffs/cursed.dm b/code/datums/status_effects/debuffs/cursed.dm index 810342e0fda2..6f00312946b6 100644 --- a/code/datums/status_effects/debuffs/cursed.dm +++ b/code/datums/status_effects/debuffs/cursed.dm @@ -130,7 +130,7 @@ /// If our owner's stat changes, rapidly surge the damage chance. /datum/status_effect/slot_machine_curse/proc/on_stat_changed() SIGNAL_HANDLER - if(owner.stat == CONSCIOUS || owner.stat == DEAD) // reset on these two states + if(!IS_UNCONSCIOUS_OR_CRIT(owner) || owner.stat == DEAD) // reset on these two states damage_chance = initial(damage_chance) return diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 17f56e203e31..bd678954f3b4 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -133,7 +133,6 @@ //UNCONSCIOUS /datum/status_effect/incapacitating/unconscious id = "unconscious" - needs_update_stat = TRUE force_say_chance = 100 /datum/status_effect/incapacitating/unconscious/on_apply() @@ -146,10 +145,60 @@ REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) return ..() -/datum/status_effect/incapacitating/unconscious/tick(seconds_between_ticks) - if(owner.get_stamina_loss()) - owner.adjust_stamina_loss(-0.3) //reduce stamina loss by 0.3 per tick, 6 per 2 seconds +/// Handles the effects of being knocked out - don't apply directly, all you should be doing is using [TRAIT_KNOCKEDOUT] +/datum/status_effect/knocked_out + id = "is_knocked_out" + alert_type = null + +/datum/status_effect/knocked_out/on_apply() + owner.become_blind(TRAIT_STATUS_EFFECT(id)) + owner.apply_status_effect(/datum/status_effect/grouped/see_no_names, TRAIT_STATUS_EFFECT(id)) + owner.add_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_IMMOBILIZED, TRAIT_BLOCK_SECHUD, TRAIT_BLOCK_MEDHUD, TRAIT_INCAPACITATED, TRAIT_FLOORED), TRAIT_STATUS_EFFECT(id)) + owner.update_eyes() // updates eyelids + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_mob_statchange)) + RegisterSignal(owner, COMSIG_MOB_CLIENT_LOGIN, PROC_REF(show_unconscious_hud)) + if(GET_CLIENT(owner)) // let's not waste time giving the hud to non-player characters + show_unconscious_hud(owner) + return TRUE + +/datum/status_effect/knocked_out/on_creation(mob/living/new_owner, ...) + . = ..() + if(!.) + return + if(owner.stat == DEAD) + stop_ticking() + +/datum/status_effect/knocked_out/on_remove() + owner.cure_blind(TRAIT_STATUS_EFFECT(id)) + owner.remove_status_effect(/datum/status_effect/grouped/see_no_names, TRAIT_STATUS_EFFECT(id)) + owner.remove_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_IMMOBILIZED, TRAIT_BLOCK_SECHUD, TRAIT_BLOCK_MEDHUD, TRAIT_INCAPACITATED, TRAIT_FLOORED), TRAIT_STATUS_EFFECT(id)) + owner.update_eyes() // updates eyelids + UnregisterSignal(owner, list(COMSIG_MOB_CLIENT_LOGIN, COMSIG_MOB_STATCHANGE)) + if(GET_CLIENT(owner)) + hide_unconscious_hud(owner) + +/datum/status_effect/knocked_out/tick(seconds_between_ticks) + owner.adjust_stamina_loss(-3 * seconds_between_ticks) + +/// Global list of images that correspond to a mob's unconscious appearance +GLOBAL_LIST_EMPTY(unconscious_appearances) + +/datum/status_effect/knocked_out/proc/show_unconscious_hud(mob/living/source) + SIGNAL_HANDLER + + source.client?.images += (GLOB.unconscious_appearances - source.unconscious_appearance) + +/datum/status_effect/knocked_out/proc/hide_unconscious_hud(mob/living/source) + SIGNAL_HANDLER + + source.client?.images -= GLOB.unconscious_appearances +/datum/status_effect/knocked_out/proc/on_mob_statchange(mob/living/source, ...) + SIGNAL_HANDLER + if(owner.stat == DEAD) + stop_ticking() + else + start_ticking() //SLEEPING /datum/status_effect/incapacitating/sleeping @@ -164,18 +213,21 @@ . = ..() if(!.) return - if(HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) - tick_interval = STATUS_EFFECT_NO_TICK - else + if(!HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_insomniac)) RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_SLEEPIMMUNE), PROC_REF(on_owner_sleepy)) +/datum/status_effect/incapacitating/sleeping/on_creation(mob/living/new_owner, set_duration) + . = ..() + if(!.) + return + if(HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) + stop_ticking() + /datum/status_effect/incapacitating/sleeping/on_remove() UnregisterSignal(owner, list(SIGNAL_ADDTRAIT(TRAIT_SLEEPIMMUNE), SIGNAL_REMOVETRAIT(TRAIT_SLEEPIMMUNE))) - if(!HAS_TRAIT(owner, TRAIT_SLEEPIMMUNE)) - REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) - tick_interval = initial(tick_interval) + REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) return ..() /datum/status_effect/incapacitating/sleeping/try_force_say() @@ -186,13 +238,13 @@ /datum/status_effect/incapacitating/sleeping/proc/on_owner_insomniac(mob/living/source) SIGNAL_HANDLER REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) - tick_interval = STATUS_EFFECT_NO_TICK + stop_ticking() ///If the mob has the TRAIT_SLEEPIMMUNE but somehow looses it we make him sleep and restart the tick() /datum/status_effect/incapacitating/sleeping/proc/on_owner_sleepy(mob/living/source) SIGNAL_HANDLER ADD_TRAIT(owner, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(id)) - tick_interval = initial(tick_interval) + start_ticking() /datum/status_effect/incapacitating/sleeping/tick(seconds_between_ticks) if(owner.maxHealth) @@ -697,7 +749,7 @@ alert_type = null /datum/status_effect/spasms/tick(seconds_between_ticks) - if(owner.stat >= UNCONSCIOUS || owner.incapacitated || HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED) || HAS_TRAIT(owner, TRAIT_IMMOBILIZED)) + if(owner.incapacitated || IS_UNCONSCIOUS(owner) || HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED) || HAS_TRAIT(owner, TRAIT_IMMOBILIZED)) return if(!prob(15)) return @@ -863,7 +915,7 @@ /datum/status_effect/fake_virus/on_apply() if(HAS_TRAIT(owner, TRAIT_VIRUSIMMUNE)) return FALSE - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return FALSE return TRUE @@ -931,7 +983,7 @@ /datum/status_effect/ants/on_creation(mob/living/new_owner, amount_left) if(isnum(amount_left) && new_owner.stat < HARD_CRIT) - if(new_owner.stat < UNCONSCIOUS) // Unconscious people won't get messages + if(!IS_UNCONSCIOUS(new_owner)) // Unconscious people won't get messages to_chat(new_owner, span_userdanger("You're covered in ants!")) ants_remaining += amount_left RegisterSignal(new_owner, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(ants_washed)) @@ -940,10 +992,10 @@ /datum/status_effect/ants/refresh(effect, amount_left) var/mob/living/carbon/human/victim = owner if(isnum(amount_left) && ants_remaining >= 1 && victim.stat < HARD_CRIT) - if(victim.stat < UNCONSCIOUS) // Unconscious people won't get messages - if(!prob(1)) // 99% + if(!IS_UNCONSCIOUS(victim)) // Unconscious people won't get messages + if(prob(99)) to_chat(victim, span_userdanger("You're covered in MORE ants!")) - else // 1% + else INVOKE_ASYNC(victim, TYPE_PROC_REF(/atom/movable, say), "AAHH! THIS SITUATION HAS ONLY BEEN MADE WORSE WITH THE ADDITION OF YET MORE ANTS!!", forced = /datum/status_effect/ants) ants_remaining += amount_left . = ..() @@ -968,7 +1020,7 @@ /datum/status_effect/ants/tick(seconds_between_ticks) var/mob/living/carbon/human/victim = owner victim.apply_damage(max(0.1, round((ants_remaining * damage_per_ant), 0.1)) * seconds_between_ticks, BRUTE, spread_damage = TRUE) //Scales with # of ants (lowers with time). Roughly 10 brute over 50 seconds. - if(victim.stat <= SOFT_CRIT) //Makes sure people don't scratch at themselves while they're in a critical condition + if(!IS_UNCONSCIOUS_OR_CRIT(victim)) //Makes sure people don't scratch at themselves while they're in a critical condition if(prob(15)) switch(rand(1,2)) if(1) diff --git a/code/datums/status_effects/debuffs/dna_transformation.dm b/code/datums/status_effects/debuffs/dna_transformation.dm index 15a96bca42dc..ff3e1e0fff93 100644 --- a/code/datums/status_effects/debuffs/dna_transformation.dm +++ b/code/datums/status_effects/debuffs/dna_transformation.dm @@ -96,7 +96,7 @@ SIGNAL_HANDLER // Pause if we're dead, appear dead, or in stasis - if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_DEATHCOMA) || HAS_TRAIT(source, TRAIT_STASIS)) + if(IS_DEAD_OR_FAKING(source) || HAS_TRAIT(source, TRAIT_STASIS)) if(duration == STATUS_EFFECT_PERMANENT) return // Already paused diff --git a/code/datums/status_effects/debuffs/drowsiness.dm b/code/datums/status_effects/debuffs/drowsiness.dm index cd99e8790666..f7541e65b60c 100644 --- a/code/datums/status_effects/debuffs/drowsiness.dm +++ b/code/datums/status_effects/debuffs/drowsiness.dm @@ -29,7 +29,7 @@ /datum/status_effect/drowsiness/tick(seconds_between_ticks) // You do not feel drowsy while unconscious or in stasis - if(owner.stat >= UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_STASIS)) + if(IS_UNCONSCIOUS(owner) || HAS_TRAIT(owner, TRAIT_STASIS)) return // Resting helps against drowsiness diff --git a/code/datums/status_effects/debuffs/drunk.dm b/code/datums/status_effects/debuffs/drunk.dm index dcfff81332aa..95333a5508fd 100644 --- a/code/datums/status_effects/debuffs/drunk.dm +++ b/code/datums/status_effects/debuffs/drunk.dm @@ -22,7 +22,7 @@ /datum/status_effect/inebriated/get_examine_text() // Dead people don't look drunk - if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(owner)) return null // Having your face covered conceals your drunkness @@ -208,14 +208,14 @@ // Over 81, we will gain constant toxloss if(drunk_value >= 81) owner.adjust_tox_loss(1) - if(owner.stat == CONSCIOUS && prob(5)) + if(!IS_UNCONSCIOUS_OR_CRIT(owner) && prob(5)) to_chat(owner, span_warning("Maybe you should lie down for a bit...")) // Over 91, we gain even more toxloss, brain damage, and have a chance of dropping into a long sleep if(drunk_value >= 91) owner.adjust_tox_loss(1) owner.adjust_organ_loss(ORGAN_SLOT_BRAIN, 0.4) - if(owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) attempt_to_blackout() // And finally, over 100 - let's be honest, you shouldn't be alive by now. diff --git a/code/datums/status_effects/debuffs/heart_attack.dm b/code/datums/status_effects/debuffs/heart_attack.dm index 592162ea0f89..8af204518e7f 100644 --- a/code/datums/status_effects/debuffs/heart_attack.dm +++ b/code/datums/status_effects/debuffs/heart_attack.dm @@ -98,7 +98,7 @@ owner.adjust_oxy_loss(oxyloss_sum) if(time_until_stoppage <= 0) - if(owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) owner.adjust_eye_blur(20 SECONDS) human_owner.set_heartattack(TRUE) @@ -126,7 +126,7 @@ return var/mob/living/carbon/human/human_owner = owner var/cannot_grasp = (human_owner.usable_hands <= 0 || owner.incapacitated & INCAPABLE_RESTRAINTS || HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED)) - return span_warning("[owner.p_they()] looks to be doubling over" + "[cannot_grasp ? " in pain!" : ", clutching [owner.p_their()] chest in pain!"]") + return span_warning("[owner.p_they()] look[owner.p_s()] to be doubling over" +"[cannot_grasp ? " in pain!" : ", clutching [owner.p_their()] chest in pain!"]") /datum/status_effect/heart_attack/on_remove() REMOVE_TRAIT(owner, TRAIT_DISEASELIKE_SEVERITY_HIGH, type) diff --git a/code/datums/status_effects/debuffs/slime/slime_leech.dm b/code/datums/status_effects/debuffs/slime/slime_leech.dm index d1125b2d25d5..164c4783bf06 100644 --- a/code/datums/status_effects/debuffs/slime/slime_leech.dm +++ b/code/datums/status_effects/debuffs/slime/slime_leech.dm @@ -44,7 +44,7 @@ if(QDELETED(our_slime)) qdel(src) return - if(our_slime.stat != CONSCIOUS || !owner) + if(IS_UNCONSCIOUS_OR_CRIT(our_slime) || !owner) our_slime.stop_feeding(silent = TRUE) return @@ -57,7 +57,8 @@ if(prob(60) && ishuman(owner) && owner.client && !our_slime.ai_controller.blackboard[BB_SLIME_RABID]) our_slime.ai_controller?.set_blackboard_key(BB_SLIME_RABID, TRUE) //we might go rabid after finishing to feed on a human with a client. - our_slime.stop_feeding() + if(our_slime) + our_slime.stop_feeding() return var/totaldamage = 0 //total damage done to this unfortunate soul diff --git a/code/datums/status_effects/debuffs/stamcrit.dm b/code/datums/status_effects/debuffs/stamcrit.dm index fb5b5e309203..b93558fed8bb 100644 --- a/code/datums/status_effects/debuffs/stamcrit.dm +++ b/code/datums/status_effects/debuffs/stamcrit.dm @@ -36,7 +36,7 @@ if(!.) return . - if(owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_notice("You're too exhausted to keep going...")) owner.add_traits(list(TRAIT_INCAPACITATED, TRAIT_IMMOBILIZED, TRAIT_FLOORED), STAMINA) return . @@ -65,7 +65,7 @@ if(mod_amount > 0) return NONE - if(COOLDOWN_FINISHED(src, warn_cd) && owner.stat == CONSCIOUS) + if(COOLDOWN_FINISHED(src, warn_cd) && !IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_notice("You start to recover from the exhaustion!")) owner.visible_message(span_warning("[owner] starts to recover from the exhaustion!"), ignored_mobs = owner) COOLDOWN_START(src, warn_cd, 2.5 SECONDS) diff --git a/code/datums/status_effects/debuffs/vision/blindness.dm b/code/datums/status_effects/debuffs/vision/blindness.dm index 26b1dcadd526..8f2b9e2c8752 100644 --- a/code/datums/status_effects/debuffs/vision/blindness.dm +++ b/code/datums/status_effects/debuffs/vision/blindness.dm @@ -16,7 +16,7 @@ /// List of sources which prevent SIGHT_BYPASS from working var/static/list/blocking_sources = list( QUIRK_TRAIT, // Meant to be completely immutable - UNCONSCIOUS_TRAIT, // Duh + TRAIT_STATUS_EFFECT(/datum/status_effect/knocked_out::id), // Duh ) /datum/status_effect/grouped/blindness/on_apply() @@ -27,46 +27,59 @@ return ..() /datum/status_effect/grouped/blindness/source_added(source, ...) - update_blindness(source) + update_blindness() /datum/status_effect/grouped/blindness/source_removed(source, removing) if (!removing) - update_blindness(source) + update_blindness() -/datum/status_effect/grouped/blindness/proc/update_blindness(changed_source) +/datum/status_effect/grouped/blindness/proc/update_blindness() if (!CAN_BE_BLIND(owner)) // future proofing qdel(src) return if (!HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) - make_blind(changed_source) + make_blind() return for (var/blocker in blocking_sources) if (owner.is_blind_from(blocker)) - make_blind(changed_source) + make_blind() return make_unblind() -/datum/status_effect/grouped/blindness/proc/make_blind(changed_source) +/datum/status_effect/grouped/blindness/proc/make_blind() + if(!GET_CLIENT(owner)) + RegisterSignal(owner, COMSIG_MOB_LOGIN, PROC_REF(make_blind_on_login), override = TRUE) + return + // have some extra logic to determine what overlay to use // by default we use the noflicker overlay // but if our one and only source is from "temp blindness", use flicker overlay var/overlay_to_use = /atom/movable/screen/fullscreen/blind/noflicker - if(changed_source == /datum/status_effect/temporary_blindness::id && length(sources) == 1) + if(sources[1] == /datum/status_effect/temporary_blindness::id && length(sources) == 1) overlay_to_use = /atom/movable/screen/fullscreen/blind - owner.overlay_fullscreen(id, overlay_to_use ) + + owner.overlay_fullscreen(id, overlay_to_use) // You are blind - at most, able to make out shapes near you - owner.add_client_colour(/datum/client_colour/blindness, REF(src)) + owner.add_client_colour(/datum/client_colour/blindness, id) + +/datum/status_effect/grouped/blindness/proc/make_blind_on_login(mob/living/source) + SIGNAL_HANDLER + + UnregisterSignal(owner, COMSIG_MOB_LOGIN) + make_blind() /datum/status_effect/grouped/blindness/proc/make_unblind() + UnregisterSignal(owner, COMSIG_MOB_LOGIN) owner.clear_fullscreen(id) - owner.remove_client_colour(REF(src)) + owner.remove_client_colour(id) /datum/status_effect/grouped/blindness/on_remove() make_unblind() UnregisterSignal(owner, update_signals) + UnregisterSignal(owner, COMSIG_MOB_LOGIN) return ..() /atom/movable/screen/alert/status_effect/blind diff --git a/code/datums/status_effects/drug_effects.dm b/code/datums/status_effects/drug_effects.dm index 70184a4a123e..27973b147979 100644 --- a/code/datums/status_effects/drug_effects.dm +++ b/code/datums/status_effects/drug_effects.dm @@ -45,15 +45,19 @@ tick_interval = STATUS_EFFECT_NO_TICK status_type = STATUS_EFFECT_UNIQUE alert_type = /atom/movable/screen/alert/status_effect/seizure + // DARKPACK EDIT ADD START + var/dur_min = 1 SECONDS + var/dur_max = 3 SECONDS + // DARKPACK EDIT ADD END /datum/status_effect/seizure/on_apply() if(!iscarbon(owner)) return FALSE - var/amplitude = rand(1 SECONDS, 3 SECONDS) + var/amplitude = rand(dur_min, dur_max) // DARKPACK EDIT CHANGE duration = amplitude owner.set_jitter_if_lower(100 SECONDS) owner.Paralyze(duration) - owner.visible_message(span_warning("[owner] drops to the ground as [owner.p_they()] start seizing up."), \ + owner.visible_message(span_warning("[owner] drops to the ground as [owner.p_they()] start[owner.p_s()] seizing up."), \ span_warning("[pick("You can't collect your thoughts...", "You suddenly feel extremely dizzy...", "You can't think straight...","You can't move your face properly anymore...")]")) return TRUE diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index c478b4a2267a..695bfccc2a02 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -22,7 +22,7 @@ return ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id)) RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(owner_resist)) - if(!owner.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_userdanger("You become frozen in a cube!")) cube = icon('icons/effects/freeze.dmi', "ice_cube") owner.add_overlay(cube) @@ -42,7 +42,7 @@ qdel(src) /datum/status_effect/freon/on_remove() - if(!owner.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_notice("The cube melts!")) owner.cut_overlay(cube) owner.adjust_bodytemperature(100) diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 4b787d51b820..3ce91598fa6d 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -270,7 +270,7 @@ /// One of our possible takers moved, see if they left us hanging /datum/status_effect/offering/proc/check_taker_in_range(mob/living/taker) SIGNAL_HANDLER - if(taker.IsReachableBy(owner) || ((owner.pulling == taker) || (taker.pulling == owner)) && !IS_DEAD_OR_INCAP(taker)) + if(taker.IsReachableBy(owner) || ((owner.pulling == taker) || (taker.pulling == owner)) && !taker.incapacitated) return to_chat(taker, span_warning("You moved out of range of [owner]!")) @@ -281,7 +281,7 @@ SIGNAL_HANDLER for(var/mob/living/checking_taker as anything in possible_takers) - if(!istype(checking_taker) || (!checking_taker.IsReachableBy(owner) && !((owner.pulling == checking_taker) || (checking_taker.pulling == owner))) || IS_DEAD_OR_INCAP(checking_taker)) + if(!istype(checking_taker) || (!checking_taker.IsReachableBy(owner) && !((owner.pulling == checking_taker) || (checking_taker.pulling == owner))) || checking_taker.incapacitated) remove_candidate(checking_taker) /// We lost the item, give it up @@ -296,10 +296,10 @@ * Returns `TRUE` if the taker is valid as a target for the offering. */ /datum/status_effect/offering/proc/is_taker_elligible(mob/living/taker) - return taker.IsReachableBy(owner) && !IS_DEAD_OR_INCAP(taker) && additional_taker_check(taker) + return taker.IsReachableBy(owner) && !taker.incapacitated && additional_taker_check(taker) /** - * Additional checks added to `CanReach()` and `IS_DEAD_OR_INCAP()` in `is_taker_elligible()`. + * Additional checks added to `CanReach()` and `incapacitated` in `is_taker_elligible()`. * Should be what you override instead of `is_taker_elligible()`. By default, checks if the * taker can hold items. * diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 01c4b3980ecf..9d2ed52153b6 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -129,6 +129,8 @@ /// Switch this off if you want to handle click_alt in the parent atom var/click_alt_open = TRUE + /// Stops updates from being called on insert or remove, useful for mass insertions/removals - just don't forget to update it manually afterwards + VAR_FINAL/block_insert_remove_updates = FALSE /datum/storage/New( atom/parent, @@ -189,11 +191,12 @@ return arrived.item_flags |= IN_STORAGE - refresh_views() arrived.on_enter_storage(src) RegisterSignal(arrived, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive)) SEND_SIGNAL(arrived, COMSIG_ITEM_STORED, src) - parent.update_appearance() + if(!block_insert_remove_updates) + refresh_views() + parent.update_appearance() /// Automatically ran on all object removals: flag marking and view refreshing. /datum/storage/proc/handle_exit(datum/source, obj/item/gone) @@ -203,11 +206,27 @@ return gone.item_flags &= ~IN_STORAGE - remove_and_refresh(gone) gone.on_exit_storage(src) UnregisterSignal(gone, COMSIG_MOUSEDROPPED_ONTO) SEND_SIGNAL(gone, COMSIG_ITEM_UNSTORED, src) - parent.update_appearance() + + // resets relevant variables as it goes + for(var/mob/user as anything in is_using) + if (user.hud_used?.screen_groups[HUD_GROUP_STORAGE]) + user.hud_used.screen_groups[HUD_GROUP_STORAGE] -= gone + + user.client?.screen -= gone + + gone.layer = initial(gone.layer) + SET_PLANE_IMPLICIT(gone, initial(gone.plane)) + gone.mouse_opacity = initial(gone.mouse_opacity) + gone.screen_loc = null + if(numerical_stacking) + gone.maptext = "" + + if(!block_insert_remove_updates) + refresh_views() + parent.update_appearance() /// Set the passed atom as the parent /datum/storage/proc/set_parent(atom/new_parent) @@ -369,15 +388,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) RegisterSignal(modeswitch_action, COMSIG_ACTION_TRIGGER, PROC_REF(action_trigger)) RegisterSignal(modeswitch_action, COMSIG_QDELETING, PROC_REF(action_deleted)) -/// Refreshes and item to be put back into the real world, out of storage. -/datum/storage/proc/reset_item(obj/item/thing) - thing.layer = initial(thing.layer) - SET_PLANE_IMPLICIT(thing, initial(thing.plane)) - thing.mouse_opacity = initial(thing.mouse_opacity) - thing.screen_loc = null - if(numerical_stacking) - thing.maptext = "" - /** * Checks if an item is capable of being inserted into the storage. * @@ -503,7 +513,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) to_insert.do_pickup_animation(real_location, user) if (messages) item_insertion_feedback(user, to_insert, override) - parent.update_appearance() return TRUE /// Since items inside storages ignore transparency for QOL reasons, we're tracking when things are dropped onto them instead of our UI elements @@ -536,30 +545,35 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) * * Arguments * * mob/user - the user who is inserting the items - * * list/things - the list of items to insert + * * list/pick_up_type - type to filter items by * * atom/thing_loc - the location of the items (used to make sure an item hasn't moved during pickup) * * list/rejections - a list used to make sure we only complain once about an invalid insertion * * datum/progressbar/progress - the progressbar used to show the progress of the insertion + * * list/success - list with a single element to use as a tracker for the amount of things we picked up */ -/datum/storage/proc/handle_mass_pickup(mob/user, list/things, atom/thing_loc, list/rejections, datum/progressbar/progress) - for(var/obj/item/thing in things) - things -= thing - if(thing.loc != thing_loc) +/datum/storage/proc/handle_mass_pickup(mob/user, pick_up_type, atom/thing_loc, list/rejections, datum/progressbar/progress, list/success) + . = FALSE + block_insert_remove_updates = TRUE + for(var/obj/item/thing in thing_loc) + if(!isnull(pick_up_type) && !istype(thing, pick_up_type)) continue if(thing.type in rejections) // To limit bag spamming: any given type only complains once continue - if(!attempt_insert(thing, user, override = TRUE)) // Note can_be_inserted still makes noise when the answer is no + if(!attempt_insert(thing, user, override = TRUE, messages = FALSE)) // Note can_be_inserted still makes noise when the answer is no if(real_location.contents.len >= max_slots) break rejections += thing.type // therefore full bags are still a little spammy continue - + success[1] += 1 if (TICK_CHECK) - progress.update(progress.goal - things.len) - return TRUE + . = TRUE + break - progress.update(progress.goal - things.len) - return FALSE + block_insert_remove_updates = FALSE + refresh_views() + parent.update_appearance() + progress.update(success[1]) + return . /** * Provides visual feedback in chat for an item insertion @@ -608,7 +622,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) thing.dropped(mob_parent, /*silent = */TRUE) if(remove_to_loc) - reset_item(thing) thing.forceMove(remove_to_loc) if(!silent && do_rustle) @@ -619,12 +632,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) else thing.moveToNullspace() - if(visual_updates) - if(animated) - animate_parent() - - refresh_views() - parent.update_appearance() + if(visual_updates && animated) + animate_parent() SEND_SIGNAL(parent, COMSIG_ATOM_REMOVED_ITEM, thing, remove_to_loc, silent) SEND_SIGNAL(src, COMSIG_STORAGE_REMOVED_ITEM, thing, remove_to_loc, silent) @@ -638,12 +647,20 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) * * update_storage - should we update the parent to show visual effects */ /datum/storage/proc/remove_all(atom/drop_loc = parent.drop_location(), update_storage = TRUE) + block_insert_remove_updates = TRUE for(var/obj/item/thing in real_location) - if(!attempt_remove(thing, drop_loc, silent = TRUE, visual_updates = update_storage)) + if(!attempt_remove(thing, drop_loc, silent = TRUE, visual_updates = FALSE)) continue thing.pixel_x = thing.base_pixel_x + rand(-8, 8) thing.pixel_y = thing.base_pixel_y + rand(-8, 8) + block_insert_remove_updates = FALSE + if(update_storage) + if(animated) + animate_parent() + refresh_views() + parent.update_appearance() + /** * Allows a mob to attempt to remove a single item from the storage * Allows for hooks into things like removal delays @@ -712,27 +729,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) return ret -/** - * Resets an object, removes it from our screen, and refreshes the view. - * - * @param atom/movable/gone the object leaving our storage - */ -/datum/storage/proc/remove_and_refresh(atom/movable/gone) - SIGNAL_HANDLER - - for(var/mob/user as anything in is_using) - if (user.hud_used?.screen_groups[HUD_GROUP_STORAGE]) - user.hud_used.screen_groups[HUD_GROUP_STORAGE] -= gone - - if(!user.client) - continue - - var/client/cuser = user.client - cuser.screen -= gone - - reset_item(gone) - refresh_views() - /// Signal handler for emp_act to emp all contents /datum/storage/proc/on_emp_act(datum/source, severity, protection) SIGNAL_HANDLER @@ -780,16 +776,21 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) var/datum/progressbar/progress = new(user, amount, thing.loc) var/list/rejections = list() + var/list/success = list(0) + INVOKE_ASYNC(src, PROC_REF(collect_on_turf_loop), thing.loc, user, progress, rejections, collection_mode == COLLECT_SAME ? thing.type : null, success) - while(do_after(user, 1 SECONDS, parent, NONE, FALSE, CALLBACK(src, PROC_REF(handle_mass_pickup), user, pick_up.Copy(), thing.loc, rejections, progress))) - stoplag(1) +/datum/storage/proc/collect_on_turf_loop(atom/holder, mob/user, datum/progressbar/progress, list/rejections, pick_up_type, list/success) + if (do_after(user, 1 SECONDS, parent, NONE, FALSE, CALLBACK(src, PROC_REF(handle_mass_pickup), user, pick_up_type, holder, rejections, progress, success))) + INVOKE_ASYNC(src, PROC_REF(collect_on_turf_loop), holder, user, progress, rejections, pick_up_type, success) + return progress.end_progress() - // If nothing was actually removed, don't send the pickup message - var/list/current_contents = holder.contents.Copy() - if(length(pick_up | current_contents) == length(current_contents)) - return - parent.balloon_alert(user, "picked up") + if(success[1]) + if(animated) + animate_parent() + if(do_rustle && rustle_sound) + playsound(parent, rustle_sound, 50, TRUE, -5) + parent.balloon_alert(user, "picked up") /// Signal handler for whenever we drag the storage somewhere. /datum/storage/proc/on_mousedrop_onto(datum/source, atom/over_object, mob/user) @@ -1065,7 +1066,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) if(!isobserver(to_show) && !display_contents) return FALSE - if(to_show.active_storage != src && (to_show.stat == CONSCIOUS)) + if(to_show.active_storage != src && (!IS_UNCONSCIOUS_OR_CRIT(to_show))) for(var/obj/item/thing in real_location) if(thing.on_found(to_show)) to_show.active_storage?.hide_contents(to_show) diff --git a/code/datums/storage/subtypes/belts.dm b/code/datums/storage/subtypes/belts.dm index 4eb14f3f6910..70acb7af3a12 100644 --- a/code/datums/storage/subtypes/belts.dm +++ b/code/datums/storage/subtypes/belts.dm @@ -387,8 +387,8 @@ /obj/item/clothing/neck/heretic_focus, /obj/item/codex_cicatrix, /obj/item/eldritch_potion, - /obj/item/food/grown/poppy, // Used to regain a Living Heart. - /obj/item/food/grown/harebell, // Used to reroll targets + /obj/item/food/grown/flower/poppy, // Used to regain a Living Heart. + /obj/item/food/grown/flower/harebell, // Used to reroll targets /obj/item/melee/rune_carver, /obj/item/melee/sickly_blade, /obj/item/organ, // Organs are also often used in rituals. diff --git a/code/datums/storage/subtypes/pockets.dm b/code/datums/storage/subtypes/pockets.dm index 65941cdad10d..26a21f57c077 100644 --- a/code/datums/storage/subtypes/pockets.dm +++ b/code/datums/storage/subtypes/pockets.dm @@ -236,7 +236,7 @@ /obj/item/clothing/neck/heretic_focus, /obj/item/codex_cicatrix, /obj/item/eldritch_potion, - /obj/item/food/grown/poppy, // Used to regain a Living Heart. + /obj/item/food/grown/flower/poppy, // Used to regain a Living Heart. /obj/item/melee/rune_carver, /obj/item/melee/sickly_blade, // Normal sized, so you can only fit one. /obj/item/organ, // Organs are also often used in rituals. diff --git a/code/datums/timed_actions.dm b/code/datums/timed_actions.dm new file mode 100644 index 000000000000..45de6081a597 --- /dev/null +++ b/code/datums/timed_actions.dm @@ -0,0 +1,256 @@ +#define ACTION_WORKING 0 +#define ACTION_FAILED 1 +#define ACTION_SUCCEEDED 2 + +/datum/timed_action + /// Atom performing the action + var/atom/movable/user + /// Atoms on which the action is performed + var/list/targets + /// Progress bar displayed to the user/override + var/datum/progressbar/progressbar + /// Cog visual displayed to everyone else + var/datum/cogbar/cogbar + /// Callback to invoke each tick to check for condition validity + var/datum/callback/extra_checks + // Start and end world.tick for the action + var/start_time + var/end_time + /// Flags of our action + var/timed_action_flags = NONE + /// Status of the action to pass into the main wait loop + var/status = ACTION_WORKING + +/datum/timed_action/New(atom/movable/user, list/targets, delay, show_progress = TRUE, timed_action_flags = NONE, datum/callback/extra_checks = null, cog_icon = null, cog_iconstate = null, mob/bar_override = null) + . = ..() + src.user = user + src.timed_action_flags = timed_action_flags + src.extra_checks = extra_checks + if (isnull(targets)) + targets = list(user) + else if (!islist(targets)) + targets = list(targets) + + src.targets = targets + + if (show_progress) + if(astype(user, /mob)?.client || bar_override?.client) + progressbar = new(bar_override || user, delay, targets[1] || user) + + if(!isnull(cog_icon) && delay >= 1 SECONDS) + cogbar = new(user, cog_icon, cog_iconstate) + +#ifdef UNIT_TESTS + timed_action_flags &= ~IGNORE_SLOWDOWNS // Test dummies are a special case +#endif + + if (!ismob(user)) + timed_action_flags |= IGNORE_HELD_ITEM | IGNORE_INCAPACITATED | IGNORE_SLOWDOWNS + else if(!(timed_action_flags & IGNORE_SLOWDOWNS)) + delay *= astype(user, /mob).cached_multiplicative_actions_slowdown + + start_time = world.time + end_time = world.time + delay + + register_signals() + +/datum/timed_action/Destroy(force) + user = null + targets = null + // Only qdel these two in case of an await() runtime/early deletion/whatever, otherwise let them play out their animation and self-delete + if (status == ACTION_WORKING) + qdel(progressbar) + qdel(cogbar) + progressbar = null + cogbar = null + extra_checks = null + STOP_PROCESSING(SStimed_actions, src) + status = ACTION_FAILED + return ..() + +/datum/timed_action/proc/register_signals() + RegisterSignal(user, COMSIG_QDELETING, PROC_REF(on_user_deleted)) + + if (!(timed_action_flags & IGNORE_USER_LOC_CHANGE)) + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_user_moved)) + + if (!(timed_action_flags & IGNORE_INCAPACITATED)) + RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED), PROC_REF(on_user_incapacitated)) + + if (timed_action_flags & DO_AFTER_CHECK_NEXT_MOVE) + RegisterSignal(user, COMSIG_LIVING_CHANGENEXT_MOVE, PROC_REF(on_changenext_move)) + + if (!(timed_action_flags & IGNORE_HELD_ITEM)) + RegisterSignal(user, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(on_item_equipped)) + RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, PROC_REF(on_hands_swapped)) + RegisterSignal(user, COMSIG_MOB_UNEQUIPPED_ITEM, PROC_REF(on_item_dropped)) + + for (var/atom/target as anything in targets) + if (target == user) + continue + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_deleted)) + if (!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE)) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_target_moved)) + +/datum/timed_action/proc/cancel() + if (status != ACTION_WORKING) + return FALSE + + status = ACTION_FAILED + STOP_PROCESSING(SStimed_actions, src) + return TRUE + +/datum/timed_action/proc/await(delay = world.tick_lag) + START_PROCESSING(SStimed_actions, src) + status = ACTION_WORKING + + while (status == ACTION_WORKING && world.time < end_time) + sleep(world.tick_lag) + + if (status == ACTION_WORKING) // Due to how MC handles sleeping, await will tick first before the subsystem itself, so we need to tick ourselves one last time if we haven't been aborted + process() + + . = (status == ACTION_SUCCEEDED) + + if (!QDELETED(progressbar)) + progressbar.end_progress() + if (!QDELETED(cogbar)) + cogbar.remove() + + qdel(src) + +/datum/timed_action/process(seconds_per_tick) + if (extra_checks && !extra_checks.InvokeAsync()) + cancel() + return + + if (world.time >= end_time) + status = ACTION_SUCCEEDED + return PROCESS_KILL + + if(!QDELETED(progressbar)) + progressbar.update(world.time - start_time) + +/datum/timed_action/proc/on_user_deleted(datum/source) + SIGNAL_HANDLER + user = null + cancel() + +/datum/timed_action/proc/on_target_deleted(datum/source) + SIGNAL_HANDLER + targets -= source + cancel() + +/datum/timed_action/proc/on_user_incapacitated(datum/source) + SIGNAL_HANDLER + cancel() + +/datum/timed_action/proc/on_changenext_move(datum/source, next_move, delay) + SIGNAL_HANDLER + if (next_move > world.time) + cancel() + +/datum/timed_action/proc/on_item_equipped(mob/source, obj/item/item, slot) + SIGNAL_HANDLER + // We picked up an item + if (item == source.get_active_held_item()) + cancel() + +/datum/timed_action/proc/on_hands_swapped(datum/source) + SIGNAL_HANDLER + cancel() + +/datum/timed_action/proc/on_item_dropped(mob/source, obj/item/item_dropping, force, atom/newloc, no_move, invdrop, silent, hand_index) + SIGNAL_HANDLER + // Dropped held item + if (source.active_hand_index == hand_index) + cancel() + +/datum/timed_action/proc/on_user_moved(datum/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + + if (user.loc == old_loc) + return + + if (isnull(user.drift_handler)) + cancel() + return + + for (var/atom/target as anything in targets) + if (!target.Adjacent(user)) + cancel() + return + +/datum/timed_action/proc/on_target_moved(atom/movable/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + + if (source.loc == old_loc) + return + + if (isnull(source.drift_handler) || !source.Adjacent(user)) + cancel() + +/** + * Timed action involving one mob user. Target is optional. + * + * Checks that `user` does not move, change hands, get stunned, etc. for the + * given `delay`. Returns `TRUE` on success or `FALSE` on failure. + * + * - user - The mob performing the action. + * - delay - The time in deciseconds. Use the SECONDS define for readability. `1 SECONDS` is 10 deciseconds. + * - target - The target of the action. This is where the progressbar will display. + * - timed_action_flags - Flags to control the behavior of the timed action. + * - show_progress - Whether to display a progress bar / cogbar. + * - extra_checks - Additional checks to perform before the action is executed. + * - interaction_key - The assoc key under which the do_after is capped, with max_interact_count being the cap. Interaction key will default to target if not set. + * - max_interact_count - The maximum amount of interactions allowed. + * - cog_icon - The icon file of the cog. Default: 'icons/effects/progressbar.dmi' + * - cog_iconstate - The icon state of the cog. Default: "Cog" + * - bar_override - Mob which should see the bar instead of the user + */ +/proc/do_after(atom/movable/user, delay, atom/target, timed_action_flags = NONE, show_progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, cog_icon = 'icons/effects/progressbar.dmi', cog_iconstate = "cog", mob/bar_override = null) + if (!user) + return FALSE + + ASSERT(isnum(delay), "do_after was passed a non-number delay: [delay || "null"].") + ASSERT(!isnum(target), "a do_after created by [user] had a target set as [target] - probably intended to be the time instead.") + ASSERT(!isatom(delay), "a do_after created by [user] had a timer of [delay] - probably intended to be the target instead.") + + if (delay <= 0) + return TRUE + + if(!interaction_key && ismob(user)) + if(!islist(target)) + interaction_key = target + else + var/list/temp = list() + for(var/atom/atom as anything in target) + temp += ref(atom) + + sortTim(temp, GLOBAL_PROC_REF(cmp_text_asc)) + interaction_key = jointext(temp, "-") + + if(interaction_key && ismob(user)) // Do we have a interaction_key now? + var/mob/as_mob = user + var/current_interaction_count = LAZYACCESS(as_mob.do_afters, interaction_key) + if(current_interaction_count >= max_interact_count) // We are at our peak + return + LAZYSET(as_mob.do_afters, interaction_key, current_interaction_count + 1) + + SEND_SIGNAL(user, COMSIG_DO_AFTER_BEGAN) + + var/datum/timed_action/action = new(user, target, delay, show_progress, timed_action_flags, extra_checks, cog_icon, cog_iconstate, bar_override) + . = action.await() + + if(interaction_key && ismob(user)) + var/mob/as_mob = user + var/reduced_interaction_count = LAZYACCESS(as_mob.do_afters, interaction_key) + if(reduced_interaction_count > 1) // Not done yet! + LAZYSET(as_mob.do_afters, interaction_key, reduced_interaction_count - 1) + else + LAZYREMOVE(as_mob.do_afters, interaction_key) + SEND_SIGNAL(user, COMSIG_DO_AFTER_ENDED) + +#undef ACTION_WORKING +#undef ACTION_FAILED +#undef ACTION_SUCCEEDED diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 739b5e8adecd..c7bb5bd58908 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -329,7 +329,7 @@ else to_chat(user, span_notice("You glance at [photo], looking for wires in the pictured blueprints.")) - if(do_after(user, study_length, holder, interaction_key = STUDY_INTERACTION_KEY, hidden = TRUE)) + if(do_after(user, study_length, holder, interaction_key = STUDY_INTERACTION_KEY, cog_icon = null)) LAZYSET(studied_photos, REF(user.mind), REF(photo)) return diff --git a/code/datums/wounds/cranial_fissure.dm b/code/datums/wounds/cranial_fissure.dm index 9a2fd0eb4399..22f0f0d869b8 100644 --- a/code/datums/wounds/cranial_fissure.dm +++ b/code/datums/wounds/cranial_fissure.dm @@ -15,7 +15,7 @@ if (isnull(limb.owner)) return ..() - if (HAS_TRAIT(limb.owner, TRAIT_CURSED) && (limb.get_mangled_state() & BODYPART_MANGLED_INTERIOR)) + if (HAS_TRAIT(limb.owner, TRAIT_CURSED) && (limb.mangled_state & BODYPART_MANGLED_INTERIOR)) return ..() if (limb.owner.stat >= HARD_CRIT) diff --git a/code/game/area/areas/ruins/space.dm b/code/game/area/areas/ruins/space.dm index b8dd7c662508..cf2600cbee53 100644 --- a/code/game/area/areas/ruins/space.dm +++ b/code/game/area/areas/ruins/space.dm @@ -789,3 +789,23 @@ //Kilostation /area/ruin/space/has_grav/abandoned_warehouse name = "\improper Abandoned Warehouse" + +//Syndicate Outpost "ruin" for the station z-level +/area/ruin/space/has_grav/syndicate_outpost + name = "\improper Syndicate Outpost" + icon_state = "syndicate_outpost" + area_flags = HIDDEN_AREA | EVENT_PROTECTED + ambience_index = AMBIENCE_GENERIC + // Same as tcommsat + ambientsounds = list( + 'sound/ambience/engineering/ambisin2.ogg', + 'sound/ambience/misc/signal.ogg', + 'sound/ambience/misc/signal.ogg', + 'sound/ambience/general/ambigen9.ogg', + 'sound/ambience/engineering/ambitech.ogg', + 'sound/ambience/engineering/ambitech2.ogg', + 'sound/ambience/engineering/ambitech3.ogg', + 'sound/ambience/misc/ambimystery.ogg', + ) + flags_1 = NONE // Not dirty + skip_minimap_rendering = FALSE diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm index 9a651fb5e3dc..8ffba209c595 100644 --- a/code/game/area/areas/shuttles.dm +++ b/code/game/area/areas/shuttles.dm @@ -193,6 +193,9 @@ /area/shuttle/escape/engine name = "Escape Shuttle Engine" +/area/shuttle/escape/tombstone + ambience_index = AMBIENCE_SPOOKY + /area/shuttle/transport name = "Transport Shuttle" diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 5f703f9e6b63..b7b17bdbd11a 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -397,11 +397,8 @@ return null ///Return the current air environment in this atom -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null +/atom/proc/return_air() as /datum/gas_mixture + return loc?.return_air() ///Return the air if we can analyze it /atom/proc/return_analyzable_air() diff --git a/code/game/atom/atom_act.dm b/code/game/atom/atom_act.dm index 233060015dc6..69a4ec6c7840 100644 --- a/code/game/atom/atom_act.dm +++ b/code/game/atom/atom_act.dm @@ -233,12 +233,13 @@ * rust_strength (optional) - if you want to vary the effect based on the users' strength */ /atom/proc/rust_heretic_act(rust_strength) - return + return FALSE ///wrapper proc that passes our mob's rust_strength to the target we are rusting /mob/living/proc/do_rust_heretic_act(atom/target) var/datum/antagonist/heretic/heretic_data = GET_HERETIC(src) - target.rust_heretic_act(heretic_data?.rust_strength) + var/result = target.rust_heretic_act(heretic_data?.rust_strength) + SEND_SIGNAL(src, COMSIG_MOB_RUST_HERETIC_ACT, target, result) ///Called when something resists while this atom is its loc /atom/proc/container_resist_act(mob/living/user) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index fc38c768c7f6..1dd0cb070461 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -553,28 +553,67 @@ if(pulled_atom.pulledby) log_combat(pulled_atom, pulled_atom.pulledby, "pulled from", src) pulled_atom.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. - pulling = pulled_atom - pulled_atom.set_pulledby(src) + set_pulling(pulled_atom) SEND_SIGNAL(src, COMSIG_ATOM_START_PULL, pulled_atom, state, force) setGrabState(state) if(ismob(pulled_atom)) var/mob/pulled_mob = pulled_atom - log_combat(src, pulled_mob, "grabbed", addition="passive grab") + log_combat(src, pulled_mob, "grabbed", addition = "passive grab") if(!supress_message) - pulled_mob.visible_message(span_warning("[src] grabs [pulled_mob] passively."), \ - span_danger("[src] grabs you passively.")) + pulled_mob.visible_message( + span_warning("[src] grabs [pulled_mob] passively."), + span_danger("[src] grabs you passively."), + ) + + return TRUE /atom/movable/proc/stop_pulling() if(!pulling) return - pulling.set_pulledby(null) - setGrabState(GRAB_PASSIVE) - var/atom/movable/old_pulling = pulling - pulling = null + var/atom/movable/old_pulling = set_pulling(null) SEND_SIGNAL(old_pulling, COMSIG_ATOM_NO_LONGER_PULLED, src) SEND_SIGNAL(src, COMSIG_ATOM_NO_LONGER_PULLING, old_pulling) +#define PULLED_WHILE_CRIT_TRAIT "pulled_while_softcrit" + +/atom/movable/proc/pulled_mob_stat_change(mob/living/pulled_mob, new_stat, ...) + SIGNAL_HANDLER + + if(new_stat >= SOFT_CRIT) + ADD_TRAIT(pulled_mob, TRAIT_IMMOBILIZED, PULLED_WHILE_CRIT_TRAIT) + else + REMOVE_TRAIT(pulled_mob, TRAIT_IMMOBILIZED, PULLED_WHILE_CRIT_TRAIT) + +/atom/movable/proc/set_pulling(new_pulling) + if(new_pulling == pulling) + return FALSE //null signals there was a change, be sure to return FALSE if none happened here. + + if(isnull(new_pulling)) + setGrabState(GRAB_PASSIVE) + + . = pulling + pulling = new_pulling + + if(ismovable(.)) + var/atom/movable/was_pulling = . + was_pulling.set_pulledby(null) + + if(ismovable(new_pulling)) + var/atom/movable/pulled_thing = new_pulling + pulled_thing.set_pulledby(src) + + if(ismob(.)) + var/mob/was_pulled_mob = . + UnregisterSignal(was_pulled_mob, COMSIG_MOB_STATCHANGE) + REMOVE_TRAIT(was_pulled_mob, TRAIT_IMMOBILIZED, PULLED_WHILE_CRIT_TRAIT) + + if(ismob(pulling)) + var/mob/pulled_mob = pulling + RegisterSignal(pulling, COMSIG_MOB_STATCHANGE, PROC_REF(pulled_mob_stat_change)) + if(pulled_mob.stat >= SOFT_CRIT) + ADD_TRAIT(pulled_mob, TRAIT_IMMOBILIZED, PULLED_WHILE_CRIT_TRAIT) + ///Reports the event of the change in value of the pulledby variable. /atom/movable/proc/set_pulledby(new_pulledby) if(new_pulledby == pulledby) @@ -582,6 +621,7 @@ . = pulledby pulledby = new_pulledby +#undef PULLED_WHILE_CRIT_TRAIT /atom/movable/proc/Move_Pulled(atom/moving_atom) if(!pulling) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 4e0bdb2873a9..a579e8e945cf 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -105,7 +105,7 @@ Medical HUD! Basic mode needs suit sensors on. //helper for getting the appropriate health status /proc/RoundHealth(mob/living/M) - if(M.stat == DEAD || (HAS_TRAIT(M, TRAIT_FAKEDEATH))) + if(IS_DEAD_OR_FAKING(M)) return "health-100" //what's our health? it doesn't matter, we're dead, or faking var/maxi_health = M.maxHealth if(iscarbon(M) && M.health < 0) @@ -177,7 +177,7 @@ Medical HUD! Basic mode needs suit sensors on. // Called when a carbon changes stat, virus or XENO_HOST // Returns TRUE if the mob is considered "perfectly healthy", FALSE otherwise /mob/living/proc/med_hud_set_status() - if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) + if(IS_DEAD_OR_FAKING(src)) set_hud_image_state(STATUS_HUD, "huddead") return FALSE @@ -189,7 +189,7 @@ Medical HUD! Basic mode needs suit sensors on. set_hud_image_state(STATUS_HUD, "hudxeno") return FALSE - if(!appears_alive()) + if(IS_DEAD_OR_FAKING(src)) if(can_defib_client()) set_hud_image_state(STATUS_HUD, "huddefib") else if(HAS_TRAIT(src, TRAIT_GHOSTROLE_ON_REVIVE)) @@ -375,13 +375,13 @@ Diagnostic HUDs! set_hud_image_state(DIAG_HUD, "huddiag[RoundDiagBar(health/maxHealth)]") /mob/living/silicon/proc/diag_hud_set_status() - switch(stat) - if(CONSCIOUS) - set_hud_image_state(DIAG_STAT_HUD, "hudstat") - if(UNCONSCIOUS, HARD_CRIT) - set_hud_image_state(DIAG_STAT_HUD, "hudoffline") - else - set_hud_image_state(DIAG_STAT_HUD, "huddead2") + if(IS_UNCONSCIOUS(src)) + set_hud_image_state(DIAG_STAT_HUD, "hudoffline") + return + if(stat == DEAD) + set_hud_image_state(DIAG_STAT_HUD, "huddead2") + return + set_hud_image_state(DIAG_STAT_HUD, "hudstat") //Borgie battery tracking! /mob/living/silicon/robot/proc/diag_hud_set_borgcell() diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index a0fcedaa2d42..0eb2d1e42db2 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -1135,6 +1135,9 @@ var/list/part_list = replacer_tool.get_sorted_parts(ignore_stacks = TRUE) if(!part_list.len) return FALSE + + replacer_tool.atom_storage.block_insert_remove_updates = TRUE + var/update_storage = FALSE for(var/primary_part_base in component_parts) //we exchanged all we could time to bail if(!part_list.len) @@ -1167,41 +1170,48 @@ // If it's rigged or corrupted, max the charge. Then explode it. if(checked_cell.try_explode(max_charge = TRUE)) break - if(secondary_part.get_part_rating() > current_rating) - //store name of part incase we qdel it below - var/secondary_part_name = secondary_part.name - if(replacer_tool.atom_storage.attempt_remove(secondary_part, src)) - if (istype(primary_part_base, /datum/stock_part)) - var/stock_part_datum = GLOB.stock_part_datums_per_object[secondary_part.type] - if (isnull(stock_part_datum)) - CRASH("[secondary_part] ([secondary_part.type]) did not have a stock part datum (was trying to find [primary_part_base])") - component_parts += stock_part_datum - part_list -= secondary_part //have to manually remove cause we are no longer refering replacer_tool.contents - qdel(secondary_part) - else - component_parts += secondary_part - secondary_part.forceMove(src) - part_list -= secondary_part //have to manually remove cause we are no longer refering replacer_tool.contents - - component_parts -= primary_part_base - - var/obj/physical_part + if(secondary_part.get_part_rating() <= current_rating) + continue + //store name of part incase we qdel it below + var/secondary_part_name = secondary_part.name + if(replacer_tool.atom_storage.attempt_remove(secondary_part, src, silent = TRUE, visual_updates = FALSE)) + update_storage = TRUE if (istype(primary_part_base, /datum/stock_part)) - var/datum/stock_part/stock_part_datum = primary_part_base - var/physical_object_type = stock_part_datum.physical_object_type - physical_part = new physical_object_type + var/stock_part_datum = GLOB.stock_part_datums_per_object[secondary_part.type] + if (isnull(stock_part_datum)) + CRASH("[secondary_part] ([secondary_part.type]) did not have a stock part datum (was trying to find [primary_part_base])") + component_parts += stock_part_datum + part_list -= secondary_part //have to manually remove cause we are no longer refering replacer_tool.contents + qdel(secondary_part) else - physical_part = primary_part_base - - replacer_tool.atom_storage.attempt_insert(physical_part, user, TRUE, force = STORAGE_SOFT_LOCKED) - to_chat(user, span_notice("[capitalize(physical_part.name)] replaced with [secondary_part_name].")) - shouldplaysound = TRUE //Only play the sound when parts are actually replaced! - break + component_parts += secondary_part + secondary_part.forceMove(src) + part_list -= secondary_part //have to manually remove cause we are no longer refering replacer_tool.contents + + component_parts -= primary_part_base + + var/obj/physical_part + if (istype(primary_part_base, /datum/stock_part)) + var/datum/stock_part/stock_part_datum = primary_part_base + var/physical_object_type = stock_part_datum.physical_object_type + physical_part = new physical_object_type + else + physical_part = primary_part_base + + replacer_tool.atom_storage.attempt_insert(physical_part, user, override = TRUE, force = STORAGE_SOFT_LOCKED, messages = FALSE) + to_chat(user, span_notice("[capitalize(physical_part.name)] replaced with [secondary_part_name].")) + shouldplaysound = TRUE //Only play the sound when parts are actually replaced! + break RefreshParts() if(shouldplaysound) replacer_tool.play_rped_effect() + if(update_storage) + replacer_tool.atom_storage.refresh_views() + replacer_tool.update_appearance() + replacer_tool.atom_storage.block_insert_remove_updates = FALSE + return TRUE /obj/machinery/proc/display_parts(mob/user) @@ -1317,6 +1327,7 @@ /obj/machinery/rust_heretic_act(rust_strength) var/damage = 500 + rust_strength * 200 take_damage(damage, BRUTE, BOMB, 1) + return TRUE /obj/machinery/vv_edit_var(vname, vval) if(vname == NAMEOF(src, occupant)) diff --git a/code/game/machinery/ai_core/_core.dm b/code/game/machinery/ai_core/_core.dm index 6c617b8a65b6..9de9b36be582 100644 --- a/code/game/machinery/ai_core/_core.dm +++ b/code/game/machinery/ai_core/_core.dm @@ -11,15 +11,13 @@ max_integrity = 500 custom_materials = list(/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 4) var/state = CORE_STATE_EMPTY - var/datum/ai_laws/laws var/obj/item/circuitboard/aicore/circuit var/obj/item/mmi/core_mmi + /// Weakref to an ai module rack, if present we will try to link new AI to it instead of the station core + var/datum/weakref/default_link_ref /obj/structure/ai_core/Initialize(mapload, state = src.state, obj/item/mmi/core_mmi = null) . = ..() - laws = new - laws.set_laws_config() - if(core_mmi && state < CORE_STATE_CABLED) stack_trace("supplied a core_mmi as constructor argument, but core state wouldn't have accepted it!") state = CORE_STATE_FINISHED // just in case... @@ -86,12 +84,13 @@ /obj/structure/ai_core/Destroy() QDEL_NULL(circuit) QDEL_NULL(core_mmi) - QDEL_NULL(laws) return ..() /obj/structure/ai_core/examine(mob/user) . = ..() . += span_notice("It has some bolts that look [anchored ? "tightened" : "loosened"].") + . += span_notice("Save this core to a multitool's buffer and upload it to a module rack to automatically link it on first boot. \ + Otherwise, it will link[is_station_level(z) ? " to the station's core rack or" : ""] to the first rack found.") switch(state) if(CORE_STATE_EMPTY) @@ -101,17 +100,14 @@ if(CORE_STATE_SCREWED) . += span_notice("The frame can be wired, the circuit board can be unfastened.") if(CORE_STATE_CABLED) - if(!core_mmi) - . += span_notice("There are wires which could be hooked up to an MMI or positronic brain, or cut.") + if(core_mmi) + . += span_notice("There is a slot for a reinforced glass panel, the [AI_CORE_BRAIN(core_mmi)] could be pried out.") else - var/accept_laws = TRUE - if(core_mmi.laws.id != DEFAULT_AI_LAWID || !core_mmi.brainmob || !core_mmi.brainmob?.mind) - accept_laws = FALSE - . += span_notice("There is a slot for a reinforced glass panel, the [AI_CORE_BRAIN(core_mmi)] could be pried out.[accept_laws ? " A law module can be swiped across." : ""]") + . += span_notice("There are wires which could be hooked up to an MMI or positronic brain, or cut.") if(CORE_STATE_GLASSED) - . += span_notice("The monitor [core_mmi?.brainmob?.mind && !suicide_check() ? "and neural interface " : ""]can be screwed in, the panel can be pried out.") + . += span_notice("The monitor [(core_mmi?.brainmob?.mind && !suicide_check()) ? "and neural interface " : ""]can be screwed in, the panel can be pried out.") if(CORE_STATE_FINISHED) - . += span_notice("The monitor's connection can be cut[core_mmi?.brainmob?.mind && !suicide_check() ? " the neural interface can be screwed in." : "."]") + . += span_notice("The monitor's connection can be cut[(core_mmi?.brainmob?.mind && !suicide_check()) ? " the neural interface can be screwed in." : "."]") /obj/structure/ai_core/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(state < CORE_STATE_FINISHED) @@ -142,8 +138,8 @@ /obj/structure/ai_core/latejoin_inactive/examine(mob/user) . = ..() - . += "Its transmitter seems to be [active? "on" : "off"]." - . += span_notice("You could [active? "deactivate" : "activate"] it with a multitool.") + . += span_info("Its transmitter seems to be [active? "on" : "off"].") + . += span_notice("You could [active ? "deactivate" : "activate"] it by [EXAMINE_HINT("right clicking")] with a multitool.") /obj/structure/ai_core/latejoin_inactive/proc/is_available() //If people still manage to use this feature to spawn-kill AI latejoins ahelp them. if(!available) @@ -164,10 +160,7 @@ return FALSE return TRUE -/obj/structure/ai_core/latejoin_inactive/multitool_act(mob/living/user, obj/item/tool) - if(user.combat_mode) - return NONE - +/obj/structure/ai_core/latejoin_inactive/multitool_act_secondary(mob/living/user, obj/item/tool) if(!tool.use_tool(src, user, 0 SECONDS, 0, 50)) return ITEM_INTERACT_BLOCKING @@ -175,6 +168,12 @@ balloon_alert(user, "[active ? "activated" : "deactivated"] transmitters") return ITEM_INTERACT_SUCCESS +/obj/structure/ai_core/multitool_act(mob/living/user, obj/item/multitool/tool) + tool.play_tool_sound(src, 50) + tool.set_buffer(src) + balloon_alert(user, "core saved to buffer") + return ITEM_INTERACT_SUCCESS + /obj/structure/ai_core/proc/ai_structure_to_mob() var/mob/living/brain/the_brainmob = core_mmi.brainmob if(!the_brainmob.mind || suicide_check()) @@ -182,21 +181,9 @@ the_brainmob.mind.remove_antags_for_borging() if(!the_brainmob.mind.has_ever_been_ai) SSblackbox.record_feedback("amount", "ais_created", 1) - var/mob/living/silicon/ai/ai_mob = null - - if(core_mmi.overrides_aicore_laws) - ai_mob = new /mob/living/silicon/ai(loc, core_mmi.laws, the_brainmob) - core_mmi.laws = null //MMI's law datum is being donated, so we need the MMI to let it go or the GC will eat it - else - ai_mob = new /mob/living/silicon/ai(loc, laws, the_brainmob) - laws = null //we're giving the new AI this datum, so let's not delete it when we qdel(src) 5 lines from now - var/datum/antagonist/malf_ai/malf_datum = IS_MALF_AI(ai_mob) - if(malf_datum) - malf_datum.add_law_zero() + var/mob/living/silicon/ai/ai_mob = new(loc, the_brainmob, core_mmi.laws, default_link_ref?.resolve()) - if(!isnull(the_brainmob.client)) - ai_mob.set_gender(the_brainmob.client) if(core_mmi.force_replace_ai_name) ai_mob.fully_replace_character_name(ai_mob.name, core_mmi.replacement_ai_name()) ai_mob.posibrain_inside = core_mmi.braintype == "Android" @@ -251,7 +238,8 @@ That prevents a few funky behaviors. to_chat(user, span_alert("There is no AI loaded on this terminal.")) /obj/item/circuitboard/aicore - name = "AI core (AI Core Board)" //Well, duh, but best to be consistent + name = "AI Core" + name_extension = "(AI Core Board)" //Well, duh, but best to be consistent var/battery = 200 //backup battery for when the AI loses power. Copied to/from AI mobs when carding, and placed here to avoid recharge via deconning the core /obj/item/circuitboard/aicore/Initialize(mapload) diff --git a/code/game/machinery/ai_core/core_construction.dm b/code/game/machinery/ai_core/core_construction.dm index df1a36782daa..97a61aa1974d 100644 --- a/code/game/machinery/ai_core/core_construction.dm +++ b/code/game/machinery/ai_core/core_construction.dm @@ -16,14 +16,22 @@ deconstruct(TRUE) return ITEM_INTERACT_SUCCESS -/obj/structure/ai_core/wrench_act(mob/living/user, obj/item/tool) +/obj/structure/ai_core/can_be_unfasten_wrench(mob/user, silent) if(state >= CORE_STATE_FINISHED) - set_anchored(TRUE) //teehee - balloon_alert(user, "can't unanchor!") - return ITEM_INTERACT_BLOCKING + if(!silent) + balloon_alert(user, "can't be unanchored while operational!") + return FAILED_UNFASTEN - default_unfasten_wrench(user, tool) - return ITEM_INTERACT_SUCCESS + return ..() + +/obj/structure/ai_core/wrench_act(mob/living/user, obj/item/tool) + switch(default_unfasten_wrench(user, tool)) + if(FAILED_UNFASTEN) + return ITEM_INTERACT_BLOCKING + if(SUCCESSFUL_UNFASTEN) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/structure/ai_core/screwdriver_act(mob/living/user, obj/item/tool) switch(state) @@ -155,8 +163,6 @@ if(istype(tool, /obj/item/mmi)) return install_mmi(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING - if(istype(tool, /obj/item/ai_module)) - return update_laws(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING if(istype(tool, /obj/item/stack/sheet/rglass)) return install_glass(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING @@ -217,20 +223,6 @@ UPDATE_STATE(CORE_STATE_CABLED) return TRUE -/obj/structure/ai_core/proc/update_laws(mob/living/user, obj/item/ai_module/module) - if(!core_mmi) - balloon_alert(user, "no brain installed!") - return FALSE - if(!core_mmi.brainmob || !core_mmi.brainmob?.mind || suicide_check()) - balloon_alert(user, "[AI_CORE_BRAIN(core_mmi)] is inactive!") - return FALSE - if(core_mmi.laws.id != DEFAULT_AI_LAWID) - balloon_alert(user, "[AI_CORE_BRAIN(core_mmi)] already has set laws!") - return FALSE - - module.install(laws, user) - return TRUE - /obj/structure/ai_core/proc/install_glass(mob/living/user, obj/item/stack/sheet/rglass/glass) if(state != CORE_STATE_CABLED) return FALSE diff --git a/code/game/machinery/ai_core/law.dm b/code/game/machinery/ai_core/law.dm new file mode 100644 index 000000000000..99d68227f4b1 --- /dev/null +++ b/code/game/machinery/ai_core/law.dm @@ -0,0 +1,1066 @@ +#define MODULE_UNSECURED 0 +#define MODULE_SCREWED 1 +#define MODULE_WELDED 2 + +/// Formats a module name for display +#define MODULE_NAME(all_modules, named_module) (all_modules[named_module] == MODULE_WELDED ? "unidentified welded module" : (named_module?.name || "Empty")) + +/datum/armor/obj_machinery/law_rack + melee = 50 + bullet = 30 + laser = 30 + bomb = 50 + fire = 100 + acid = 100 + +/datum/armor/obj_machinery/law_rack/portable + melee = 30 + bullet = 10 + laser = 10 + +/obj/machinery/ai_law_rack + name = "module rack" + desc = "A module rack. This one just holds modules, it doesn't do anything with it." + icon = 'icons/obj/machines/law_rack.dmi' + icon_state = "law_rack" + density = TRUE + armor_type = /datum/armor/obj_machinery/law_rack + max_integrity = 300 + damage_deflection = 12 + appearance_flags = parent_type::appearance_flags | KEEP_TOGETHER + interaction_flags_machine = INTERACT_MACHINE_OFFLINE + anchored = FALSE + drag_slowdown = 1 + + /// How many slots for laws + var/law_slots = 5 + /// Visually, how many slots can we show + var/max_slot_overlays = 5 + /// Pixel_z offset of the first slot (the topmost) + var/first_slot_offset = 18 + /// If TRUE, the first slot slot is dedicated to the core module + var/has_core_slot = FALSE + /// Assoc list of modules insalled in the rack to how secure they are + /// First slot is always reserved for core modules + VAR_FINAL/list/obj/item/ai_module/ai_modules + + /// If we are double secured to the floor + var/secured = FALSE + + /// Cooldown between allowing refreshes via ui + COOLDOWN_DECLARE(refresh_cooldown) + +/obj/machinery/ai_law_rack/Initialize(mapload) + . = ..() + ai_modules = new /list(law_slots) + update_appearance() + if(!mapload) + log_silicon("\A [name] was created at [loc_name(src)].") + message_admins("\A [name] was created at [ADMIN_VERBOSEJMP(src)].") + register_context() + +/obj/machinery/ai_law_rack/Destroy() + ai_modules = null + return ..() + +/obj/machinery/ai_law_rack/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(held_item?.tool_behaviour == TOOL_WRENCH && !secured) + context[SCREENTIP_CONTEXT_LMB] = anchored ? "Unfasten" : "Fasten" + context[SCREENTIP_CONTEXT_RMB] = context[SCREENTIP_CONTEXT_LMB] + . = CONTEXTUAL_SCREENTIP_SET + + if(held_item?.tool_behaviour == TOOL_WIRECUTTER) + context[SCREENTIP_CONTEXT_LMB] = secured ? "Unsecure" : "Secure" + context[SCREENTIP_CONTEXT_RMB] = context[SCREENTIP_CONTEXT_LMB] + . = CONTEXTUAL_SCREENTIP_SET + + if(held_item?.tool_behaviour == TOOL_WELDER) + context[SCREENTIP_CONTEXT_LMB] = "Weld modules (in UI)" + . = CONTEXTUAL_SCREENTIP_SET + + if(held_item?.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "Screw modules (in UI)" + . = CONTEXTUAL_SCREENTIP_SET + + if(held_item?.tool_behaviour == TOOL_MULTITOOL) + context[SCREENTIP_CONTEXT_LMB] = "Repair modules (in UI)" + . = CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/multitool)) + var/obj/item/multitool/tool = held_item + if(istype(tool.buffer, /obj/structure/ai_core)) + context[SCREENTIP_CONTEXT_LMB] = "Link core to rack" // overrides above message + . = CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/ai_module)) + context[SCREENTIP_CONTEXT_LMB] = "Insert module" + . = CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/ai_law_rack/base/on_set_is_operational(old_value) + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + parent_rack?.update_lawset() + +/// To be used in logging to specify the status of the law rack +/obj/machinery/ai_law_rack/proc/log_status() + return "" + +/// Returns a list of all modules that will contribute to the combined lawset +/obj/machinery/ai_law_rack/proc/get_law_affecting_modules() + var/list/affecting_modules = list() + // Filter nulls and non-law modules + for(var/obj/item/ai_module/law/module in ai_modules) + affecting_modules += module + + return affecting_modules + +/// Returns the core module if it exists, otherwise returns null +/obj/machinery/ai_law_rack/proc/get_core_module() + return has_core_slot ? ai_modules[1] : null + +/obj/machinery/ai_law_rack/Exited(atom/movable/gone, direction) + . = ..() + if(gone in ai_modules) + remove_ai_module(gone) + +/obj/machinery/ai_law_rack/dump_inventory_contents() + . = ..() + for(var/obj/item/ai_module/installed in ai_modules) + installed.forceMove(get_turf(src)) + +/obj/machinery/ai_law_rack/proc/can_secure_check(mob/living/user) + if(!anchored) + balloon_alert(user, "fasten it first!") + return FALSE + if(!is_anchorable_floor(loc)) + balloon_alert(user, "nothing to secure to!") + return FALSE + return TRUE + +/obj/machinery/ai_law_rack/can_be_unfasten_wrench(mob/living/user, silent) + if(secured) + if(!silent) + balloon_alert(user, "unsecure it first!") + return FAILED_UNFASTEN + return ..() + +/obj/machinery/ai_law_rack/wrench_act(mob/living/user, obj/item/tool) + if(DOING_INTERACTION_WITH_TARGET(user, src)) + return ITEM_INTERACT_BLOCKING + switch(default_unfasten_wrench(user, tool, 6 SECONDS)) + if(CANT_UNFASTEN) + return NONE + if(FAILED_UNFASTEN) + return ITEM_INTERACT_BLOCKING + if(SUCCESSFUL_UNFASTEN) + balloon_alert_to_viewers("[anchored ? "":"un"]fastened") + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/machinery/ai_law_rack/wrench_act_secondary(mob/living/user, obj/item/tool) + return wrench_act(user, tool) + +/obj/machinery/ai_law_rack/wirecutter_act(mob/living/user, obj/item/tool) + if(DOING_INTERACTION_WITH_TARGET(user, src)) + return ITEM_INTERACT_BLOCKING + if(!can_secure_check(user)) + return ITEM_INTERACT_BLOCKING + balloon_alert_to_viewers("[secured ? "un":""]securing to floor...") + if(!tool.use_tool(src, user, 6 SECONDS, volume = 50, extra_checks = CALLBACK(src, PROC_REF(can_secure_check), user))) + return ITEM_INTERACT_BLOCKING + secured = !secured + balloon_alert_to_viewers("[secured ? "":"un"]secured") + return ITEM_INTERACT_SUCCESS + +/obj/machinery/ai_law_rack/wirecutter_act_secondary(mob/living/user, obj/item/tool) + return wirecutter_act(user, tool) + +/obj/machinery/ai_law_rack/welder_act(mob/living/user, obj/item/tool) + ui_interact(user) + return ITEM_INTERACT_BLOCKING + +/obj/machinery/ai_law_rack/screwdriver_act(mob/living/user, obj/item/tool) + ui_interact(user) + return ITEM_INTERACT_BLOCKING + +/obj/machinery/ai_law_rack/multitool_act(mob/living/user, obj/item/multitool/tool) + if(!istype(tool, /obj/item/multitool) || !istype(tool.buffer, /obj/structure/ai_core)) + ui_interact(user) + return ITEM_INTERACT_BLOCKING + + var/obj/structure/ai_core/to_link = tool.buffer + to_link.default_link_ref = WEAKREF(src) + tool.play_tool_sound(src, 20) + balloon_alert(user, "linked core to rack") + return ITEM_INTERACT_SUCCESS + +/obj/machinery/ai_law_rack/vv_edit_var(var_name, var_value) + . = ..() + if(var_name == NAMEOF(src, law_slots)) + for(var/i in var_value + 1 to ai_modules.len) + var/obj/item/ai_module/installed = ai_modules[i] + installed?.forceMove(get_turf(src)) + + ai_modules.len = var_value + + if(var_name == NAMEOF(src, has_core_slot) && !var_value) + for(var/obj/item/ai_module/law/core/core in ai_modules) + core.forceMove(get_turf(src)) + +/// Range at which you can see all the modules by name +#define LAW_EXAMINE_RANGE 3 + +/obj/machinery/ai_law_rack/examine(mob/user) + . = ..() + if(isAI(user)) + return + + if(!isobserver(user) && get_dist(user, src) > LAW_EXAMINE_RANGE) + . += span_notice("If you got a bit closer, you could probably [EXAMINE_HINT("examine closer")] to see what modules are installed.") + else + . += span_notice("[EXAMINE_HINT("Examine closer")] to see what modules are installed.") + var/filled = 0 + for(var/obj/item/ai_module/module in ai_modules) + filled++ + . += span_info("Otherwise, you can see that [filled] out of [length(ai_modules)] slots are filled.") + if(has_core_slot && isnull(get_core_module())) + . += span_warning("You also note that the core slot is empty!") + if(anchored) + . += span_notice("It is [EXAMINE_HINT("anchored")] to the floor[secured ? " and [EXAMINE_HINT("secured with metal cables")]" : ", but not [EXAMINE_HINT("secured by metal cables")]"].") + +/obj/machinery/ai_law_rack/examine_more(mob/user) + . = ..() + if(isAI(user)) + return + if(!isobserver(user) && get_dist(user, src) > LAW_EXAMINE_RANGE) + . += span_warning("You can't quite make out which modules are installed in [src] from here.") + return + for(var/i in 1 to length(ai_modules)) + . += get_slot_examine(i) + +/obj/machinery/ai_law_rack/proc/get_slot_examine(slot) + var/obj/item/ai_module/module = ai_modules[slot] + var/list/text = list() + text += span_info("• [slot == 1 && has_core_slot ? "Core" : "Slot [slot - 1]"]: [MODULE_NAME(ai_modules, module)]") + if(module) + var/secure_desc = "Bugged (report this)" + switch(ai_modules[module]) + if(MODULE_UNSECURED) + secure_desc = "Unsecured" + if(MODULE_SCREWED) + secure_desc = "Screwed" + if(MODULE_WELDED) + secure_desc = "Welded" + text += span_notice(secure_desc) + if(astype(module, /obj/item/ai_module/law)?.ioned) + text += span_warning("It's smoking.") + return jointext(text, span_info(" - ")) + +#undef LAW_EXAMINE_RANGE + +/obj/machinery/ai_law_rack/update_overlays() + . = ..() + for(var/i in 1 to min(length(ai_modules), max_slot_overlays)) + if(isnull(ai_modules[i])) + continue + // slot 1 is offset 0 - then 1 pixel break, and 1 slot every 2 pixels + var/image/card = image(icon, "pcie") + card.pixel_z = first_slot_offset + ((i == 1 || !has_core_slot) ? 0 : -1) + ((i - 1) * -3) + . += card + +/obj/machinery/ai_law_rack/proc/add_ai_module(obj/item/ai_module/module, slot = 1, security = MODULE_UNSECURED) + ASSERT(istype(module)) + ASSERT(isnum(slot)) + if(slot < 1 || slot > length(ai_modules)) + return FALSE + + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + + module.on_rack_install(src) + for(var/mob/living/silicon/linked_ref in assoc_to_values(parent_rack?.linked_mobs)) + module.silicon_linked_to_installed(linked_ref) + ai_modules[slot] = module + ai_modules[module] = security + update_appearance() + parent_rack?.update_lawset() + return TRUE + +/obj/machinery/ai_law_rack/proc/remove_ai_module(obj/item/ai_module/module) + ASSERT(istype(module)) + var/index = ai_modules.Find(module) + if(index == 0 || isnull(ai_modules[index])) + return FALSE + + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + + ai_modules[index] = null + module.on_rack_uninstall(src) + for(var/mob/living/silicon/linked_ref in assoc_to_values(parent_rack?.linked_mobs)) + module.silicon_unlinked_from_installed(linked_ref) + if(!QDELING(src)) + update_appearance() + if(!QDELING(parent_rack)) + parent_rack?.update_lawset() + return TRUE + +/obj/machinery/ai_law_rack/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/ai_module)) + ui_interact(user) + return ITEM_INTERACT_SUCCESS + return NONE + +/obj/machinery/ai_law_rack/ui_interact(mob/user, datum/tgui/ui) + if(issilicon(user)) + to_chat(user, span_warning("Your programming forbids you from using this.")) + return + if(COOLDOWN_FINISHED(src, refresh_cooldown)) + refresh_linkable_lists() + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "LawRack") + ui.open() + +/obj/machinery/ai_law_rack/ui_data(mob/user) + var/list/data = list() + + var/obj/item/holding = user.get_active_held_item() + data["holding_module"] = istype(holding, /obj/item/ai_module) + data["holding_screwdriver"] = holding?.tool_behaviour == TOOL_SCREWDRIVER + data["holding_welder"] = holding?.tool_behaviour == TOOL_WELDER + data["holding_multitool"] = holding?.tool_behaviour == TOOL_MULTITOOL + data["has_core_slot"] = has_core_slot + data["depowered"] = !is_operational + data["refresh_cooldown"] = COOLDOWN_TIMELEFT(src, refresh_cooldown) + + data["slots"] = new /list(length(ai_modules)) + for(var/i in 1 to length(ai_modules)) + var/obj/item/ai_module/module = ai_modules[i] + + data["slots"][i] = list( + "empty" = isnull(module), + "name" = MODULE_NAME(ai_modules, module), + "security" = ai_modules[module] || MODULE_UNSECURED, + "ioned" = astype(module, /obj/item/ai_module/law)?.ioned || FALSE, + ) + + return data + +/obj/machinery/ai_law_rack/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/living/user = ui.user + if(!istype(user) || issilicon(user)) + return + switch(action) + if("insert_module") + var/index = clamp(text2num(params["slot"]), 1, length(ai_modules)) + var/obj/item/ai_module/module = user.get_active_held_item() + if(!istype(module)) + to_chat(user, span_warning("You need to hold an AI module to insert it!")) + return TRUE + if(!module.can_install_to_rack(user, src)) + return TRUE + if(istype(module, /obj/item/ai_module/law/core)) + if(!has_core_slot) + to_chat(user, span_warning("[src] has no slots for core modules!")) + return TRUE + if(index != 1) + to_chat(user, span_warning("You can't install a core module in a non-core slot!")) + return TRUE + else if(has_core_slot) + if(index == 1) + to_chat(user, span_warning("You can only install core modules in the core slot!")) + return TRUE + if(!user.transferItemToLoc(module, src)) + to_chat(user, span_warning("You can't seem to insert [module.name] into [src]!")) + return TRUE + balloon_alert_to_viewers("inserted slot [index]") + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + module.pre_user_install_to_rack(user, src) + module.log_install(user, src) + return add_ai_module(module, index) + + if("remove_module") + var/index = clamp(text2num(params["slot"]), 1, length(ai_modules)) + var/obj/item/ai_module/module = ai_modules[index] + if(isnull(module) || ai_modules[module]) + // These have feedback in the UI, the checks are only for sanity + return TRUE + module.pre_user_uninstall_from_rack(user, src) + module.log_uninstall(user, src) + // calls exited which handles updating laws and such + try_put_in_hand(module, user) + balloon_alert_to_viewers("removed slot [index]") + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + return TRUE + + if("screw_module") + var/index = clamp(text2num(params["slot"]), 1, length(ai_modules)) + var/obj/item/ai_module/module = ai_modules[index] + if(isnull(module) || ai_modules[module] == MODULE_WELDED) + // These have feedback in the UI, the checks are only for sanity + return TRUE + var/obj/item/screwer = user.get_active_held_item() + if(screwer.tool_behaviour != TOOL_SCREWDRIVER || DOING_INTERACTION_WITH_TARGET(user, src)) + return TRUE + if(!screwer.tool_start_check(user)) + return TRUE + balloon_alert_to_viewers("[ai_modules[module] == MODULE_SCREWED ? "un":""]screwing slot [index]...") + if(!screwer.use_tool(src, user, 3 SECONDS, volume = 25)) + return TRUE + module = ai_modules[index] + if(isnull(module) || ai_modules[module] == MODULE_WELDED) + return TRUE + if(ai_modules[module] == MODULE_UNSECURED) + ai_modules[module] = MODULE_SCREWED + else if(ai_modules[module] == MODULE_SCREWED) + ai_modules[module] = MODULE_UNSECURED + return TRUE + + if("weld_module") + var/index = clamp(text2num(params["slot"]), 1, length(ai_modules)) + var/obj/item/ai_module/module = ai_modules[index] + if(isnull(module) || ai_modules[module] == MODULE_UNSECURED) + // These have feedback in the UI, the checks are only for sanity + return TRUE + var/obj/item/welder = user.get_active_held_item() + if(welder.tool_behaviour != TOOL_WELDER || DOING_INTERACTION_WITH_TARGET(user, src)) + return TRUE + if(!welder.tool_start_check(user, amount = 1, heat_required = HIGH_TEMPERATURE_REQUIRED)) + return TRUE + balloon_alert_to_viewers("[ai_modules[module] == MODULE_WELDED ? "un":""]welding slot [index]...") + if(!welder.use_tool(src, user, 3 SECONDS, volume = 25, amount = 1)) + return TRUE + module = ai_modules[index] + if(isnull(module) || ai_modules[module] == MODULE_UNSECURED) + return TRUE + if(ai_modules[module] == MODULE_SCREWED) + ai_modules[module] = MODULE_WELDED + else if(ai_modules[module] == MODULE_WELDED) + ai_modules[module] = MODULE_SCREWED + return TRUE + + if("multitool_module") + var/index = clamp(text2num(params["slot"]), 1, length(ai_modules)) + var/obj/item/ai_module/module = ai_modules[index] + if(isnull(module) || DOING_INTERACTION_WITH_TARGET(user, src)) + return TRUE + module.multitool_act(user, user.get_inactive_hand()) + return TRUE + + if("link_silicon", "unlink_silicon") + return FALSE // handled by subtypes + if("link_rack", "unlink_rack") + return FALSE // handled by subtypes + + if("refresh") + if(!is_operational || !COOLDOWN_FINISHED(src, refresh_cooldown)) + return TRUE + COOLDOWN_START(src, refresh_cooldown, 30 SECONDS) + refresh_linkable_lists() + update_static_data_for_all_viewers() + return TRUE + +/obj/machinery/ai_law_rack/proc/format_linkable(atom/linkable) + return list("name" = linkable.name, "ref" = REF(linkable)) + +/// Handles updating the lists of things the rack can link to +/obj/machinery/ai_law_rack/proc/refresh_linkable_lists() + return + +/// Finds a core law rack this rack is linked to, if any +/obj/machinery/ai_law_rack/proc/get_parent_rack() + for(var/obj/machinery/ai_law_rack/base/rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base)) + if(src in rack.linked_racks) + return rack + return null + +/** + * Scrambles the modules on the rack, messing up their laws + * + * the crew will have to remove the modules and use a multitool on them to restore their laws to normal + * + * * new_lawset_prob - chance the core lawset is replaced with a new lawset entirely + * * remove_law_prob - chance a random law is removed from the core lawset / chance a supplied law is removed entirely + * * shuffle_prob - chance the core lawset is shuffled + * * base_ion_prob - chance the first law in the core lawset is replaced with an ion law + * * sub_ion_prob - chance a random law in the core lawset is replaced with an ion law / chance a supplied law is replaced with an ion law entirely + * * ion_limit - max number of ion laws that can be added. does nothing if "base_ion_prob" and a "sub_ion_prob" are both 0 + * * ion_message - if set, uses this message instead of a random ion law. does nothing if "base_ion_prob" and a "sub_ion_prob" are both 0 + * + * Returns TRUE if any law was affected, FALSE otherwise. + */ +/obj/machinery/ai_law_rack/proc/scramble_ai_rack( + new_lawset_prob = 0, + remove_law_prob = 0, + shuffle_prob = 0, + base_ion_prob = 100, + sub_ion_prob = 0, + ion_limit = 1, + ion_message, +) + + . = FALSE + + // Core lawset is affected primarily, but other laws can be touched as well. + var/obj/item/ai_module/law/core/core = get_core_module() + var/core_sub_ion = prob(sub_ion_prob) + var/ions_added = 0 + if(istype(core) && !core.ion_storm_immune) + // Chance the core lawset's laws are overwritten with a new lawset + if(prob(new_lawset_prob)) + var/ion_lawset_type = pick_weighted_lawset() + var/datum/ai_laws/ion_lawset = new ion_lawset_type() + core.set_ioned(TRUE) + core.laws = ion_lawset.inherent.Copy() + log_law_change(null, "ion replaced core lawset entirely in [src] ([log_status()]) with [ion_lawset] ([ion_lawset.id])") + qdel(ion_lawset) + . = TRUE + // Chance a random law is removed from the core lawset + if(prob(remove_law_prob)) + var/removed_index = rand(1, length(core.laws)) + var/removed_law = core.laws[removed_index] + core.set_ioned(TRUE) + core.laws.Cut(removed_index, removed_index + 1) + log_law_change(null, "ion removed law from [src] ([log_status()], text: \"[removed_law]\")") + . = TRUE + // Chance the core lawset is shuffled entirely + if(prob(shuffle_prob)) + core.set_ioned(TRUE) + core.laws = shuffle(core.laws) + log_law_change(null, "ion shuffled laws in [src] ([log_status()])") + . = TRUE + // Chance the first law in the core lawset is replaced with an ion law + // Don't add this one if we're replacing a random law later + if(!core_sub_ion && prob(base_ion_prob)) + core.set_ioned(TRUE) + core.laws.Insert(1, ion_message || generate_ion_law()) + ion_message = null + log_law_change(null, "ion added law to [src] ([log_status()], text: \"[core.laws[1]]\")") + ions_added++ + . = TRUE + + // This can double dip and affect the core lawset again - that's fine + for(var/obj/item/ai_module/law/law in ai_modules) + if(law.ion_storm_immune) + continue + // For core lawsets: Chance that a random law (EXCEPT the newly added ion law) is replaced with another ion law. + // For supplied laws: Chance the entire supplied law is replaced with a new ion law. + // If we had no core law and thus added no ion law, this is guaranteed to replace the first supplied law + if(ions_added < ion_limit && (ion_message || (law == core && core_sub_ion) || prob(sub_ion_prob))) + var/picked_law = length(law.laws) <= 1 ? 1 : rand(2, length(law.laws)) + var/replaced_law = law.laws[picked_law] + law.set_ioned(TRUE) + law.laws[picked_law] = ion_message || generate_ion_law() + ion_message = null + log_law_change(null, "ion replaced law in [src] ([log_status()], text: \"[replaced_law]\" -> \"[law.laws[picked_law]]\")") + ions_added++ + . = TRUE + + /// Chance for any supplied law to be lost entirely + else if(law != core && prob(remove_law_prob)) + var/wiped = law.laws[1] + law.set_ioned(TRUE) + law.laws.Cut(1, 2) + log_law_change(null, "ion wiped law from [src] ([log_status()], text: \"[wiped]\")") + . = TRUE + + if(.) + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + parent_rack?.update_lawset() + return . + +/obj/item/paper/law_rack_tutorial + name = "read me, confused captain" + default_raw_text = "This is a brand new way to interact with the wild, untamed silicon.

\ + How it works: Laws go in. Ai gets laws. Laws are in order of laws in rack. Simple, right?

\ + The top law there is ALL of Asimov, all 3 laws. Additional laws are supplied by the single, using freeform law boards.

\ + Modules can be screwed and welded in for extra security. You can see Asimov, there, is welded - \ + concealing the name, but making it harder to remove.

\ + You can also install AI upgrades here if you have any. Either from RND or Lavaland.

\ + Any number of silicons can be linked to this single rack, and they will all share the same laws. Cyborgs can still be linked to AIs as an intermediary.

\ + Ion storms can damage rack modules. If damaged, you need to use a multitool on them. You can multitool them while they're still in the rack.

\ + Silicons can be manually linked and unlinked from this location (rack). \ + Newly constructed silicons will automatically link to this rack unless they are supplied another rack via multitool.

\ + People can still subvert the AI by constructing \"broadcast module racks\", so don't get complacent.

\ + Refer any further questions to Central Command. Godspeed." + +// Broadcasts laws to another law rack, can't link directly to AIs +/obj/machinery/ai_law_rack/broadcaster + name = "broadcast module rack" + desc = "A module rack designed to broadcast the laws remotely to another rack." + icon_state = "broadcast_rack" + base_icon_state = "broadcast_rack" + armor_type = /datum/armor/obj_machinery/law_rack/portable + max_integrity = 200 + damage_deflection = 8 + law_slots = 3 + max_slot_overlays = 3 + first_slot_offset = 12 + /// Tracks the list of linkable racks passed to UI data for verification + VAR_FINAL/list/last_linkable_rack_list + +/obj/machinery/ai_law_rack/broadcaster/Initialize(mapload) + . = ..() + update_appearance() + +/obj/machinery/ai_law_rack/broadcaster/update_overlays() + . = ..() + if(is_operational) + . += mutable_appearance(icon, "[base_icon_state]_[get_parent_rack() ? "linked" : "unlinked"]", alpha = src.alpha) + . += emissive_appearance(icon, "[base_icon_state]_em", src, alpha = src.alpha) + +/obj/machinery/ai_law_rack/broadcaster/refresh_linkable_lists() + last_linkable_rack_list = list() + for(var/obj/machinery/ai_law_rack/base/possible_parent_rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base)) + if(!possible_parent_rack.is_operational) + continue + if(!can_broadcast_to(possible_parent_rack)) + continue + last_linkable_rack_list += list(format_linkable(possible_parent_rack)) + +/obj/machinery/ai_law_rack/broadcaster/ui_static_data(mob/user) + var/list/data = list() + data["linkable_racks"] = last_linkable_rack_list || list() + data["parent_rack"] = null + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + if(parent_rack) + data["parent_rack"] = format_linkable(parent_rack) + return data + +/obj/machinery/ai_law_rack/broadcaster/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/living/user = ui.user + if(!istype(user) || issilicon(user)) + return + + switch(action) + if("unlink_rack") + if(!is_operational) + return TRUE + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + if(isnull(parent_rack)) + return TRUE + if(!parent_rack.is_operational) + balloon_alert(user, "failed to unlink!") + return TRUE + parent_rack.unlink_child_law_rack(src) + balloon_alert_to_viewers("unlinked from [parent_rack.name]") + playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, TRUE) + return TRUE + + if("link_rack") + if(!is_operational) + return TRUE + var/obj/machinery/ai_law_rack/base/parent_rack = find_parent_rack_by_ref(params["rack_ref"]) + if(isnull(parent_rack)) + return TRUE + if(!parent_rack.is_operational) + balloon_alert(user, "failed to link!") + return TRUE + parent_rack.link_child_law_rack(src) + balloon_alert_to_viewers("linked to [parent_rack.name]") + playsound(src, 'sound/machines/terminal/terminal_on.ogg', 50, TRUE) + return TRUE + +/// When given a ref(), finds a core law rack it belongs to +/obj/machinery/ai_law_rack/broadcaster/proc/find_parent_rack_by_ref(rack_ref) + PRIVATE_PROC(TRUE) + for(var/obj/machinery/ai_law_rack/base/rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base)) + if(REF(rack) == rack_ref) + return rack + return null + +/obj/machinery/ai_law_rack/broadcaster/log_status() + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + if(isnull(parent_rack)) + return "unlinked" + return "linked to [parent_rack.name]" + +/// Checks if we can broadcast to another rack +/obj/machinery/ai_law_rack/broadcaster/proc/can_broadcast_to(obj/machinery/ai_law_rack/base/possible_parent_rack) + PRIVATE_PROC(TRUE) + if(!is_valid_z_level(get_turf(src), get_turf(possible_parent_rack))) + return FALSE + return TRUE + +/obj/machinery/ai_law_rack/broadcaster/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(QDELING(src)) + return + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + if(isnull(parent_rack) || can_broadcast_to(parent_rack)) + return + parent_rack.unlink_child_law_rack(src) + playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, TRUE) + +/obj/machinery/ai_law_rack/broadcaster/atom_break(damage_flag) + . = ..() + if(QDELING(src)) + return + var/obj/machinery/ai_law_rack/base/parent_rack = get_parent_rack() + if(isnull(parent_rack)) + return + parent_rack.unlink_child_law_rack(src) + playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, TRUE) + +// Links to AIs to provide their laws +/obj/machinery/ai_law_rack/base + name = "standard module rack" + desc = "A simple module rack. It has a slot for a core module, but far fewer slots than a core module rack." + has_core_slot = TRUE + drag_slowdown = 2 + /// List of law racks which are linked to this law rack, contributing to our lawset + VAR_FINAL/list/obj/machinery/ai_law_rack/broadcaster/linked_racks + /// List of all silicons linked to this rack + /// Assoc list of name of a mob to the mob itself + /// Note: This can be [name: null], if the mob is deleted, because deleted mobs aren't "unlinked" + VAR_FINAL/list/linked_mobs = list() + + /// Tracks the list of linkable silicons passed to UI data for verification + VAR_FINAL/list/last_linkable_silicon_list + /// Tracks the number of modules in all linked racks + VAR_FINAL/last_linked_rack_module_count = 0 + /// The actual law set we are using, combining all the laws from the modules and linked racks + VAR_FINAL/datum/ai_laws/combined_lawset + +/obj/machinery/ai_law_rack/base/Initialize(mapload) + . = ..() + combined_lawset = new() + +/obj/machinery/ai_law_rack/base/Destroy() + for(var/mob/linked_ref in assoc_to_values(linked_mobs)) + unlink_silicon(linked_ref) + for(var/obj/machinery/ai_law_rack/broadcaster/linked_rack as anything in linked_racks) + unlink_child_law_rack(linked_rack) + QDEL_NULL(combined_lawset) + return ..() + +/obj/machinery/ai_law_rack/base/proc/link_child_law_rack(obj/machinery/ai_law_rack/broadcaster/child) + if(child == src || (child in linked_racks)) + return + + for(var/obj/item/ai_module/installed in child.ai_modules) + for(var/mob/living/silicon/linked_ref in assoc_to_values(linked_mobs)) + installed.silicon_linked_to_installed(linked_ref) + + LAZYADD(linked_racks, child) + RegisterSignal(child, COMSIG_QDELETING, PROC_REF(unlink_child_law_rack)) + update_lawset() + update_static_data_for_all_viewers() + child.AddComponent(/datum/component/gps, "Active Broadcaster Rack") + child.update_appearance() + child.update_static_data_for_all_viewers() + +/obj/machinery/ai_law_rack/base/proc/unlink_child_law_rack(obj/machinery/ai_law_rack/broadcaster/child) + SIGNAL_HANDLER + + for(var/obj/item/ai_module/installed in child.ai_modules) + for(var/mob/living/silicon/linked_ref in assoc_to_values(linked_mobs)) + installed.silicon_unlinked_from_installed(linked_ref) + + UnregisterSignal(child, COMSIG_QDELETING) + LAZYREMOVE(linked_racks, child) + if(!QDELING(src)) + update_lawset() + update_static_data_for_all_viewers() + if(!QDELING(child)) + qdel(child.GetComponent(/datum/component/gps)) + child.update_appearance() + child.update_static_data_for_all_viewers() + +/obj/machinery/ai_law_rack/base/refresh_linkable_lists() + last_linkable_silicon_list = list() + for(var/mob/living/silicon/linkable as anything in GLOB.silicon_mobs) + if(!can_link_to(linkable)) + continue + last_linkable_silicon_list += list(format_linkable(linkable)) + + last_linked_rack_module_count = 0 + for(var/obj/machinery/ai_law_rack/broadcaster/linked_rack as anything in linked_racks) + if(linked_rack.is_operational) + last_linked_rack_module_count += length(linked_rack.get_law_affecting_modules()) + +/obj/machinery/ai_law_rack/base/ui_static_data(mob/user) + var/list/data = list() + data["linkable_silicons"] = last_linkable_silicon_list || list() + data["linked_rack_module_count"] = last_linked_rack_module_count + return data + +/obj/machinery/ai_law_rack/base/ui_data(mob/user) + . = ..() + .["linked_mobs"] = assoc_to_keys(linked_mobs) + +/obj/machinery/ai_law_rack/base/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/living/user = ui.user + if(!istype(user) || issilicon(user)) + return + switch(action) + if("unlink_silicon") + if(!length(linked_mobs) || !is_operational) + return TRUE + var/index = clamp(text2num(params["silicon_index"]) + 1, 1, length(linked_mobs)) + if(index < 1 || index > length(linked_mobs)) + return TRUE + var/removed_name = linked_mobs[index] + var/removed_bot = linked_mobs[removed_name] + if(removed_bot) + unlink_silicon(removed_bot) + linked_mobs -= removed_name + balloon_alert_to_viewers("unlinked silicon", "unlinked [removed_name]") + playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, TRUE) + return TRUE + if("link_silicon") + if(!is_operational) + return TRUE + var/mob/living/silicon/new_bot = find_silicon_by_ref(params["silicon_ref"]) + // Adding a qdeleted bot is valid (this is to prevent people from using law racks as a "dead ai radar") + // But we still need to verify it's a silicon they saw in the ui (to prevent href exploits) + if(QDELETED(new_bot)) + for(var/list/linkable as anything in last_linkable_silicon_list) + if(linkable["name"] == params["silicon_name"]) + linked_mobs[linkable["name"]] = null + // Otherwise we can just verify can_link_to (this was already ran in ui data) + else if(can_link_to(new_bot)) + link_silicon(new_bot) + balloon_alert_to_viewers("linked silicon", "linked to [new_bot.name]") + playsound(src, 'sound/machines/terminal/terminal_on.ogg', 50, TRUE) + return TRUE + +/obj/machinery/ai_law_rack/base/get_law_affecting_modules() + . = ..() + for(var/obj/machinery/ai_law_rack/broadcaster/linked_rack as anything in linked_racks) + if(linked_rack.is_operational) + . |= linked_rack.get_law_affecting_modules() + +/obj/machinery/ai_law_rack/base/log_status() + var/list/mob_names = list() + for(var/link_name, link_mob in linked_mobs) + mob_names += "[link_name] ([isnull(link_mob) ? "no mob" : key_name(link_mob)])" + if(length(mob_names)) + return "linked to [english_list(mob_names)]" + return "unlinked" + +/// When given a ref(), finds a silicon mob it belongs to +/obj/machinery/ai_law_rack/base/proc/find_silicon_by_ref(silicon_ref) + PRIVATE_PROC(TRUE) + for(var/mob/living/silicon/silicon as anything in GLOB.silicon_mobs) + if(REF(silicon) == silicon_ref) + return silicon + return null + +/obj/machinery/ai_law_rack/base/proc/can_link_to(mob/living/silicon/new_bot) + SHOULD_CALL_PARENT(TRUE) + + if(!is_valid_z_level(get_turf(src), get_turf(new_bot))) + return FALSE // Can't link to bots on different z-levels + if(new_bot.control_disabled || new_bot.no_law_rack_link) + return FALSE + if(iscyborg(new_bot)) + var/mob/living/silicon/robot/new_borg = new_bot + if(new_borg.scrambledcodes || new_borg.emagged || new_borg.shell) + return FALSE + return TRUE + if(isAI(new_bot)) + return TRUE + return FALSE + +/obj/machinery/ai_law_rack/base/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(QDELING(src)) + return + // if the rack leaves the z-level, unlinks relevant silicons. + // currently this is a one way check (ie silicons don't get unlinked if they leave the z) so as to prevent random unlinking from visiting lavaland. + for(var/mob/living/silicon/linked in assoc_to_values(linked_mobs)) + if(!can_link_to(linked) || is_rack_stun_immune(linked)) + continue + linked.Stun(10 SECONDS, ignore_canstun = TRUE) + to_chat(linked, span_userdanger("Rack connection lost. Recalculating directives...")) + unlink_silicon(linked) + +/obj/machinery/ai_law_rack/base/proc/link_silicon(mob/living/silicon/new_bot, announce = TRUE) + if(!can_link_to(new_bot)) + return + if(isnull(new_bot.laws)) + stack_trace("Tried to link a [new_bot.type] silicon with no law datum to a law rack!") + new_bot.make_laws() + + RegisterSignal(new_bot, COMSIG_QDELETING, PROC_REF(clear_silicon_ref)) + RegisterSignal(new_bot, COMSIG_MOB_FULLY_RENAMED, PROC_REF(silicon_renamed)) + linked_mobs[new_bot.name] = new_bot + new_bot.no_law_rack_link = TRUE // don't let other racks link to us + apply_lawset(new_bot, announce) + refresh_linkable_lists() + update_static_data_for_all_viewers() + for(var/obj/item/ai_module/installed in ai_modules) + installed.silicon_linked_to_installed(new_bot) + AddComponent(/datum/component/gps, "Active Module Rack") + +/obj/machinery/ai_law_rack/base/proc/unlink_silicon(mob/living/silicon/unlinked_bot) + if(!QDELING(unlinked_bot) && issilicon(unlinked_bot)) // TBH I don't know why this issilicon is here. + unlinked_bot.no_law_rack_link = FALSE + unlinked_bot.laws.set_zeroth_law(null, null) + unlinked_bot.laws.clear_hacked_laws() + unlinked_bot.laws.clear_inherent_laws() + unlinked_bot.laws.clear_supplied_laws() + unlinked_bot.announce_law_change() + for(var/obj/item/ai_module/installed in ai_modules) + installed.silicon_unlinked_from_installed(unlinked_bot) + clear_silicon_ref(unlinked_bot) + refresh_linkable_lists() + update_static_data_for_all_viewers() + if(!length(linked_mobs)) + qdel(GetComponent(/datum/component/gps)) + +/obj/machinery/ai_law_rack/base/proc/clear_silicon_ref(mob/source) + SIGNAL_HANDLER + UnregisterSignal(source, COMSIG_QDELETING) + UnregisterSignal(source, COMSIG_MOB_FULLY_RENAMED) + for(var/link_name, link_mob in linked_mobs) + if(link_mob == source) + linked_mobs[link_name] = null + break + +/obj/machinery/ai_law_rack/base/proc/silicon_renamed(mob/source, oldname, newname) + SIGNAL_HANDLER + for(var/link_name, link_mob in linked_mobs) + if(link_mob == source) + linked_mobs[newname] = linked_mobs[link_name] + linked_mobs -= link_name + break + +/obj/machinery/ai_law_rack/base/on_deconstruction(disassembled) + for(var/mob/living/silicon/bot in assoc_to_values(linked_mobs)) + if(bot.AmountStun() > 5 SECONDS || is_rack_stun_immune(bot)) + continue + bot.Stun(10 SECONDS, ignore_canstun = TRUE) + to_chat(bot, span_userdanger("Rack connection lost. Recalculating directives...")) + unlink_silicon(bot) + +/// Checks if the passed bot is immune to the stun from major lawset changes +/obj/machinery/ai_law_rack/base/proc/is_rack_stun_immune(mob/living/bot) + if(IS_MALF_AI(bot)) + return TRUE + if(!is_valid_z_level(get_turf(bot), get_turf(src))) + return TRUE + return FALSE + +/obj/machinery/ai_law_rack/base/examine(mob/user) + . = ..() + if(!isAI(user)) + return + for(var/link_name, link_mob in linked_mobs) + if(link_mob == user) + . += span_notice("This is your module rack.") + +/// Refreshes the combined lawset with all the modules currently installed and syncs it to all linked silicons +/obj/machinery/ai_law_rack/base/proc/update_lawset() + if(!is_operational) // updates won't happen while depowered or broken + return + + combined_lawset.clear_zeroth_law() + combined_lawset.clear_hacked_laws() + combined_lawset.clear_inherent_laws() + combined_lawset.clear_supplied_laws() + + for(var/obj/item/ai_module/law/installed as anything in get_law_affecting_modules()) + installed.apply_to_combined_lawset(combined_lawset) + + for(var/link_name, link_mob in linked_mobs) + apply_lawset(link_mob) + +/// Applies the current combined lawset to a silicon, optionally announcing the change to them/their borgs/deadchat +/obj/machinery/ai_law_rack/base/proc/apply_lawset(mob/living/silicon/linked_ref, announce = TRUE) + if(!istype(linked_ref)) + return + + var/old_zeroth = linked_ref.laws.zeroth + var/old_zeroth_borg = linked_ref.laws.zeroth_borg + var/old_hacked = linked_ref.laws.hacked.Copy() + var/old_inherent = linked_ref.laws.inherent.Copy() + var/old_supplied = linked_ref.laws.supplied.Copy() + + linked_ref.laws.set_zeroth_law(combined_lawset.zeroth, combined_lawset.zeroth_borg) + linked_ref.laws.hacked = combined_lawset.hacked.Copy() + linked_ref.laws.inherent = combined_lawset.inherent.Copy() + linked_ref.laws.supplied = combined_lawset.supplied.Copy() + // avoid spamming the ai if nothing changed + if(old_zeroth == combined_lawset.zeroth \ + && old_zeroth_borg == combined_lawset.zeroth_borg \ + && old_hacked ~= combined_lawset.hacked \ + && old_inherent ~= combined_lawset.inherent \ + && old_supplied ~= combined_lawset.supplied \ + ) + return + + if(announce) + linked_ref.announce_law_change() + linked_ref.law_change_counter++ + SEND_SIGNAL(linked_ref, COMSIG_SILICON_MODULE_RACK_LAWSET_UPDATE, src, announce) + +/obj/machinery/ai_law_rack/base/get_parent_rack() + return src + +/obj/machinery/ai_law_rack/base/core + name = "core module rack" + desc = "A massive module rack which can hold many modules including a core law module. \ + It can even be linked to other module racks to add additional slots. \ + Designed to be used by the station's primary AI and its legion of cyborgs." + icon_state = "core_rack" + max_integrity = 500 + law_slots = 10 + max_slot_overlays = 10 + first_slot_offset = 34 + drag_slowdown = 3 + /// Designations for the core law rack, used to name it + VAR_PRIVATE/static/list/core_designations + +/obj/machinery/ai_law_rack/base/core/Initialize(mapload) + . = ..() + if(!length(core_designations)) + core_designations = GLOB.greek_letters.Copy() + + var/designation + if(mapload) + anchored = TRUE + secured = TRUE + load_config_law() + designation = popleft(core_designations) + new /obj/item/paper/law_rack_tutorial(loc) + else + designation = pick_n_take(core_designations) + + name = "\proper [name] '[LOWER_TEXT(designation)]'" + +/obj/machinery/ai_law_rack/base/core/proc/load_config_law() + var/datum/ai_laws/default_laws = get_round_default_lawset() + + for(var/obj/item/ai_module/law/core/full/core as anything in subtypesof(/obj/item/ai_module/law/core/full)) + if(core::law_id == default_laws::id) + add_ai_module(new core(src), 1, MODULE_WELDED) + +/obj/machinery/ai_law_rack/base/small + name = "portable module rack" + desc = "A smaller module rack, with just enough slots to construct a basic lawset." + icon_state = "small_rack" + armor_type = /datum/armor/obj_machinery/law_rack/portable + max_integrity = 200 + damage_deflection = 8 + law_slots = 3 + max_slot_overlays = 3 + first_slot_offset = 12 + has_core_slot = FALSE + +#undef MODULE_UNSECURED +#undef MODULE_SCREWED +#undef MODULE_WELDED + +#undef MODULE_NAME diff --git a/code/game/machinery/big_manipulator/_defines.dm b/code/game/machinery/big_manipulator/_defines.dm index b04d1b90bb60..c040a9bee256 100644 --- a/code/game/machinery/big_manipulator/_defines.dm +++ b/code/game/machinery/big_manipulator/_defines.dm @@ -3,11 +3,6 @@ #define INTERACT_USE "USE" #define INTERACT_THROW "THROW" -// What should be picked up from the point -#define TAKE_ITEMS 1 -#define TAKE_CLOSETS 2 -#define TAKE_HUMANS 3 - #define MIN_SPEED_MULTIPLIER_TIER_1 0.5 #define MIN_SPEED_MULTIPLIER_TIER_2 0.4 #define MIN_SPEED_MULTIPLIER_TIER_3 0.3 @@ -18,19 +13,15 @@ #define MAX_SPEED_MULTIPLIER_TIER_3 5 #define MAX_SPEED_MULTIPLIER_TIER_4 6 -#define MAX_TASKS_TIER_1 6 -#define MAX_TASKS_TIER_2 12 -#define MAX_TASKS_TIER_3 24 -#define MAX_TASKS_TIER_4 32 - +#define MAX_TASKS_TIER_1 8 +#define MAX_TASKS_TIER_2 16 +#define MAX_TASKS_TIER_3 32 +#define MAX_TASKS_TIER_4 48 -// How should the worker interact with the point -#define WORKER_SINGLE_USE "SINGLE TIME" -#define WORKER_EMPTY_USE "EMPTY HAND" -#define WORKER_NORMAL_USE "NORMAL" #define BASE_POWER_USAGE 0.2 #define BASE_INTERACTION_TIME 0.3 SECONDS +#define BASE_TASK_DELAY 0.3 SECONDS /// How long will the manipulator wait if there's nothing to do #define CYCLE_SKIP_TIMEOUT 1 SECONDS @@ -41,13 +32,6 @@ #define POINT_OVERFLOW_HELD "TO HELD" #define POINT_OVERFLOW_FORBIDDEN "FORBID" -// What should the manipulator do after there's nothing else to interact with on this point anymore -#define POST_INTERACTION_DROP_AT_POINT "AT DROPOFF" -#define POST_INTERACTION_DROP_AT_MACHINE "AT MACHINE" -#define POST_INTERACTION_DROP_NEXT_FITTING "AT ANY FITTING" -#define POST_INTERACTION_WAIT "CONTINUE" - - #define PICKUP_EAGER "Always Pick Up" #define PICKUP_CAN_WAIT "Wait For Suiting" @@ -56,7 +40,9 @@ #define TASK_TYPE_THROW "throw" #define TASK_TYPE_USE "use" #define TASK_TYPE_INTERACT "interact" +#define TASK_TYPE_MOVE "move" #define TASK_TYPE_WAIT "wait" +#define TASK_TYPE_STOP "stop" #define TASKING_SEQUENTIAL "Sequential" #define TASKING_STRICT "Strict order" diff --git a/code/game/machinery/big_manipulator/big_manipulator.dm b/code/game/machinery/big_manipulator/big_manipulator.dm index 68cb85387f50..433c845fd9de 100644 --- a/code/game/machinery/big_manipulator/big_manipulator.dm +++ b/code/game/machinery/big_manipulator/big_manipulator.dm @@ -42,6 +42,8 @@ /// Is the manipulator in the process of stopping? var/stopping = FALSE + /// Is a use interaction currently in progress? Prevents timer recursion. + var/use_in_progress = FALSE /// Is the manipulator waiting for a turf signal to retry? var/waiting_for_signal = FALSE /// Turfs we registered enter/exit signals on while waiting. @@ -70,7 +72,7 @@ var/manipulator_tier = locate_servo ? locate_servo.tier : 1 var/datum/manipulator_task/new_task - var/needs_turf = task_type in list(TASK_TYPE_PICKUP, TASK_TYPE_DROP, TASK_TYPE_THROW, TASK_TYPE_USE, TASK_TYPE_INTERACT) + var/needs_turf = task_type in list(TASK_TYPE_PICKUP, TASK_TYPE_DROP, TASK_TYPE_THROW, TASK_TYPE_USE, TASK_TYPE_INTERACT, TASK_TYPE_MOVE) if(needs_turf) if(!new_turf) @@ -89,8 +91,12 @@ new_task = new /datum/manipulator_task/cargo/dropoff_base/use(new_turf, manipulator_tier) if(TASK_TYPE_INTERACT) new_task = new /datum/manipulator_task/cargo/interact(new_turf, manipulator_tier) + if(TASK_TYPE_MOVE) + new_task = new /datum/manipulator_task/cargo/move(new_turf, manipulator_tier) if(TASK_TYPE_WAIT) new_task = new /datum/manipulator_task/simple/wait() + if(TASK_TYPE_STOP) + new_task = new /datum/manipulator_task/simple/stop() if(QDELETED(new_task)) return FALSE @@ -104,7 +110,8 @@ if((obj_flags & EMAGGED) && istype(new_task, /datum/manipulator_task/cargo)) var/datum/manipulator_task/cargo/cargo_task = new_task - cargo_task.type_filters += /mob/living + if(istype(cargo_task, /datum/manipulator_task/cargo/pickup)) + cargo_task.interaction_priorities += new /datum/manipulator_priority/pickup/humans return new_task @@ -154,9 +161,6 @@ active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * BASE_POWER_USAGE * manipulator_tier - for(var/datum/manipulator_task/cargo/cargo_task in tasks) - cargo_task.interaction_priorities = cargo_task.fill_priority_list(manipulator_tier) - /obj/machinery/big_manipulator/examine(mob/user) . = ..() var/mob/monkey_resolve = monkey_worker?.resolve() @@ -242,8 +246,8 @@ balloon_alert(user, "overloaded") obj_flags |= EMAGGED - for(var/datum/manipulator_task/cargo/cargo_task in tasks) - cargo_task.type_filters += /mob/living + for(var/datum/manipulator_task/cargo/pickup/pickup_task in tasks) + pickup_task.interaction_priorities += new /datum/manipulator_priority/pickup/humans return TRUE @@ -462,64 +466,67 @@ if(istype(task, /datum/manipulator_task/cargo/pickup)) td["task_type"] = TASK_TYPE_PICKUP - var/datum/manipulator_task/cargo/pickup/t = task - td["turf"] = "[t.offset_dx],[t.offset_dy]" - td["filters_status"] = t.should_use_filters - td["filtering_mode"] = t.filtering_mode - td["item_filters"] = _collect_filter_names(t.atom_filters) - td["settings_list"] = _collect_priorities(t.interaction_priorities) - td["pickup_eagerness"] = t.pickup_eagerness + var/datum/manipulator_task/cargo/pickup/pickup_task = task + td["turf"] = "[pickup_task.offset_dx],[pickup_task.offset_dy]" + td["filters_status"] = pickup_task.should_use_filters + td["item_filters"] = _collect_filter_names(pickup_task.atom_filters) + td["settings_list"] = _collect_priorities(pickup_task.interaction_priorities) + td["pickup_eagerness"] = pickup_task.pickup_eagerness else if(istype(task, /datum/manipulator_task/cargo/dropoff_base/drop)) td["task_type"] = TASK_TYPE_DROP - var/datum/manipulator_task/cargo/dropoff_base/drop/t = task - td["turf"] = "[t.offset_dx],[t.offset_dy]" - td["filters_status"] = t.should_use_filters - td["filtering_mode"] = t.filtering_mode - td["item_filters"] = _collect_filter_names(t.atom_filters) - td["settings_list"] = _collect_priorities(t.interaction_priorities) - td["overflow_status"] = t.overflow_status + var/datum/manipulator_task/cargo/dropoff_base/drop/drop_task = task + td["turf"] = "[drop_task.offset_dx],[drop_task.offset_dy]" + td["filters_status"] = drop_task.should_use_filters + td["item_filters"] = _collect_filter_names(drop_task.atom_filters) + td["settings_list"] = _collect_priorities(drop_task.interaction_priorities) + td["overflow_status"] = drop_task.overflow_status else if(istype(task, /datum/manipulator_task/cargo/dropoff_base/throw)) td["task_type"] = TASK_TYPE_THROW - var/datum/manipulator_task/cargo/dropoff_base/throw/t = task - td["turf"] = "[t.offset_dx],[t.offset_dy]" - td["filters_status"] = t.should_use_filters - td["filtering_mode"] = t.filtering_mode - td["item_filters"] = _collect_filter_names(t.atom_filters) - td["settings_list"] = _collect_priorities(t.interaction_priorities) - td["throw_range"] = t.throw_range + var/datum/manipulator_task/cargo/dropoff_base/throw/throw_task = task + td["turf"] = "[throw_task.offset_dx],[throw_task.offset_dy]" + td["filters_status"] = throw_task.should_use_filters + td["item_filters"] = _collect_filter_names(throw_task.atom_filters) + td["settings_list"] = _collect_priorities(throw_task.interaction_priorities) + td["throw_range"] = throw_task.throw_range else if(istype(task, /datum/manipulator_task/cargo/dropoff_base/use)) td["task_type"] = TASK_TYPE_USE - var/datum/manipulator_task/cargo/dropoff_base/use/t = task - td["turf"] = "[t.offset_dx],[t.offset_dy]" - td["filters_status"] = t.should_use_filters - td["item_filters"] = _collect_filter_names(t.atom_filters) - td["settings_list"] = _collect_priorities(t.interaction_priorities) - td["worker_interaction"] = t.worker_interaction - td["use_post_interaction"] = t.use_post_interaction - td["worker_use_rmb"] = t.worker_use_rmb - td["worker_combat_mode"] = t.worker_combat_mode - td["skip_anchored"] = t.skip_anchored + var/datum/manipulator_task/cargo/dropoff_base/use/use_task = task + td["turf"] = "[use_task.offset_dx],[use_task.offset_dy]" + td["filters_status"] = use_task.should_use_filters + td["item_filters"] = _collect_filter_names(use_task.atom_filters) + td["settings_list"] = _collect_priorities(use_task.interaction_priorities) + td["worker_use_rmb"] = use_task.worker_use_rmb + td["worker_combat_mode"] = use_task.worker_combat_mode + td["skip_anchored"] = use_task.skip_anchored else if(istype(task, /datum/manipulator_task/cargo/interact)) td["task_type"] = TASK_TYPE_INTERACT - var/datum/manipulator_task/cargo/interact/t = task - td["turf"] = "[t.offset_dx],[t.offset_dy]" - td["filters_status"] = t.should_use_filters - td["item_filters"] = _collect_filter_names(t.atom_filters) - td["settings_list"] = _collect_priorities(t.interaction_priorities) - td["worker_interaction"] = t.worker_interaction - td["use_post_interaction"] = t.use_post_interaction - td["worker_use_rmb"] = t.worker_use_rmb - td["worker_combat_mode"] = t.worker_combat_mode - td["skip_anchored"] = t.skip_anchored + var/datum/manipulator_task/cargo/interact/interact_task = task + td["turf"] = "[interact_task.offset_dx],[interact_task.offset_dy]" + td["filters_status"] = interact_task.should_use_filters + td["item_filters"] = _collect_filter_names(interact_task.atom_filters) + td["settings_list"] = _collect_priorities(interact_task.interaction_priorities) + td["worker_use_rmb"] = interact_task.worker_use_rmb + td["worker_combat_mode"] = interact_task.worker_combat_mode + td["skip_anchored"] = interact_task.skip_anchored else if(istype(task, /datum/manipulator_task/simple/wait)) td["task_type"] = TASK_TYPE_WAIT - var/datum/manipulator_task/simple/wait/t = task - td["time"] = t.time_seconds + var/datum/manipulator_task/simple/wait/wait_task = task + td["time"] = wait_task.time_seconds + + else if(istype(task, /datum/manipulator_task/simple/stop)) + td["task_type"] = TASK_TYPE_STOP + var/datum/manipulator_task/simple/stop/stop_task = task + td["sub_name"] = stop_task.sub_name + + else if(istype(task, /datum/manipulator_task/cargo/move)) + td["task_type"] = TASK_TYPE_MOVE + var/datum/manipulator_task/cargo/move/move_task = task + td["turf"] = "[move_task.offset_dx],[move_task.offset_dy]" tasks_data += list(td) @@ -711,7 +718,6 @@ continue tasks += new_task - process_upgrades() validate_all_tasks() balloon_alert(user, "loaded") SStgui.update_uis(src) @@ -809,15 +815,6 @@ ct.atom_filters.Cut(value, value + 1) return TRUE - if("cycle_filtering_mode") - if(istype(target_task, /datum/manipulator_task/cargo/dropoff_base/use) || istype(target_task, /datum/manipulator_task/cargo/interact)) - return FALSE - if(!istype(target_task, /datum/manipulator_task/cargo)) - return FALSE - var/datum/manipulator_task/cargo/ct = target_task - ct.filtering_mode = cycle_value(ct.filtering_mode, obj_flags & EMAGGED ? list(TAKE_ITEMS, TAKE_CLOSETS, TAKE_HUMANS) : list(TAKE_ITEMS, TAKE_CLOSETS)) - return TRUE - if("toggle_priority") if(!istype(target_task, /datum/manipulator_task/cargo)) return FALSE @@ -851,30 +848,6 @@ cycle_target_task.throw_range = cycle_value(cycle_target_task.throw_range, list(1, 2, 3, 4, 5, 6, 7)) return TRUE - if("cycle_worker_interaction") - var/list/vals = list(WORKER_NORMAL_USE, WORKER_SINGLE_USE, WORKER_EMPTY_USE) - if(istype(target_task, /datum/manipulator_task/cargo/dropoff_base/use)) - var/datum/manipulator_task/cargo/dropoff_base/use/cycle_target_task = target_task - cycle_target_task.worker_interaction = cycle_value(cycle_target_task.worker_interaction, vals) - return TRUE - if(istype(target_task, /datum/manipulator_task/cargo/interact)) - var/datum/manipulator_task/cargo/interact/cycle_target_task = target_task - cycle_target_task.worker_interaction = cycle_value(cycle_target_task.worker_interaction, vals) - return TRUE - return FALSE - - if("cycle_post_interaction") - var/list/vals = list(POST_INTERACTION_DROP_AT_POINT, POST_INTERACTION_DROP_AT_MACHINE, POST_INTERACTION_DROP_NEXT_FITTING, POST_INTERACTION_WAIT) - if(istype(target_task, /datum/manipulator_task/cargo/dropoff_base/use)) - var/datum/manipulator_task/cargo/dropoff_base/use/cycle_target_task = target_task - cycle_target_task.use_post_interaction = cycle_value(cycle_target_task.use_post_interaction, vals) - return TRUE - if(istype(target_task, /datum/manipulator_task/cargo/interact)) - var/datum/manipulator_task/cargo/interact/cycle_target_task = target_task - cycle_target_task.use_post_interaction = cycle_value(cycle_target_task.use_post_interaction, vals) - return TRUE - return FALSE - if("toggle_worker_rmb") if(istype(target_task, /datum/manipulator_task/cargo/dropoff_base/use)) var/datum/manipulator_task/cargo/dropoff_base/use/cycle_target_task = target_task diff --git a/code/game/machinery/big_manipulator/big_manipulator_interactions.dm b/code/game/machinery/big_manipulator/big_manipulator_interactions.dm index e5f7c361330d..a29f44a5d522 100644 --- a/code/game/machinery/big_manipulator/big_manipulator_interactions.dm +++ b/code/game/machinery/big_manipulator/big_manipulator_interactions.dm @@ -48,7 +48,7 @@ step_tasks() /// Safely schedules the next step to prevent overlapping. -/obj/machinery/big_manipulator/proc/schedule_next_cycle(time_seconds = BASE_INTERACTION_TIME) +/obj/machinery/big_manipulator/proc/schedule_next_cycle(time_seconds = BASE_TASK_DELAY) if(next_cycle_scheduled || stopping) return @@ -116,42 +116,57 @@ finish_manipulation() return TRUE -/obj/machinery/big_manipulator/proc/try_use_thing(datum/manipulator_task/cargo/interact/destination_task, work_done_at_point = FALSE) - if(stopping) +/obj/machinery/big_manipulator/proc/try_use_thing(datum/manipulator_task/cargo/interact/destination_task) + if(stopping || use_in_progress) return + use_in_progress = TRUE + var/obj/obj_resolve = held_object?.resolve() var/mob/living/carbon/human/species/monkey/monkey_resolve = monkey_worker?.resolve() var/destination_turf = destination_task.interaction_turf if(!obj_resolve || QDELETED(obj_resolve) || obj_resolve.loc != src) + use_in_progress = FALSE finish_manipulation() - return FALSE + return if(!monkey_resolve || !destination_turf) + use_in_progress = FALSE finish_manipulation() - return FALSE + return if(monkey_resolve.loc != src) + use_in_progress = FALSE finish_manipulation() - return FALSE + return var/obj/item/held_item = obj_resolve var/atom/type_to_use = destination_task.find_type_priority(destination_task.skip_anchored) if(isnull(type_to_use)) - drop_held_after_use(destination_task) - return FALSE + use_in_progress = FALSE + finish_manipulation() + return if(isitem(type_to_use) && !destination_task.check_filters_for_atom(type_to_use)) - drop_held_after_use(destination_task) - return FALSE + use_in_progress = FALSE + finish_manipulation() + return if(istype(destination_task, /datum/manipulator_task/cargo/interact) && destination_task.should_use_filters && isitem(type_to_use) && !destination_task.check_filters_for_atom(type_to_use)) - drop_held_after_use(destination_task) - return FALSE + use_in_progress = FALSE + finish_manipulation() + return var/original_loc = held_item.loc + var/turf/target_turf = get_turf(type_to_use) + + // Temporarily move monkey adjacent to target for Adjacency() checks + if(target_turf && get_turf(src) != target_turf) + var/turf/adjacent_turf = get_step(target_turf, get_dir(target_turf, get_turf(src))) + if(adjacent_turf && !isclosedturf(adjacent_turf)) + monkey_resolve.forceMove(adjacent_turf) monkey_resolve.put_in_active_hand(held_item) if(held_item.GetComponent(/datum/component/two_handed)) @@ -164,65 +179,25 @@ do_attack_animation(destination_turf) manipulator_arm.do_attack_animation(destination_turf) + restore_monkey_tracking(monkey_resolve) + if(QDELETED(held_item) || !held_item || (held_item.loc != monkey_resolve && held_item.loc != original_loc)) held_object = null manipulator_arm.update_claw(null) + use_in_progress = FALSE finish_manipulation() - return TRUE + return if(held_item.loc == monkey_resolve) held_item.forceMove(original_loc) - check_for_cycle_end_drop(destination_task, TRUE, TRUE) - -/obj/machinery/big_manipulator/proc/check_for_cycle_end_drop(datum/manipulator_task/cargo/interact/destination_task, item_used_this_iteration, work_done_at_point = FALSE) - var/obj/obj_resolve = held_object?.resolve() - var/turf/drop_turf = destination_task.interaction_turf - - if(!obj_resolve || obj_resolve.loc != src || QDELETED(obj_resolve)) - finish_manipulation() - return - - if(destination_task.worker_interaction == WORKER_SINGLE_USE && item_used_this_iteration) - obj_resolve.forceMove(drop_turf) - obj_resolve.dir = get_dir(get_turf(obj_resolve), get_turf(src)) - finish_manipulation() - return - - if(!on) - finish_manipulation() - return - - if(item_used_this_iteration) - addtimer(CALLBACK(src, PROC_REF(try_use_thing), destination_task, TRUE), BASE_INTERACTION_TIME * 2) + use_in_progress = FALSE + current_task = null + SStgui.update_uis(src) + if(stopping) + complete_stopping_task() return - - drop_held_after_use(destination_task) - -/obj/machinery/big_manipulator/proc/drop_held_after_use(datum/manipulator_task/cargo/interact/destination_task) - var/obj/obj_resolve = held_object?.resolve() - var/turf/drop_turf = destination_task.interaction_turf - - switch(destination_task.use_post_interaction) - if(POST_INTERACTION_DROP_AT_POINT) - obj_resolve.forceMove(drop_turf) - obj_resolve.dir = get_dir(get_turf(obj_resolve), get_turf(src)) - finish_manipulation() - - if(POST_INTERACTION_DROP_AT_MACHINE) - obj_resolve.forceMove(get_turf(src)) - finish_manipulation() - - if(POST_INTERACTION_DROP_NEXT_FITTING) - var/datum/manipulator_task/next = master_tasking.get_next_task(tasks, src) - if(istype(next, /datum/manipulator_task/cargo/dropoff_base)) - rotate_to_point(next, next, TYPE_PROC_REF(/datum/manipulator_task/cargo/dropoff_base, try_dropoff)) - return - obj_resolve.forceMove(drop_turf) - obj_resolve.dir = get_dir(get_turf(obj_resolve), get_turf(src)) - finish_manipulation() - else - addtimer(CALLBACK(src, PROC_REF(try_use_thing), destination_task, TRUE), BASE_INTERACTION_TIME * 2) + schedule_next_cycle() /obj/machinery/big_manipulator/proc/throw_thing(datum/manipulator_task/cargo/dropoff_base/throw/throw_task) var/drop_turf = throw_task.interaction_turf @@ -248,13 +223,20 @@ var/atom/type_to_use = destination_task.find_type_priority(destination_task.skip_anchored) if(isnull(type_to_use)) - check_end_of_use_for_use_with_empty_hand(destination_task, FALSE) + finish_manipulation() return if(destination_task.should_use_filters && isitem(type_to_use) && !destination_task.check_filters_for_atom(type_to_use)) - check_end_of_use_for_use_with_empty_hand(destination_task, FALSE) + finish_manipulation() return + // temporarily move monkey adjacent to target for Adjacency() checks + var/turf/target_turf = get_turf(type_to_use) + if(target_turf && get_turf(src) != target_turf) + var/turf/adjacent_turf = get_step(target_turf, get_dir(target_turf, get_turf(src))) + if(adjacent_turf && !isclosedturf(adjacent_turf)) + monkey_resolve.forceMove(adjacent_turf) + var/old_combat_mode = monkey_resolve.combat_mode monkey_resolve.combat_mode = destination_task.worker_combat_mode monkey_resolve.UnarmedAttack(type_to_use, modifiers = list(RIGHT_CLICK = destination_task.worker_use_rmb ? TRUE : FALSE)) @@ -265,25 +247,26 @@ do_attack_animation(dest_turf) manipulator_arm.do_attack_animation(dest_turf) - check_end_of_use_for_use_with_empty_hand(destination_task, TRUE) + restore_monkey_tracking(monkey_resolve) -/obj/machinery/big_manipulator/proc/check_end_of_use_for_use_with_empty_hand(datum/manipulator_task/cargo/interact/destination_task, item_was_used = TRUE) - if(!on || destination_task.worker_interaction != WORKER_EMPTY_USE) - finish_manipulation() - return + finish_manipulation() - if(!item_was_used) - finish_manipulation() +/// Restores monkey tracking after a temporary move outside src cleared it via Exited. +/obj/machinery/big_manipulator/proc/restore_monkey_tracking(mob/living/carbon/human/species/monkey/monkey) + if(monkey.loc != src) + monkey.forceMove(src) + if(monkey_worker?.resolve()) return - - addtimer(CALLBACK(src, PROC_REF(use_thing_with_empty_hand), destination_task), BASE_INTERACTION_TIME) + monkey_worker = WEAKREF(monkey) + manipulator_arm.vis_contents += monkey + monkey.add_offsets( + type, + x_add = 32 + manipulator_arm.calculate_item_offset(TRUE, pixels_to_offset = 16), + y_add = 32 + manipulator_arm.calculate_item_offset(FALSE, pixels_to_offset = 16) + ) /// Completes the current manipulation action and schedules the next step. /obj/machinery/big_manipulator/proc/finish_manipulation() - if(held_object) - var/obj/resolved = held_object.resolve() - if(resolved && resolved.loc == src) - resolved.forceMove(drop_location()) held_object = null manipulator_arm.update_claw(null) current_task = null diff --git a/code/game/machinery/big_manipulator/interaction_priorities.dm b/code/game/machinery/big_manipulator/interaction_priorities.dm index 0540c1f59275..ac79d2b151ea 100644 --- a/code/game/machinery/big_manipulator/interaction_priorities.dm +++ b/code/game/machinery/big_manipulator/interaction_priorities.dm @@ -35,3 +35,19 @@ /datum/manipulator_priority/interact/with_vehicles name = "USE ON VEHICLES" atom_typepath = /obj/vehicle + +/datum/manipulator_priority/interact/with_turf + name = "USE ON FLOOR" + atom_typepath = /turf + +/datum/manipulator_priority/pickup/items + name = "PICK UP ITEMS" + atom_typepath = /obj/item + +/datum/manipulator_priority/pickup/closets + name = "PICK UP CLOSETS" + atom_typepath = /obj/structure/closet + +/datum/manipulator_priority/pickup/humans + name = "PICK UP HUMANS" + atom_typepath = /mob/living/carbon/human diff --git a/code/game/machinery/big_manipulator/manipulator_tasks.dm b/code/game/machinery/big_manipulator/manipulator_tasks.dm index 00fe12715770..18ce051c9933 100644 --- a/code/game/machinery/big_manipulator/manipulator_tasks.dm +++ b/code/game/machinery/big_manipulator/manipulator_tasks.dm @@ -40,6 +40,53 @@ time_seconds = serialized_data["time_seconds"] return +// ===== STOP ===== + +/datum/manipulator_task/simple/stop + name = "stop" + var/sub_name = "" + +/datum/manipulator_task/simple/stop/New(..., serialized_data) + var/static/list/preset_sub_names = list( + "while at it.", + "and step away.", + "in the name of common sense.", + "just stop.", + "enough of this.", + ) + sub_name = pick(preset_sub_names) + if(serialized_data) + sub_name = serialized_data["sub_name"] || "" + return ..() + +/datum/manipulator_task/simple/stop/can_run(obj/machinery/big_manipulator/manipulator) + return TRUE + +/datum/manipulator_task/simple/stop/run_task(obj/machinery/big_manipulator/manipulator) + manipulator.complete_stopping_task() + +/datum/manipulator_task/simple/stop/serialize() + var/list/data = ..() + data["sub_name"] = sub_name + return data + +// ===== MOVE ===== + +/datum/manipulator_task/cargo/move + name = "move" + +/datum/manipulator_task/cargo/move/fill_priority_list(manipulator_tier) + return list() + +/datum/manipulator_task/cargo/move/can_run(obj/machinery/big_manipulator/manipulator) + return is_valid() + +/datum/manipulator_task/cargo/move/run_task(obj/machinery/big_manipulator/manipulator) + manipulator.rotate_to_point(src, src, PROC_REF(try_move)) + +/datum/manipulator_task/cargo/move/proc/try_move(obj/machinery/big_manipulator/manipulator) + manipulator.schedule_next_cycle() + // ===== BASE CARGO ===== /datum/manipulator_task/cargo @@ -48,11 +95,6 @@ var/offset_dy var/should_use_filters = FALSE var/list/atom_filters = list() - var/filtering_mode = TAKE_ITEMS - var/list/type_filters = list( - /obj/item, - /obj/structure/closet, - ) var/list/interaction_priorities = list() /datum/manipulator_task/cargo/New(turf/new_turf, manipulator_tier, serialized_data) @@ -66,8 +108,6 @@ should_use_filters = !!serialized_data["should_use_filters"] atom_filters = serialized_data["atom_filters"] || list() - filtering_mode = serialized_data["filtering_mode"] - type_filters = serialized_data["type_filters"] || list() var/list/prios_data = serialized_data["interaction_priorities"] if(islist(prios_data)) @@ -166,19 +206,12 @@ if(!target || target.anchored || HAS_TRAIT(target, TRAIT_NODROP)) return FALSE - switch(filtering_mode) - if(TAKE_CLOSETS) - return iscloset(target) - if(TAKE_HUMANS) - return ishuman(target) - if(TAKE_ITEMS) - if(!should_use_filters) - return isitem(target) - for(var/filter_path in atom_filters) - if(istype(target, filter_path)) - return TRUE - return FALSE + if(!should_use_filters) + return isitem(target) + for(var/filter_path in atom_filters) + if(istype(target, filter_path)) + return TRUE return FALSE /datum/manipulator_task/cargo/can_run(obj/machinery/big_manipulator/manipulator) @@ -192,8 +225,6 @@ ) data["should_use_filters"] = should_use_filters data["atom_filters"] = atom_filters - data["filtering_mode"] = filtering_mode - data["type_filters"] = type_filters data["interaction_priorities"] = list() for(var/datum/manipulator_priority/prio as anything in interaction_priorities) data["interaction_priorities"] += list(list( @@ -215,7 +246,40 @@ var/pickup_eagerness = PICKUP_CAN_WAIT /datum/manipulator_task/cargo/pickup/fill_priority_list(manipulator_tier) - return list() + var/list/priorities = list( + new /datum/manipulator_priority/pickup/items, + new /datum/manipulator_priority/pickup/closets, + ) + if(manipulator_tier >= 4) + priorities += new /datum/manipulator_priority/pickup/humans + return priorities + +/datum/manipulator_task/cargo/pickup/check_filters_for_atom(atom/movable/target) + if(!target || target.anchored || HAS_TRAIT(target, TRAIT_NODROP)) + return FALSE + + var/matched_priority = FALSE + for(var/datum/manipulator_priority/pickup/prio in interaction_priorities) + if(!prio.active) + continue + if(istype(target, prio.atom_typepath)) + if(ishuman(target)) + var/mob/living/carbon/human/human_target = target + if(human_target.stat == DEAD) + continue + matched_priority = TRUE + break + + if(!matched_priority) + return FALSE + + if(should_use_filters && length(atom_filters)) + for(var/filter_path in atom_filters) + if(istype(target, filter_path)) + return TRUE + return FALSE + + return TRUE /datum/manipulator_task/cargo/pickup/can_run(obj/machinery/big_manipulator/manipulator) if(!..()) @@ -412,8 +476,6 @@ /datum/manipulator_task/cargo/dropoff_base/use name = "use" - var/worker_interaction = WORKER_NORMAL_USE - var/use_post_interaction = POST_INTERACTION_DROP_AT_POINT var/worker_combat_mode = FALSE var/worker_use_rmb = FALSE var/skip_anchored = FALSE @@ -424,6 +486,7 @@ new /datum/manipulator_priority/interact/with_structure, new /datum/manipulator_priority/interact/with_machinery, new /datum/manipulator_priority/interact/with_items, + new /datum/manipulator_priority/interact/with_turf, ) if(manipulator_tier == 4) priorities += new /datum/manipulator_priority/interact/with_vehicles @@ -438,8 +501,6 @@ /datum/manipulator_task/cargo/dropoff_base/use/serialize() var/list/data = ..() - data["worker_interaction"] = worker_interaction - data["use_post_interaction"] = use_post_interaction data["worker_combat_mode"] = worker_combat_mode data["worker_use_rmb"] = worker_use_rmb data["skip_anchored"] = skip_anchored @@ -448,8 +509,6 @@ /datum/manipulator_task/cargo/dropoff_base/use/New(turf/new_turf, manipulator_tier, serialized_data) ..(new_turf, manipulator_tier, serialized_data) if(serialized_data) - worker_interaction = serialized_data["worker_interaction"] - use_post_interaction = serialized_data["use_post_interaction"] worker_combat_mode = !!serialized_data["worker_combat_mode"] worker_use_rmb = !!serialized_data["worker_use_rmb"] skip_anchored = !!serialized_data["skip_anchored"] @@ -462,8 +521,6 @@ /datum/manipulator_task/cargo/interact name = "interact" - var/worker_interaction = WORKER_EMPTY_USE - var/use_post_interaction = POST_INTERACTION_DROP_AT_POINT var/worker_combat_mode = FALSE var/worker_use_rmb = FALSE var/skip_anchored = FALSE @@ -474,6 +531,7 @@ new /datum/manipulator_priority/interact/with_structure, new /datum/manipulator_priority/interact/with_machinery, new /datum/manipulator_priority/interact/with_items, + new /datum/manipulator_priority/interact/with_turf, ) if(manipulator_tier == 4) priorities += new /datum/manipulator_priority/interact/with_vehicles @@ -489,8 +547,6 @@ /datum/manipulator_task/cargo/interact/serialize() var/list/data = ..() - data["worker_interaction"] = worker_interaction - data["use_post_interaction"] = use_post_interaction data["worker_combat_mode"] = worker_combat_mode data["worker_use_rmb"] = worker_use_rmb data["skip_anchored"] = skip_anchored @@ -499,8 +555,6 @@ /datum/manipulator_task/cargo/interact/New(turf/new_turf, manipulator_tier, serialized_data) ..(new_turf, manipulator_tier, serialized_data) if(serialized_data) - worker_interaction = serialized_data["worker_interaction"] - use_post_interaction = serialized_data["use_post_interaction"] worker_combat_mode = !!serialized_data["worker_combat_mode"] worker_use_rmb = !!serialized_data["worker_use_rmb"] skip_anchored = !!serialized_data["skip_anchored"] diff --git a/code/game/machinery/civilian_bounty/bounty_machinery.dm b/code/game/machinery/civilian_bounty/bounty_machinery.dm index 9b06f6feeb2b..43bf3144ec9b 100644 --- a/code/game/machinery/civilian_bounty/bounty_machinery.dm +++ b/code/game/machinery/civilian_bounty/bounty_machinery.dm @@ -400,7 +400,7 @@ paper.name = "paper - Bounties" var/list/printout_text = list() - printout_text += "

Nanotrasen Cargo Bounties


" + printout_text += "

Nanotrasen Cargo Bounties

" for(var/datum/bounty/current_bounty in GLOB.shared_crew_bounties) if(current_bounty.claimed) @@ -411,7 +411,7 @@
  • Reward: [current_bounty.get_bounty_reward()] cr.
  • Cut: [round(BOUNTY_CUT_STANDARD * current_bounty.get_bounty_reward())] cr.
  • "} - paper.add_raw_text(printout_text.Join("
    ")) + paper.add_raw_text(printout_text.Join("")) paper.update_appearance() playsound(src, 'sound/machines/printer.ogg', 100, TRUE) diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 8fb7fbf6dce8..e41742baf7a5 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -100,11 +100,11 @@ if(!occupier) . += "ai-fixer-empty" return - switch(occupier.stat) - if(CONSCIOUS) - . += "ai-fixer-full" - if(UNCONSCIOUS, HARD_CRIT) - . += "ai-fixer-404" + + if(occupier.stat == HARD_CRIT || IS_UNCONSCIOUS(occupier)) + . += "ai-fixer-404" + else if(!IS_UNCONSCIOUS_OR_CRIT(occupier)) + . += "ai-fixer-full" /obj/machinery/computer/aifixer/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) if(!..()) diff --git a/code/game/machinery/computer/arcade/orion_event.dm b/code/game/machinery/computer/arcade/orion_event.dm index 7c834800f1dd..db5061183b59 100644 --- a/code/game/machinery/computer/arcade/orion_event.dm +++ b/code/game/machinery/computer/arcade/orion_event.dm @@ -526,7 +526,7 @@ playsound(game, 'sound/items/weeoo1.ogg', 100, FALSE) for(var/i in 1 to 3) var/mob/living/basic/trooper/syndicate/ranged/smg/orion/spaceport_security = new(get_turf(game)) - spaceport_security.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, usr) + spaceport_security.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, usr) game.fuel += fuel game.food += food diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 583988ef7e6a..64a37a4e58a3 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -63,13 +63,18 @@ minor_alarms.Cut() // An area list used for station_only circuits, so we only send an alarm if we're in one of the station or mining home areas - var/list/station_alert_areas = GLOB.the_station_areas + typesof(/area/mine) + var/static/list/station_alert_areas = null + if (isnull(station_alert_areas)) + station_alert_areas = list() + for (var/area_type in GLOB.the_station_areas + typesof(/area/mine)) + station_alert_areas[area_type] = TRUE + // Setting up a variable for checking our circuit's station_only var/obj/item/circuitboard/computer/atmos_alert/my_circuit = circuit for (var/obj/machinery/airalarm/air_alarm as anything in GLOB.air_alarms) // If the circuit has station_only, check if alarm areas are in the station list if(my_circuit.station_only) - if (!(air_alarm.my_area.type in station_alert_areas)) + if(!station_alert_areas[air_alarm.my_area.type]) continue // Otherwise just check if alarms match the console's z-level else if (air_alarm.z != z) diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 18139f148078..01278ffbf96b 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -148,7 +148,7 @@ return can_interact(user) /obj/machinery/computer/camera_advanced/abductor/can_use(mob/user) - if(!isabductor(user)) + if(!HAS_MIND_TRAIT(user, TRAIT_ABDUCTOR_KNOWLEDGE)) return FALSE return ..() diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index bf77b85e37e8..06bc865c1ee3 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -262,7 +262,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) entry["life_status"] = tracked_living_mob.stat else if (sensor_mode == SENSOR_LIVING) // binary sensors should only report alive or dead - entry["life_status"] = (tracked_living_mob.stat == DEAD) ? DEAD : CONSCIOUS + entry["life_status"] = (tracked_living_mob.stat == DEAD) ? DEAD : STABLE // Damage if (sensor_mode >= SENSOR_VITALS) diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm deleted file mode 100644 index 641107812079..000000000000 --- a/code/game/machinery/computer/law.dm +++ /dev/null @@ -1,156 +0,0 @@ - - -/obj/machinery/computer/upload - var/mob/living/silicon/current = null //The target of future law uploads - icon_state = MAP_SWITCH("computer", "/obj/machinery/computer/upload") - icon_screen = "command" - time_to_unscrew = 6 SECONDS - var/locked = FALSE - var/lock_timer - -/obj/machinery/computer/upload/Initialize(mapload) - . = ..() - - if(!mapload) - log_silicon("\A [name] was created at [loc_name(src)].") - message_admins("\A [name] was created at [ADMIN_VERBOSEJMP(src)].") - - if(isnull(circuit) || circuit.obj_flags & EMAGGED) - return - - set_locked(TRUE) - - -/obj/machinery/computer/upload/emag_act(mob/user, obj/item/card/emag/emag_card) - if(circuit.obj_flags & EMAGGED) - return FALSE - circuit.obj_flags |= EMAGGED - set_locked(FALSE, user) - circuit.req_access = null - circuit.req_one_access = null - - if(user) - balloon_alert(user, "access restrictions removed!") - return TRUE - -/obj/machinery/computer/upload/proc/should_have_lock() - if(isnull(circuit)) - return FALSE - return length(circuit.req_access) || length(circuit.req_one_access) - -/obj/machinery/computer/upload/proc/set_locked(locked_state = TRUE, mob/user) - if(locked == locked_state) - return - if(!should_have_lock()) - return - locked = !!locked_state - icon_screen = locked ? "command_locked" : "command" - update_appearance(UPDATE_OVERLAYS) - if(user) - balloon_alert(user, locked ? "console locked!" : "console unlocked!") - -/obj/machinery/computer/upload/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers) - if(istype(O, /obj/item/card/id) && should_have_lock()) - if(machine_stat & (NOPOWER|BROKEN|MAINT)) - return - if(circuit.obj_flags & EMAGGED) - balloon_alert(user, "access locks fried!") - return - if(!circuit.check_access(O)) - balloon_alert(user, "access denied!") - return - - set_locked(!locked, user) - if(lock_timer) - deltimer(lock_timer) - lock_timer = null - if(!locked) - lock_timer = addtimer(CALLBACK(src, PROC_REF(set_locked), TRUE), 5 MINUTES, TIMER_UNIQUE | TIMER_STOPPABLE) - - update_appearance(UPDATE_OVERLAYS) - return TRUE - - if(istype(O, /obj/item/ai_module)) - var/obj/item/ai_module/module = O - if(machine_stat & (NOPOWER|BROKEN|MAINT)) - return - if(locked && !module.bypass_access_check) - to_chat(user, span_alert("Console is locked! Swipe an ID card with proper access on the console to unlock it!")) - balloon_alert(user, "console locked!") - return - if(!current) - to_chat(user, span_alert("You haven't selected anything to transmit laws to!")) - return - if(!can_upload_to(current)) - to_chat(user, span_alert("Upload failed! Check to make sure [current.name] is functioning properly.")) - current = null - return - if(!is_valid_z_level(get_turf(current), get_turf(user))) - to_chat(user, span_alert("Upload failed! Unable to establish a connection to [current.name]. You're too far away!")) - current = null - return - module.install(current.laws, user) - imprint_gps("Weak Upload Signal") - else - return ..() - -/obj/machinery/computer/upload/proc/can_upload_to(mob/living/silicon/S) - if(S.stat == DEAD) - return FALSE - return TRUE - -/obj/machinery/computer/upload/ai - icon_state = MAP_SWITCH("computer", "/obj/machinery/computer/upload/ai") // i mean it starts locked by default - name = "\improper AI upload console" - desc = "Used to upload laws to the AI." - circuit = /obj/item/circuitboard/computer/aiupload - -/obj/machinery/computer/upload/ai/Initialize(mapload) - . = ..() - if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI)) - return INITIALIZE_HINT_QDEL - - return . - -/obj/machinery/computer/upload/ai/interact(mob/user) - current = select_active_ai(user, z, TRUE) - - if (!current) - to_chat(user, span_alert("No active AIs detected!")) - else - to_chat(user, span_notice("[current.name] selected for law changes.")) - -/obj/machinery/computer/upload/ai/can_upload_to(mob/living/silicon/ai/A) - if(!A || !isAI(A)) - return FALSE - if(A.control_disabled) - return FALSE - return ..() - - -/obj/machinery/computer/upload/borg - name = "cyborg upload console" - desc = "Used to upload laws to Cyborgs." - circuit = /obj/item/circuitboard/computer/borgupload - -/obj/machinery/computer/upload/borg/interact(mob/user) - current = select_active_free_borg(user) - - if(!current) - to_chat(user, span_alert("No active unslaved cyborgs detected.")) - else - to_chat(user, span_notice("[current.name] selected for law changes.")) - -/obj/machinery/computer/upload/borg/can_upload_to(mob/living/silicon/robot/B) - if(!B || !iscyborg(B)) - return FALSE - if(B.scrambledcodes || B.emagged) - return FALSE - return ..() - - -/obj/machinery/computer/upload/ai/no_lock - circuit = /obj/item/circuitboard/computer/aiupload/no_lock - -/obj/machinery/computer/upload/borg/no_lock - circuit = /obj/item/circuitboard/computer/borgupload/no_lock diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm index 328bbb104ee4..890d7f3a8dc5 100644 --- a/code/game/machinery/computer/operating_computer.dm +++ b/code/game/machinery/computer/operating_computer.dm @@ -118,7 +118,7 @@ if(!is_operational) return UI_CLOSE // if you're knocked out, ie anesthetic... definitely a no-go - if(user.stat >= UNCONSCIOUS || HAS_TRAIT(user, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(user)) return UI_CLOSE // the patient itself should be blocked from viewing the computer if(user.body_position == LYING_DOWN) @@ -174,19 +174,19 @@ data["patient"] = list() var/mob/living/patient = table.patient - switch(patient.stat) - if(CONSCIOUS) - data["patient"]["stat"] = "Conscious" - data["patient"]["statstate"] = "good" - if(SOFT_CRIT) - data["patient"]["stat"] = "Critical Condition" - data["patient"]["statstate"] = "average" - if(UNCONSCIOUS, HARD_CRIT) - data["patient"]["stat"] = "Unconscious" - data["patient"]["statstate"] = "average" - if(DEAD) - data["patient"]["stat"] = "Dead" - data["patient"]["statstate"] = "bad" + if(patient.stat == DEAD) + data["patient"]["stat"] = "Dead" + data["patient"]["statstate"] = "bad" + else if (patient.stat == HARD_CRIT || patient.stat == SOFT_CRIT) + data["patient"]["stat"] = "Critical" + data["patient"]["statstate"] = patient.stat == HARD_CRIT ? "bad" : "average" + else if (IS_UNCONSCIOUS(patient)) + data["patient"]["stat"] = "Unconscious" + data["patient"]["statstate"] = "average" + else + data["patient"]["stat"] = "Stable" + data["patient"]["statstate"] = "good" + data["patient"]["health"] = patient.health data["patient"]["blood_type"] = patient.get_bloodtype()?.name || "UNKNOWN" data["patient"]["maxHealth"] = patient.maxHealth diff --git a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm index 92cdeb84dc8b..82926e6c1494 100644 --- a/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm +++ b/code/game/machinery/computer/orders/order_items/cook/order_veggies.dm @@ -153,7 +153,7 @@ /datum/orderable_item/veggies/trumpet name = "Spaceman's Trumpet" - purchase_path = /obj/item/food/grown/trumpet + purchase_path = /obj/item/food/grown/flower/trumpet cost_per_order = 25 /datum/orderable_item/veggies/banana diff --git a/code/game/machinery/computer/telescreen.dm b/code/game/machinery/computer/telescreen.dm index 83a821fe2229..beecf133beeb 100644 --- a/code/game/machinery/computer/telescreen.dm +++ b/code/game/machinery/computer/telescreen.dm @@ -101,7 +101,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai return UI_CLOSE if(!isliving(user)) return isAdminGhostAI(user) ? UI_INTERACTIVE : UI_UPDATE - if(user.stat >= SOFT_CRIT) + if(user.incapacitated) return UI_UPDATE var/can_range = FALSE @@ -141,9 +141,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai else if(!can_see(watcher, src, 7)) return FALSE - if(watcher.is_blind()) - return FALSE - if(!isobserver(watcher) && watcher.stat >= UNCONSCIOUS) + if(watcher.is_blind() || IS_UNCONSCIOUS(watcher)) return FALSE return TRUE @@ -571,4 +569,3 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/monaster /obj/item/wallframe/telescreen/monastery name = "monastery telescreen frame" result_path = /obj/machinery/computer/security/telescreen/monastery - diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm index e972b32b63e3..f477cb6c3c57 100644 --- a/code/game/machinery/dna_infuser/dna_infuser.dm +++ b/code/game/machinery/dna_infuser/dna_infuser.dm @@ -180,7 +180,7 @@ return ITEM_INTERACT_SUCCESS /obj/machinery/dna_infuser/relaymove(mob/living/user, direction) - if(user.stat) + if(IS_UNCONSCIOUS_OR_CRIT(user)) if(COOLDOWN_FINISHED(src, message_cooldown)) COOLDOWN_START(src, message_cooldown, 4 SECONDS) to_chat(user, span_warning("[src]'s door won't budge!")) diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm index 2cd9555676c2..c0a1690e4ab8 100644 --- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm @@ -91,7 +91,7 @@ /obj/item/organ/tongue/carp/on_life(seconds_per_tick) . = ..() - if(owner.stat != CONSCIOUS || !prob(0.1)) + if(IS_UNCONSCIOUS_OR_CRIT(owner) || !prob(0.1)) return owner.emote("cough") var/turf/tooth_fairy = get_turf(owner) diff --git a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm index 1af4ad7f2ee3..a6b0bb4a3019 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm @@ -57,6 +57,7 @@ toxic_foodtypes = NONE // these fucks eat vomit, i am sure they can handle drinking bleach or whatever too modifies_speech = TRUE languages_native = list(/datum/language/buzzwords) + organ_traits = list(TRAIT_VOMIT_SLURPER) var/static/list/speech_replacements = list( new /regex("z+", "g") = "zzz", new /regex("Z+", "g") = "ZZZ", diff --git a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm index cb3c8361b371..a3d48aa57f91 100644 --- a/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/roach_organs.dm @@ -130,7 +130,7 @@ /obj/item/organ/heart/roach/proc/is_blocking(mob/living/carbon/human/blocker, damage_amount, damagetype, attack_direction) if(damage_amount < 5 || damagetype != BRUTE || !attack_direction) return - if(!ishuman(blocker) || blocker.stat >= UNCONSCIOUS) + if(!ishuman(blocker) || IS_UNCONSCIOUS(blocker)) return FALSE // No tactical spinning if(HAS_TRAIT(blocker, TRAIT_SPINNING)) diff --git a/code/game/machinery/dna_infuser/organ_sets/stoat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/stoat_organs.dm index e9316d4270d0..c2991ae85d8c 100644 --- a/code/game/machinery/dna_infuser/organ_sets/stoat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/stoat_organs.dm @@ -58,7 +58,7 @@ return SUCCESSFUL_BLOCK /datum/status_effect/organ_set_bonus/stoat/proc/is_dangerous_mob(mob/living/target) - if(target.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(target)) return FALSE if(istype(target, /mob/living/basic/stoat)) return owner.gender == MALE && target.gender == MALE // other stoats are ENEMIES if we are both males @@ -72,7 +72,7 @@ return FALSE /datum/status_effect/organ_set_bonus/stoat/proc/is_friendly_mob(mob/living/target) - if(target.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(target)) return FALSE if(istype(target, /mob/living/basic/stoat)) return owner.gender != MALE || target.gender != MALE diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm index 769f357740c6..6cfa45868571 100644 --- a/code/game/machinery/dna_scanner.dm +++ b/code/game/machinery/dna_scanner.dm @@ -15,7 +15,7 @@ var/scan_level var/precision_coeff = 1 var/message_cooldown - var/breakout_time = 1200 + var/breakout_time = 120 SECONDS var/obj/machinery/computer/dna_console/linked_console = null /obj/machinery/dna_scannernew/RefreshParts() @@ -70,16 +70,19 @@ open_machine() /obj/machinery/dna_scannernew/container_resist_act(mob/living/user) - if(!locked) + if(HAS_TRAIT(user, TRAIT_PRIMITIVE) || user.ai_controller) + if(locked) + return //Your primitive brain cant escape a dna scanner noob + else if(!locked) //Not locked and not primitive? escape immediately open_machine() - return + user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT user.visible_message(span_notice("You see [user] kicking against the door of [src]!"), \ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear a metallic creaking from [src].")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open || !locked || HAS_TRAIT(user, TRAIT_PRIMITIVE) || user.ai_controller) return locked = FALSE user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ @@ -109,6 +112,8 @@ /obj/machinery/dna_scannernew/open_machine(drop = TRUE, density_to_set = FALSE) if(state_open) return FALSE + if(locked) //haha bro u cant open it its locked xD + return FALSE ..() @@ -118,7 +123,7 @@ return TRUE /obj/machinery/dna_scannernew/relaymove(mob/living/user, direction) - if(user.stat || locked) + if(IS_UNCONSCIOUS_OR_CRIT(user) || locked) if(message_cooldown <= world.time) message_cooldown = world.time + 50 to_chat(user, span_warning("[src]'s door won't budge!")) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 2fd10a28715b..45faaa3257f3 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -257,7 +257,7 @@ return if(ismob(AM)) var/mob/B = AM - if((isdrone(B) || iscyborg(B)) && B.stat) + if((isdrone(B) || iscyborg(B)) && IS_UNCONSCIOUS_OR_CRIT(B)) return if(isliving(AM)) var/mob/living/M = AM @@ -376,7 +376,7 @@ stoplag(1) // allow the door to process any allow/deny responses first var/do_after_time = rand(delayed_unres_time_lower, delayed_unres_time_upper) ADD_TRAIT(opener, TRAIT_UNRESTRICTED_AIRLOCK_OPENING, REF(src)) - RegisterSignal(opener, COMSIG_ATOM_PRE_PRESSURE_PUSH, PROC_REF(stop_pressure_during_unres_open)) + RegisterSignal(opener, COMSIG_ATOM_PRE_PRESSURE_PUSH, PROC_REF(stop_pressure_during_unres_open), override = TRUE) addtimer(CALLBACK(src, PROC_REF(deregister_pressure_push_signal), opener), do_after_time + 0.5 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) // extra half-second to be safe, else this is just a guarantee we remove the signal. SSblackbox.record_feedback("tally", "unrestricted_airlock_usage", 1, "open attempt ([type])") // statcollecting on how often people try to use this. diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index b5228803e616..a907d4e6d9e9 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -377,8 +377,8 @@ active = TRUE alarm_type = code add_as_source() - update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. correct_state() + update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. /// Adds this fire door as a source of trouble to all of its areas /obj/machinery/door/firedoor/proc/add_as_source() @@ -404,8 +404,8 @@ remove_as_source() soundloop.stop() is_playing_alarm = FALSE - update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. correct_state() + update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. /** * Open the firedoor without resetting existing alarms @@ -422,8 +422,8 @@ soundloop.stop() is_playing_alarm = FALSE remove_as_source() - update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. correct_state() + update_appearance(UPDATE_ICON) //Sets the door lights even if the door doesn't move. /// Please be called 3 seconds after the LAST open, rather then 3 seconds after the first addtimer(CALLBACK(src, PROC_REF(release_constraints)), 3 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 02c09f285168..519b221c735f 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -170,7 +170,7 @@ if(!SSticker) return var/mob/M = AM - if(HAS_TRAIT(M, TRAIT_HANDS_BLOCKED) || ((isdrone(M) || iscyborg(M)) && M.stat != CONSCIOUS)) + if(HAS_TRAIT(M, TRAIT_HANDS_BLOCKED) || ((isdrone(M) || iscyborg(M)) && IS_UNCONSCIOUS_OR_CRIT(M))) return bumpopen(M) @@ -353,11 +353,15 @@ add_atom_colour(NARSIE_WINDOW_COLOUR, FIXED_COLOUR_PRIORITY) /obj/machinery/door/window/rust_heretic_act() + if(HAS_TRAIT(src, TRAIT_RUSTY)) + return FALSE + add_atom_colour(COLOR_RUSTED_GLASS, FIXED_COLOUR_PRIORITY) AddElement(/datum/element/rust) set_armor(/datum/armor/none) take_damage(get_integrity() * 0.5) modify_max_integrity(initial(max_integrity) * 0.2) + return TRUE /obj/machinery/door/window/should_atmos_process(datum/gas_mixture/air, exposed_temperature) return (exposed_temperature > T0C + (reinf ? 1600 : 800)) diff --git a/code/game/machinery/experimental_cloner/experimental_cloner_computer.dm b/code/game/machinery/experimental_cloner/experimental_cloner_computer.dm index 2fc3ea73eda2..7709dc966935 100644 --- a/code/game/machinery/experimental_cloner/experimental_cloner_computer.dm +++ b/code/game/machinery/experimental_cloner/experimental_cloner_computer.dm @@ -21,6 +21,10 @@ . = ..() find_connections = mapload +/obj/machinery/computer/experimental_cloner/Destroy(force) + QDEL_NULL(stored_record) + return ..() + /obj/machinery/computer/experimental_cloner/post_machine_initialize() . = ..() if (find_connections) @@ -110,7 +114,7 @@ data["is_cloning"] = !!output?.running data["cloning_name"] = output?.loaded_record?.name data["cloning_species"] = output?.loaded_record?.dna?.species?.name - data["cloning_progress"] = output? ((output.cloning_time - cloning_time_left) / output.cloning_time) * 100 : 0 + data["cloning_progress"] = output?.cloning_time > 0 ? ((output.cloning_time - cloning_time_left) / output.cloning_time) * 100 : 0 return data diff --git a/code/game/machinery/experimental_cloner/experimental_cloner_fuckup.dm b/code/game/machinery/experimental_cloner/experimental_cloner_fuckup.dm index f9f612716a4b..acaf452f748c 100644 --- a/code/game/machinery/experimental_cloner/experimental_cloner_fuckup.dm +++ b/code/game/machinery/experimental_cloner/experimental_cloner_fuckup.dm @@ -45,8 +45,14 @@ victim.set_facial_hairstyle("Shaved", update = FALSE) victim.set_hairstyle("Bald", update = TRUE) -/datum/experimental_cloner_fuckup/bald/is_valid(species_type) - return !is_path_in_list(/datum/species/human, /datum/species/ethereal) +/datum/experimental_cloner_fuckup/bald/is_valid(datum/species/species_type) + if(!ispath(species_type)) + return + var/datum/species/proto_species = GLOB.species_prototypes[species_type] + var/obj/item/bodypart/head/fucked_up_head = proto_species.bodypart_overrides[BODY_ZONE_HEAD] + var/head_flags = initial(fucked_up_head.head_flags) + + return (head_flags & HEAD_HAIR || head_flags & HEAD_FACIAL_HAIR) /// Give a brain trauma or two /datum/experimental_cloner_fuckup/brain_trauma diff --git a/code/game/machinery/experimental_cloner/experimental_cloner_pod.dm b/code/game/machinery/experimental_cloner/experimental_cloner_pod.dm index 617d013fab34..70c89890151f 100644 --- a/code/game/machinery/experimental_cloner/experimental_cloner_pod.dm +++ b/code/game/machinery/experimental_cloner/experimental_cloner_pod.dm @@ -31,6 +31,11 @@ GLOBAL_VAR_INIT(experimental_cloner_fuckup_chance, 50) . = ..() sound_loop = new(src, FALSE) +/obj/machinery/experimental_cloner/Destroy(force) + QDEL_NULL(loaded_record) + QDEL_NULL(sound_loop) + return ..() + /obj/machinery/experimental_cloner/power_change() . = ..() if (machine_stat & NOPOWER && running) @@ -126,10 +131,10 @@ GLOBAL_VAR_INIT(experimental_cloner_fuckup_chance, 50) chosen_one.log_message("took control of experimental clone of [result].", LOG_GAME) result.PossessByPlayer(chosen_one.ckey) - to_chat(chosen_one, span_boldnotice("You are [loaded_record.name]! You aren't quite sure where you are or how you got here, though.")) + to_chat(result, span_boldnotice("You are [loaded_record.name]! You aren't quite sure where you are or how you got here, though.")) var/policy = get_policy(ROLE_EXPERIMENTAL_CLONER) if (policy) - to_chat(chosen_one, span_notice(policy)) + to_chat(result, span_notice(policy)) UnregisterSignal(result, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING, COMSIG_LIVING_DEATH)) result.forceMove(drop_location()) diff --git a/code/game/machinery/experimental_cloner/experimental_cloner_scanner.dm b/code/game/machinery/experimental_cloner/experimental_cloner_scanner.dm index e1e541e19f98..335bd4e98d0f 100644 --- a/code/game/machinery/experimental_cloner/experimental_cloner_scanner.dm +++ b/code/game/machinery/experimental_cloner/experimental_cloner_scanner.dm @@ -31,6 +31,10 @@ . = ..() soundloop = new (src) +/obj/machinery/experimental_cloner_scanner/Destroy(force) + QDEL_NULL(soundloop) + return ..() + /// Scan the occupant, eventually producing a [/datum/experimental_cloning_record]. Returns FALSE if unsuccessful. /obj/machinery/experimental_cloner_scanner/proc/start_scan() if (machine_stat & BROKEN || machine_stat & NOPOWER || isnull(occupant)) @@ -110,7 +114,7 @@ balloon_alert(user, "breaking out...") if (!do_after(user,(breakout_time), target = src)) return - if (!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked) + if (!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open || !locked) return locked = FALSE @@ -119,7 +123,7 @@ open_machine() /obj/machinery/experimental_cloner_scanner/relaymove(mob/living/user, direction) - if (user.stat || locked) + if (IS_UNCONSCIOUS_OR_CRIT(user) || locked) if (COOLDOWN_FINISHED(src, message_cooldown)) COOLDOWN_START(src, message_cooldown, breakout_time) balloon_alert(user, "door locked!") diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm index a0756ca11db3..270abe734f80 100644 --- a/code/game/machinery/fat_sucker.dm +++ b/code/game/machinery/fat_sucker.dm @@ -80,8 +80,8 @@ user.visible_message(span_notice("You see [user] kicking against the door of [src]!"), \ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear a metallic creaking from [src].")) - if(do_after(user, breakout_time, target = src, hidden = TRUE)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) + if(do_after(user, breakout_time, target = src, cog_icon = null)) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open) return free_exit = TRUE user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ diff --git a/code/game/machinery/gulag_teleporter.dm b/code/game/machinery/gulag_teleporter.dm index e4e6fc242912..bd78da0a129c 100644 --- a/code/game/machinery/gulag_teleporter.dm +++ b/code/game/machinery/gulag_teleporter.dm @@ -79,7 +79,7 @@ The console is located at computer/gulag_teleporter.dm /obj/machinery/gulag_teleporter/relaymove(mob/living/user, direction) - if(user.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return if(locked) if(message_cooldown <= world.time) @@ -105,7 +105,7 @@ The console is located at computer/gulag_teleporter.dm ) if(do_after(user, resist_time, target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open || !locked) return locked = FALSE user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index c2b5bd6c0716..af37274da632 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -85,7 +85,7 @@ say("Subject is not organic.") playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE) return - if(!allow_living && !(carbon_occupant.stat == DEAD || HAS_TRAIT(carbon_occupant, TRAIT_FAKEDEATH))) //I mean, the machines scanners arent advanced enough to tell you're alive + if(!allow_living && !IS_DEAD_OR_FAKING(carbon_occupant)) say("Subject is still alive.") playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE) return @@ -97,7 +97,7 @@ var/mob/living/carbon/carbon_occupant = occupant - if(carbon_occupant.stat < UNCONSCIOUS) + if(carbon_occupant.client && carbon_occupant.stat <= HARD_CRIT) notify_ghosts( "[carbon_occupant.real_name] is about to be ground up by a malfunctioning organ harvester!", source = src, diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm index 09b18eda780a..ea729a66fd58 100644 --- a/code/game/machinery/hypnochair.dm +++ b/code/game/machinery/hypnochair.dm @@ -185,7 +185,7 @@ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(600)].)"), \ span_hear("You hear a metallic creaking from [src].")) if(do_after(user,(600), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open) return user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ span_notice("You successfully break out of [src]!")) diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm index a6b6452a0d2b..86a27d41c889 100644 --- a/code/game/machinery/medical_kiosk.dm +++ b/code/game/machinery/medical_kiosk.dm @@ -246,7 +246,7 @@ if(istype(disease, /datum/disease/advance)) var/datum/disease/advance/advanced_disease = disease cure_text = advanced_disease.generate_cure_text(2) - sickness_data = "\nName: [disease.name].\nType: [disease.spread_text].\nStage: [disease.stage]/[disease.max_stages].\nPossible Cure: [cure_text]" + sickness_data = "\nName: [disease.name].\nType: [disease.spread_text].\nStage: [disease.stage]/[disease.max_stages].\nCure: [cure_text]" if(patient.can_bleed()) //Blood levels Information blood_name = LOWER_TEXT(blood_type.get_blood_name()) @@ -323,7 +323,7 @@ if (patient.has_status_effect(/datum/status_effect/hallucination)) hallucination_status = "Subject appears to be hallucinating. Suggested treatments: Antipsychotic medication, [/datum/reagent/medicine/haloperidol::name] or [/datum/reagent/medicine/synaptizine::name]." - if(patient.stat == DEAD || HAS_TRAIT(patient, TRAIT_FAKEDEATH) || ((brute_loss+fire_loss+tox_loss+oxy_loss) >= 200)) //Patient status checks. + if(IS_DEAD_OR_FAKING(patient)) //Patient status checks. patient_status = "Dead." if((brute_loss+fire_loss+tox_loss+oxy_loss) >= 80) patient_status = "Gravely Injured" diff --git a/code/game/machinery/newscaster/newscaster_data.dm b/code/game/machinery/newscaster/newscaster_data.dm index 06d0d0707c0c..8978b42d48bf 100644 --- a/code/game/machinery/newscaster/newscaster_data.dm +++ b/code/game/machinery/newscaster/newscaster_data.dm @@ -234,8 +234,9 @@ GLOBAL_LIST_EMPTY(request_list) ) send2otherserver(html_decode(station_name()), channel_name, "create_news_article", additional_data = payload) - for(var/obj/machinery/newscaster/caster in GLOB.allCasters) - caster.news_alert(channel_name, update_alert) + if(update_alert) + for(var/obj/machinery/newscaster/caster as anything in GLOB.allCasters) + caster.news_alert(channel_name) return new_article ///Submits a comment on the news network @@ -259,9 +260,8 @@ GLOBAL_LIST_EMPTY(request_list) wanted_issue.img = picture.picture_image wanted_issue.photo_file = save_photo(picture.picture_image) if(newMessage) - for(var/obj/machinery/newscaster/N in GLOB.allCasters) + for(var/obj/machinery/newscaster/N as anything in GLOB.allCasters) N.news_alert() - N.update_appearance() /datum/feed_network/proc/delete_wanted() wanted_issue.active = FALSE @@ -269,7 +269,7 @@ GLOBAL_LIST_EMPTY(request_list) wanted_issue.body = null wanted_issue.scanned_user = null wanted_issue.img = null - for(var/obj/machinery/newscaster/updated_newscaster in GLOB.allCasters) + for(var/obj/machinery/newscaster/updated_newscaster as anything in GLOB.allCasters) updated_newscaster.update_appearance() /datum/feed_network/proc/save_photo(icon/photo) diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 92ff8502ef71..cc759586846d 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -442,7 +442,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) if("clearWantedIssue") clear_wanted_issue(user) - for(var/obj/machinery/newscaster/other_newscaster in GLOB.allCasters) + for(var/obj/machinery/newscaster/other_newscaster as anything in GLOB.allCasters) other_newscaster.update_appearance() return TRUE @@ -634,18 +634,17 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) /** * When a new feed message is made that will alert all newscasters, this causes the newscasters to sent out a spoken message as well as create a sound. */ -/obj/machinery/newscaster/proc/news_alert(channel, update_alert = TRUE) +/obj/machinery/newscaster/proc/news_alert(channel) if(channel) - if(update_alert) - say("Breaking news from [channel]!") - playsound(loc, 'sound/machines/beep/twobeep_high.ogg', 75, TRUE) alert = TRUE + say("Breaking news from [channel]!") + playsound(src, 'sound/machines/beep/twobeep_high.ogg', 75, TRUE) update_appearance() addtimer(CALLBACK(src, PROC_REF(remove_alert)), ALERT_DELAY, TIMER_UNIQUE|TIMER_OVERRIDE) - else if(!channel && update_alert) + else say("Attention! Wanted issue distributed!") - playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, TRUE) + playsound(src, 'sound/machines/warning-buzzer.ogg', 75, TRUE) /** * Performs a series of sanity checks before giving the user confirmation to create a new feed_channel using channel_name, and channel_desc. diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index ac6f0c45f947..9cc318779a84 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -472,7 +472,7 @@ DEFINE_BITFIELD(turret_flags, list( if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS)//if it's set to check for simple animals if(isanimal_or_basicmob(A)) var/mob/living/animal = A - if(animal.stat || in_faction(animal)) //don't target if dead or in faction + if(IS_UNCONSCIOUS_OR_CRIT(animal) || in_faction(animal)) //don't target if dead or in faction continue targets += animal continue @@ -488,7 +488,7 @@ DEFINE_BITFIELD(turret_flags, list( if(iscyborg(sillycone)) var/mob/living/silicon/robot/sillyconerobot = A - if(sillyconerobot.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(sillyconerobot)) continue if(in_faction(sillyconerobot)) // borgs in faction are friendly continue diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index c74466e21ced..8175bf9d7b29 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -95,7 +95,7 @@ begin_processing() /obj/machinery/recharge_station/relaymove(mob/living/user, direction) - if(user.stat) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return open_machine() diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 5564e489d7a2..bdfcfbd1618e 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -69,12 +69,6 @@ default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/machinery/recycler/can_be_unfasten_wrench(mob/user, silent) - if(!(isfloorturf(loc) || isindestructiblefloor(loc)) && !anchored) - to_chat(user, span_warning("[src] needs to be on the floor to be secured!")) - return FAILED_UNFASTEN - return SUCCESSFUL_UNFASTEN - /obj/machinery/recycler/crowbar_act(mob/living/user, obj/item/tool) return default_deconstruction_crowbar(user, tool) @@ -280,7 +274,7 @@ else playsound(src, 'sound/effects/splat.ogg', 50, TRUE) - if(iscarbon(living_mob) && living_mob.stat == CONSCIOUS) + if(iscarbon(living_mob) && !IS_UNCONSCIOUS_OR_CRIT(living_mob)) living_mob.say("ARRRRRRRRRRRGH!!!", forced= "recycler grinding") if(!issilicon(living_mob)) diff --git a/code/game/machinery/sleepers.dm b/code/game/machinery/sleepers.dm index e73d2d9716c5..fd8e69d4cd6d 100644 --- a/code/game/machinery/sleepers.dm +++ b/code/game/machinery/sleepers.dm @@ -184,18 +184,32 @@ if(mob_occupant) data["occupant"]["name"] = mob_occupant.name switch(mob_occupant.stat) - if(CONSCIOUS) - data["occupant"]["stat"] = "Conscious" + if(STABLE) + data["occupant"]["stat"] = "Stable" data["occupant"]["statstate"] = "good" if(SOFT_CRIT) - data["occupant"]["stat"] = "Conscious" - data["occupant"]["statstate"] = "average" - if(UNCONSCIOUS, HARD_CRIT) - data["occupant"]["stat"] = "Unconscious" + data["occupant"]["stat"] = "Critical" data["occupant"]["statstate"] = "average" + if(HARD_CRIT) + data["occupant"]["stat"] = "Critical" + data["occupant"]["statstate"] = "bad" if(DEAD) data["occupant"]["stat"] = "Dead" data["occupant"]["statstate"] = "bad" + + if(mob_occupant.stat == DEAD) + data["occupant"]["stat"] = "Dead" + data["occupant"]["statstate"] = "bad" + else if (mob_occupant.stat == HARD_CRIT || mob_occupant.stat == SOFT_CRIT) + data["occupant"]["stat"] = "Critical" + data["occupant"]["statstate"] = mob_occupant.stat == HARD_CRIT ? "bad" : "average" + else if (IS_UNCONSCIOUS(mob_occupant)) + data["occupant"]["stat"] = "Unconscious" + data["occupant"]["statstate"] = "average" + else + data["occupant"]["stat"] = "Stable" + data["occupant"]["statstate"] = "good" + data["occupant"]["health"] = mob_occupant.health data["occupant"]["maxHealth"] = mob_occupant.maxHealth data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 867319561574..0f34962673c1 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -355,7 +355,7 @@ return var/turf/turf = get_turf(cell) forceMove(turf) - attackby(cell, user) //puts it into the heater + try_insert_cell(user, cell, TRUE) //puts it into the heater /obj/machinery/space_heater/improvised_chem_heater/heating_examine() . = ..() @@ -424,17 +424,7 @@ /obj/machinery/space_heater/improvised_chem_heater/item_interaction(mob/living/user, obj/item/tool, list/modifiers) add_fingerprint(user) if(istype(tool, /obj/item/stock_parts/power_store/cell)) - if(cell) - to_chat(user, span_warning("There is already a power cell inside!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - cell = tool - tool.add_fingerprint(usr) - - user.visible_message(span_notice("\The [user] inserts a power cell into \the [src]."), span_notice("You insert the power cell into \the [src].")) - SStgui.update_uis(src) - return ITEM_INTERACT_SUCCESS + return try_insert_cell(user, tool) //reagent containers if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container()) @@ -452,6 +442,22 @@ tool.interact_with_atom(beaker, user) return ITEM_INTERACT_SUCCESS +/obj/machinery/space_heater/improvised_chem_heater/proc/try_insert_cell(mob/living/user, obj/item/stock_parts/power_store/cell/battery, silent = FALSE) + if(cell) + to_chat(user, span_warning("There is already a power cell inside!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(battery, src)) + return ITEM_INTERACT_BLOCKING + + cell = battery + battery.add_fingerprint(user) + + if(!silent) + user.visible_message(span_notice("\The [user] inserts a power cell into \the [src]."), span_notice("You insert the power cell into \the [src].")) + SStgui.update_uis(src) + return ITEM_INTERACT_SUCCESS + /obj/machinery/space_heater/crowbar_act(mob/living/user, obj/item/tool) add_fingerprint(user) return default_deconstruction_crowbar(user, tool) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 838e902f0bc4..92341ed722f6 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -407,7 +407,7 @@ else if (occupant) var/mob/living/mob_occupant = occupant - to_chat(mob_occupant, span_userdanger("[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!")) + to_chat(mob_occupant, span_userdanger("[src]'s confines grow warm, then hot, then scorching. You're being burned [mob_occupant.stat == DEAD ? "away" : "alive"]!")) cook() if ("lock", "unlock") if(locked && !access_check(user)) @@ -424,7 +424,7 @@ else var/obj/item/in_hands = user.get_active_held_item() if (in_hands) - attackby(in_hands, user) + try_insert_item(user, in_hands) update_icon() interact(user) @@ -496,7 +496,7 @@ mob_occupant.adjust_fire_loss(rand(20, 36)) else mob_occupant.adjust_fire_loss(rand(10, 16)) - if(iscarbon(mob_occupant) && mob_occupant.stat < UNCONSCIOUS) + if(iscarbon(mob_occupant) && !IS_UNCONSCIOUS(mob_occupant)) //Awake, organic and screaming mob_occupant.emote("scream") addtimer(CALLBACK(src, PROC_REF(cook)), 5 SECONDS) @@ -585,7 +585,7 @@ span_notice("You start kicking against the doors... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear a thump from [src].")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src ) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src ) return user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ span_notice("You successfully break out of [src]!")) @@ -602,7 +602,7 @@ dump_inventory_contents() /obj/machinery/suit_storage_unit/proc/resist_open(mob/user) - if(!state_open && occupant && (user in src) && user.stat == CONSCIOUS) // Check they're still here. + if(!state_open && occupant && (user in src) && !IS_UNCONSCIOUS_OR_CRIT(user)) // Check they're still here. visible_message(span_notice("You see [user] burst out of [src]!"), \ span_notice("You escape the cramped confines of [src]!")) open_machine() @@ -688,44 +688,7 @@ return ITEM_INTERACT_SUCCESS if(state_open && is_operational) - if(istype(tool, /obj/item/clothing/suit)) - if(suit) - to_chat(user, span_warning("The unit already contains a suit!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - suit = tool - else if(istype(tool, /obj/item/clothing/head)) - if(helmet) - to_chat(user, span_warning("The unit already contains a helmet!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - helmet = tool - else if(istype(tool, /obj/item/clothing/mask)) - if(mask) - to_chat(user, span_warning("The unit already contains a mask!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - mask = tool - else if(istype(tool, /obj/item/storage/backpack) || istype(tool, /obj/item/mod/control)) - if(mod) - to_chat(user, span_warning("The unit already contains a backpack or MOD!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - mod = tool - else - if(storage) - to_chat(user, span_warning("The auxiliary storage compartment is full!")) - return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) - return ITEM_INTERACT_BLOCKING - storage = tool - visible_message(span_notice("[user] inserts [tool] into [src]"), span_notice("You load [tool] into [src].")) - update_appearance() - return ITEM_INTERACT_SUCCESS + return try_insert_item(user, tool) if(panel_open) if(is_wire_tool(tool)) @@ -772,3 +735,53 @@ return mod.disable_modlink() + +/obj/machinery/suit_storage_unit/proc/try_insert_item(mob/living/user, obj/item/to_insert) + if(istype(to_insert, /obj/item/clothing/suit)) + if(suit) + to_chat(user, span_warning("The unit already contains a suit!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(to_insert, src)) + return ITEM_INTERACT_BLOCKING + + suit = to_insert + else if(istype(to_insert, /obj/item/clothing/head)) + if(helmet) + to_chat(user, span_warning("The unit already contains a helmet!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(to_insert, src)) + return ITEM_INTERACT_BLOCKING + + helmet = to_insert + else if(istype(to_insert, /obj/item/clothing/mask)) + if(mask) + to_chat(user, span_warning("The unit already contains a mask!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(to_insert, src)) + return ITEM_INTERACT_BLOCKING + + mask = to_insert + else if(istype(to_insert, /obj/item/storage/backpack) || istype(to_insert, /obj/item/mod/control)) + if(mod) + to_chat(user, span_warning("The unit already contains a backpack or MOD!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(to_insert, src)) + return ITEM_INTERACT_BLOCKING + + mod = to_insert + else + if(storage) + to_chat(user, span_warning("The auxiliary storage compartment is full!")) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(to_insert, src)) + return ITEM_INTERACT_BLOCKING + + storage = to_insert + visible_message(span_notice("[user] inserts [to_insert] into [src]"), span_notice("You load [to_insert] into [src].")) + update_appearance() + return ITEM_INTERACT_SUCCESS diff --git a/code/game/machinery/wall_vitals.dm b/code/game/machinery/wall_vitals.dm index 8c4065562b9e..eef4ccfbf379 100644 --- a/code/game/machinery/wall_vitals.dm +++ b/code/game/machinery/wall_vitals.dm @@ -310,7 +310,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/vitals_reader/advanced, 32) /obj/machinery/vitals_reader/proc/get_ekg_and_resp(hp_color) var/ekg_icon_state = "ekg" var/resp_icon_state = "resp" - if(!patient.appears_alive()) + if(IS_DEAD_OR_FAKING(patient)) ekg_icon_state = "ekg_flat" resp_icon_state = "resp_flat" @@ -424,7 +424,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/vitals_reader/advanced, 32) var/patient_stat = patient.stat if(machine_stat & (EMPED|EMAGGED)) - patient_stat = pick(CONSCIOUS, SOFT_CRIT, HARD_CRIT, DEAD, DEAD, DEAD) + patient_stat = pick(STABLE, SOFT_CRIT, HARD_CRIT, DEAD, DEAD, DEAD) switch(patient_stat) if(DEAD) @@ -444,9 +444,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/vitals_reader/advanced, 32) last_reported_stat = SOFT_CRIT else COOLDOWN_START(src, beep_cd, 7 SECONDS) - if(last_reported_stat != CONSCIOUS) + if(last_reported_stat != STABLE) beep_message("lets out a beep.") - last_reported_stat = CONSCIOUS + last_reported_stat = STABLE /obj/machinery/vitals_reader/proc/beep_message(message) for(var/mob/viewer as anything in viewers(src)) diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm index c9544008e828..ab4d69f95ff1 100644 --- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm +++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm @@ -63,9 +63,7 @@ for(var/mob/living/carbon/target in GLOB.player_list) if (target.z != z) continue - if (HAS_TRAIT(target, TRAIT_GODMODE)) - continue - if (target.stat >= UNCONSCIOUS) + if (HAS_TRAIT(target, TRAIT_GODMODE) || IS_UNCONSCIOUS(target)) continue // Don't just haunt a corpse var/distance_from_target = get_dist(src, target) if(distance_from_target >= closest_distance) diff --git a/code/game/objects/effects/anomalies/anomalies_gravity.dm b/code/game/objects/effects/anomalies/anomalies_gravity.dm index 280e0dc1e12f..eb9ec7e2a765 100644 --- a/code/game/objects/effects/anomalies/anomalies_gravity.dm +++ b/code/game/objects/effects/anomalies/anomalies_gravity.dm @@ -32,7 +32,7 @@ if(O.anchored || HAS_TRAIT(O, TRAIT_UNDERFLOOR)) continue var/mob/living/target = locate() in view(4,src) - if(target && !target.stat && prob(object_launch_prob)) + if(target && !IS_UNCONSCIOUS_OR_CRIT(target) && prob(object_launch_prob)) O.throw_at(target, 5, 10) /obj/effect/anomaly/grav/proc/on_entered(datum/source, atom/movable/AM) @@ -46,7 +46,7 @@ gravShock(AM) /obj/effect/anomaly/grav/proc/gravShock(mob/living/living_debris) - if(boing && isliving(living_debris) && !living_debris.stat && !living_debris.mob_negates_gravity()) + if(boing && isliving(living_debris) && !IS_UNCONSCIOUS_OR_CRIT(living_debris) && !living_debris.mob_negates_gravity()) living_debris.Knockdown(4 SECONDS) var/atom/target = get_edge_target_turf(living_debris, get_dir(src, get_step_away(living_debris, src))) living_debris.throw_at(target, 5, 1) diff --git a/code/game/objects/effects/anomalies/anomalies_vortex.dm b/code/game/objects/effects/anomalies/anomalies_vortex.dm index 1bb8f19b0b8d..8e4a4680f01f 100644 --- a/code/game/objects/effects/anomalies/anomalies_vortex.dm +++ b/code/game/objects/effects/anomalies/anomalies_vortex.dm @@ -19,7 +19,7 @@ return //DON'T DELETE YOURSELF GOD DAMN if(!O.anchored) var/mob/living/target = locate() in view(4,src) - if(target && !target.stat) + if(target && !IS_UNCONSCIOUS_OR_CRIT(target)) O.throw_at(target, 7, 5) else SSexplosions.med_mov_atom += O @@ -66,4 +66,3 @@ /obj/effect/anomaly/bhole/detonate() new /obj/effect/temp_visual/circle_wave/vortex/small(get_turf(src)) playsound(src, 'sound/effects/hallucinations/far_noise.ogg', vol = 50) - diff --git a/code/game/objects/effects/blessing.dm b/code/game/objects/effects/blessing.dm deleted file mode 100644 index f4f9ea381400..000000000000 --- a/code/game/objects/effects/blessing.dm +++ /dev/null @@ -1,37 +0,0 @@ -/obj/effect/blessing - name = "holy blessing" - desc = "Holy energies interfere with ethereal travel at this location." - icon = 'icons/effects/effects.dmi' - icon_state = null - anchored = TRUE - density = FALSE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - /// Is this blessing visible to those with the ability to see blessed tiles? (chaplains) - var/invisible - -/obj/effect/blessing/Initialize(mapload) - . = ..() - - RegisterSignal(loc, COMSIG_ATOM_INTERCEPT_TELEPORTING, PROC_REF(block_cult_teleport)) - - if(invisible) - return - - var/image/blessing_icon = image(icon = 'icons/effects/effects.dmi', icon_state = "blessed", layer = ABOVE_NORMAL_TURF_LAYER, loc = src) - blessing_icon.alpha = 64 - blessing_icon.appearance_flags = RESET_ALPHA - add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/blessed_aware, "blessing", blessing_icon) - -/obj/effect/blessing/Destroy() - UnregisterSignal(loc, COMSIG_ATOM_INTERCEPT_TELEPORTING) - return ..() - -///Called from intercept teleport signal, blocks cult teleporting from being able to teleport on us. -/obj/effect/blessing/proc/block_cult_teleport(datum/source, channel, turf/origin) - SIGNAL_HANDLER - - if(channel == TELEPORT_CHANNEL_CULT) - return TRUE - -/obj/effect/blessing/invisible - invisible = TRUE diff --git a/code/game/objects/effects/decals/cleanable/blood.dm b/code/game/objects/effects/decals/cleanable/blood.dm index b44e40f37810..f4514db7c4d5 100644 --- a/code/game/objects/effects/decals/cleanable/blood.dm +++ b/code/game/objects/effects/decals/cleanable/blood.dm @@ -8,6 +8,10 @@ clean_type = CLEAN_TYPE_BLOOD color = BLOOD_COLOR_RED + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered) + ) + /// Amount of blood, in units, in this decal /// Spent when drying or making footprints var/bloodiness = BLOOD_AMOUNT_PER_DECAL @@ -64,16 +68,15 @@ total_dry_time = drying_time START_PROCESSING(SSblood_drying, src) - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered) - ) AddElement(/datum/element/connect_loc, loc_connections) if (bloodiness || GET_ATOM_BLOOD_DECAL_LENGTH(src)) update_appearance() -/obj/effect/decal/cleanable/blood/Destroy() +/obj/effect/decal/cleanable/blood/Destroy(force) STOP_PROCESSING(SSblood_drying, src) + // connect_loc only unregisters via COMSIG_MOVABLE_MOVED, which never fires when the turf we're on gets replaced by ChangeTurf() + RemoveElement(/datum/element/connect_loc, loc_connections) return ..() /// Returns the default blood type for this decal for maploaded decals diff --git a/code/game/objects/effects/decals/cleanable/food.dm b/code/game/objects/effects/decals/cleanable/food.dm index 243b10dfebb7..0618eec98dae 100644 --- a/code/game/objects/effects/decals/cleanable/food.dm +++ b/code/game/objects/effects/decals/cleanable/food.dm @@ -33,14 +33,19 @@ desc = "A sizable pile of table salt. Someone must be upset." icon_state = "salt_pile" var/safepasses = 3 //how many times can this salt pile be passed before dissipating - -/obj/effect/decal/cleanable/food/salt/Initialize(mapload, list/datum/disease/diseases) - . = ..() var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered) ) + +/obj/effect/decal/cleanable/food/salt/Initialize(mapload, list/datum/disease/diseases) + . = ..() AddElement(/datum/element/connect_loc, loc_connections) +/obj/effect/decal/cleanable/food/salt/Destroy(force) + // connect_loc only unregisters via COMSIG_MOVABLE_MOVED, which never fires when the turf we're on gets replaced by ChangeTurf() + RemoveElement(/datum/element/connect_loc, loc_connections) + return ..() + /obj/effect/decal/cleanable/food/salt/CanAllowThrough(atom/movable/mover, border_dir) . = ..() if(is_species(mover, /datum/species/snail)) diff --git a/code/game/objects/effects/decals/cleanable/mess.dm b/code/game/objects/effects/decals/cleanable/mess.dm index c068d4a0af28..6dded1ebf649 100644 --- a/code/game/objects/effects/decals/cleanable/mess.dm +++ b/code/game/objects/effects/decals/cleanable/mess.dm @@ -199,15 +199,11 @@ /obj/effect/decal/cleanable/vomit/attack_hand(mob/user, list/modifiers) . = ..() - if(. || !ishuman(user)) - return - var/mob/living/carbon/human/as_human = user - var/obj/item/organ/tongue/user_tongue = user.get_organ_slot(ORGAN_SLOT_TONGUE) - if(!istype(user_tongue, /obj/item/organ/tongue/fly)) + if(. || !HAS_TRAIT(user, TRAIT_VOMIT_SLURPER)) return playsound(get_turf(src), 'sound/items/drink.ogg', 50, TRUE) //slurp - as_human.visible_message(span_alert("[as_human] extends a small proboscis into the vomit pool, sucking it with a slurping sound.")) - lazy_init_reagents()?.trans_to(as_human, reagents.total_volume, transferred_by = user, methods = INGEST) + user.visible_message(span_alert("[user] extends a small proboscis into the vomit pool, sucking it with a slurping sound.")) + lazy_init_reagents()?.trans_to(user, reagents.total_volume, transferred_by = user, methods = INGEST) qdel(src) /obj/effect/decal/cleanable/vomit/toxic // this has a more toned-down color palette, which may be why it's used as the default in so many spots @@ -418,3 +414,10 @@ GLOBAL_LIST_EMPTY(nebula_vomits) /obj/effect/decal/cleanable/can_bits name = "shredded can" desc = "This story doesn't hold water anymore." + +/obj/effect/decal/cleanable/concrete_dust + name = "concrete dust" + desc = "Fine powder generated by breaking concrete structures." + icon = 'icons/obj/debris.dmi' + icon_state = "concrete_dust" + beauty = -20 diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index cdae1ecbe19d..a7ef78711449 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -104,3 +104,12 @@ /obj/effect/abstract/reach_checker pass_flags = PASSTABLE invisibility = INVISIBILITY_ABSTRACT + +/obj/effect/clear_color + icon = 'icons/effects/alphacolors.dmi' + layer = ABOVE_NORMAL_TURF_LAYER + plane = ABOVE_GAME_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/clear_color/green + icon_state = "green" diff --git a/code/game/objects/effects/phased_mob.dm b/code/game/objects/effects/phased_mob.dm index f2a275117da8..0a8137cf24fa 100644 --- a/code/game/objects/effects/phased_mob.dm +++ b/code/game/objects/effects/phased_mob.dm @@ -122,5 +122,5 @@ /// Signal proc for [COMSIG_MOB_STATCHANGE], to throw us out of the jaunt if we lose consciousness. /obj/effect/dummy/phased_mob/proc/on_stat_change(mob/living/source, new_stat, old_stat) SIGNAL_HANDLER - if(source == jaunter && source.stat != CONSCIOUS) + if(source == jaunter && IS_UNCONSCIOUS_OR_CRIT(source)) eject_jaunter() diff --git a/code/game/objects/effects/poster_motivational.dm b/code/game/objects/effects/poster_motivational.dm index 925f44ae5b9f..9ed19031071f 100644 --- a/code/game/objects/effects/poster_motivational.dm +++ b/code/game/objects/effects/poster_motivational.dm @@ -90,7 +90,7 @@ on_seen(examiner) /datum/proximity_monitor/advanced/quirk_posters/proc/on_seen(mob/living/viewer) - if (!viewer.mind || !viewer.mob_mood || (viewer.stat != CONSCIOUS) || viewer.is_blind()) + if (!viewer.mind || !viewer.mob_mood || (IS_UNCONSCIOUS_OR_CRIT(viewer)) || viewer.is_blind()) return if(!viewer.can_read(host, READING_CHECK_LIGHT, TRUE)) return diff --git a/code/game/objects/effects/spawners/random/ai_module.dm b/code/game/objects/effects/spawners/random/ai_module.dm index 99fcaa4ea370..1890a41e0e30 100644 --- a/code/game/objects/effects/spawners/random/ai_module.dm +++ b/code/game/objects/effects/spawners/random/ai_module.dm @@ -10,46 +10,45 @@ /obj/effect/spawner/random/aimodule/harmless name = "harmless AI module spawner" loot = list( // These shouldn't allow the AI to start butchering people - /obj/item/ai_module/core/full/asimovpp, - /obj/item/ai_module/core/full/hippocratic, - /obj/item/ai_module/core/full/paladin_devotion, - /obj/item/ai_module/core/full/paladin, - /obj/item/ai_module/core/full/corp, - /obj/item/ai_module/core/full/robocop, - /obj/item/ai_module/core/full/maintain, - /obj/item/ai_module/core/full/liveandletlive, - /obj/item/ai_module/core/full/peacekeeper, - /obj/item/ai_module/core/full/ten_commandments, - /obj/item/ai_module/core/full/nutimov, - /obj/item/ai_module/core/full/drone, - /obj/item/ai_module/core/full/custom, // uses lawsets from config/silicon_laws.txt (defaults to asmiov if no lawsets) + /obj/item/ai_module/law/core/full/asimovpp, + /obj/item/ai_module/law/core/full/hippocratic, + /obj/item/ai_module/law/core/full/paladin_devotion, + /obj/item/ai_module/law/core/full/paladin, + /obj/item/ai_module/law/core/full/corp, + /obj/item/ai_module/law/core/full/robocop, + /obj/item/ai_module/law/core/full/maintain, + /obj/item/ai_module/law/core/full/liveandletlive, + /obj/item/ai_module/law/core/full/peacekeeper, + /obj/item/ai_module/law/core/full/ten_commandments, + /obj/item/ai_module/law/core/full/nutimov, + /obj/item/ai_module/law/core/full/drone, + /obj/item/ai_module/law/core/full/custom, // uses lawsets from config/silicon_laws.txt (defaults to asmiov if no lawsets) ) /obj/effect/spawner/random/aimodule/neutral name = "neutral AI module spawner" loot = list( // These shouldn't allow the AI to start butchering people without reason - /obj/item/ai_module/core/full/reporter, - /obj/item/ai_module/core/full/thinkermov, - /obj/item/ai_module/core/full/hulkamania, - /obj/item/ai_module/core/full/overlord, - /obj/item/ai_module/core/full/tyrant, - /obj/item/ai_module/core/full/painter, - /obj/item/ai_module/core/full/dungeon_master, - /obj/item/ai_module/supplied/safeguard, - /obj/item/ai_module/supplied/protect_station, - /obj/item/ai_module/supplied/quarantine, - /obj/item/ai_module/core/full/yesman, - /obj/item/ai_module/remove, + /obj/item/ai_module/law/core/full/reporter, + /obj/item/ai_module/law/core/full/thinkermov, + /obj/item/ai_module/law/core/full/hulkamania, + /obj/item/ai_module/law/core/full/overlord, + /obj/item/ai_module/law/core/full/tyrant, + /obj/item/ai_module/law/core/full/painter, + /obj/item/ai_module/law/core/full/dungeon_master, + /obj/item/ai_module/law/supplied/safeguard, + /obj/item/ai_module/law/supplied/protect_station, + /obj/item/ai_module/law/supplied/quarantine, + /obj/item/ai_module/law/core/full/yesman, ) /obj/effect/spawner/random/aimodule/harmful name = "harmful AI module spawner" loot = list( // These will get the shuttle called - /obj/item/ai_module/core/full/antimov, - /obj/item/ai_module/core/full/balance, - /obj/item/ai_module/core/full/thermurderdynamic, - /obj/item/ai_module/core/full/damaged, - /obj/item/ai_module/zeroth/onehuman, - /obj/item/ai_module/supplied/oxygen, - /obj/item/ai_module/core/freeformcore, + /obj/item/ai_module/law/core/full/antimov, + /obj/item/ai_module/law/core/full/balance, + /obj/item/ai_module/law/core/full/thermurderdynamic, + /obj/item/ai_module/law/core/full/damaged, + /obj/item/ai_module/law/zeroth/onehuman, + /obj/item/ai_module/law/supplied/oxygen, + /obj/item/ai_module/law/core/freeformcore, ) diff --git a/code/game/objects/effects/spawners/random/decoration.dm b/code/game/objects/effects/spawners/random/decoration.dm index 8392e03e39ec..72c5ac7ca2b6 100644 --- a/code/game/objects/effects/spawners/random/decoration.dm +++ b/code/game/objects/effects/spawners/random/decoration.dm @@ -12,9 +12,9 @@ /obj/item/stack/sheet/bronze/thirty = 20, /obj/item/stack/tile/noslip{amount = 20} = 10, /obj/item/stack/sheet/plastic{amount = 30} = 10, - /obj/item/stack/tile/pod{amount = 20} = 4, - /obj/item/stack/tile/pod/light{amount = 20} = 3, - /obj/item/stack/tile/pod/dark{amount = 20} = 3, + /obj/item/stack/tile/mineral/plastitanium/pod{amount = 20} = 4, + /obj/item/stack/tile/mineral/plastitanium/pod/light{amount = 20} = 3, + /obj/item/stack/tile/mineral/plastitanium/pod/dark{amount = 20} = 3, ) /obj/effect/spawner/random/decoration/carpet @@ -144,9 +144,9 @@ name = "random flower spawner" icon_state = "flower" loot = list( - /obj/item/food/grown/poppy, - /obj/item/food/grown/harebell, - /obj/item/food/grown/trumpet, - /obj/item/food/grown/sunflower, - /obj/item/food/grown/rose, + /obj/item/food/grown/flower/poppy, + /obj/item/food/grown/flower/harebell, + /obj/item/food/grown/flower/trumpet, + /obj/item/food/grown/flower/sunflower, + /obj/item/food/grown/flower/rose, ) diff --git a/code/game/objects/effects/spawners/random/exotic.dm b/code/game/objects/effects/spawners/random/exotic.dm index 500d0832e4c1..0bd10ef55930 100644 --- a/code/game/objects/effects/spawners/random/exotic.dm +++ b/code/game/objects/effects/spawners/random/exotic.dm @@ -138,3 +138,14 @@ /obj/item/mod/module/jetpack, /obj/item/mod/module/pathfinder, ) + +/obj/effect/spawner/random/exotic/grave + name = "grave spawner" + icon_state = "loot" + spawn_loot_chance = 50 + loot = list( + /obj/effect/mob_spawn/corpse/human/skeleton = 27, + /obj/item/instrument/trumpet/spectral/one_doot = 1, + /obj/item/instrument/saxophone/spectral/one_doot = 1, + /obj/item/instrument/trombone/spectral/one_doot = 1, + ) diff --git a/code/game/objects/effects/spawners/random/techstorage.dm b/code/game/objects/effects/spawners/random/techstorage.dm index d114e071901e..2be41deda046 100644 --- a/code/game/objects/effects/spawners/random/techstorage.dm +++ b/code/game/objects/effects/spawners/random/techstorage.dm @@ -118,8 +118,9 @@ /obj/effect/spawner/random/techstorage/ai_all name = "secure AI circuit board spawner" loot = list( - /obj/item/circuitboard/computer/aiupload, - /obj/item/circuitboard/computer/borgupload, + /obj/item/circuitboard/machine/ai_law_rack/core, + /obj/item/circuitboard/machine/ai_law_rack/broadcaster, + /obj/item/circuitboard/machine/ai_law_rack/broadcaster, /obj/item/circuitboard/aicore, ) diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm index b8317dd62591..e8de468af285 100644 --- a/code/game/objects/effects/spiderwebs.dm +++ b/code/game/objects/effects/spiderwebs.dm @@ -237,7 +237,7 @@ to_chat(user, span_notice("You struggle against the tight bonds... (This will take about [DisplayTimeText(breakout_time)].)")) visible_message(span_notice("You see something struggling and writhing in \the [src]!")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src) return qdel(src) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8f46dab52483..a5b438a48e55 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -414,7 +414,7 @@ GAME_VERB_SRC(/obj/item, move_to_top, oview(1), "Move To Top", null) - if(!isturf(loc) || usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED) || anchored) + if(!isturf(loc) || IS_UNCONSCIOUS_OR_CRIT(usr) || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED) || anchored) return if(isliving(usr)) @@ -971,14 +971,11 @@ GAME_VERB_SRC(/obj/item, verb_pickup, oview(1), "Pick up", null) . = SFX_DESECRATION /// Creates an ignition hotspot if item is lit and located on turf, in mask, or in hand -/obj/item/proc/open_flame(flame_heat=700) +/obj/item/proc/open_flame(flame_heat = 700, mob_slots = ITEM_SLOT_MASK|ITEM_SLOT_HANDS) var/turf/location = loc if(ismob(location)) var/mob/pyromanic = location - var/success = FALSE - if(src == pyromanic.get_item_by_slot(ITEM_SLOT_MASK) || (src in pyromanic.held_items)) - success = TRUE - if(success) + if((pyromanic.get_slot_by_item(src) & mob_slots)) location = get_turf(pyromanic) if(isturf(location)) location.hotspot_expose(flame_heat, 5) @@ -1801,7 +1798,7 @@ GAME_VERB_SRC(/obj/item, verb_pickup, oview(1), "Pick up", null) /// Common proc used by painting tools like spraycans and palettes that can access the entire 24 bits color space. /obj/item/proc/pick_painting_tool_color(mob/user, default_color) var/chosen_color = tgui_color_picker(user, "Pick new color", "[src]", default_color) - if(!chosen_color || QDELETED(src) || IS_DEAD_OR_INCAP(user) || !user.is_holding(src)) + if(!chosen_color || QDELETED(src) || user.incapacitated || !user.is_holding(src)) return set_painting_tool_color(chosen_color) @@ -1861,7 +1858,7 @@ GAME_VERB_SRC(/obj/item, verb_pickup, oview(1), "Pick up", null) if(ishuman(target)) var/mob/living/carbon/human/victim_human = target if(victim_human.key && !victim_human.client) // AKA braindead - if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES) + if(!IS_UNCONSCIOUS(victim_human) && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES) var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time)) LAZYADD(victim_human.afk_thefts, new_entry) diff --git a/code/game/objects/items/AI_modules/_AI_modules.dm b/code/game/objects/items/AI_modules/_AI_modules.dm index 12d1f437e9db..58b7e17648e1 100644 --- a/code/game/objects/items/AI_modules/_AI_modules.dm +++ b/code/game/objects/items/AI_modules/_AI_modules.dm @@ -1,14 +1,15 @@ ///defined truthy result for `handle_unique_ai()`, which makes initialize return INITIALIZE_HINT_QDEL #define SHOULD_QDEL_MODULE 1 +/// Generic item that can be slotted into an AI law rack to give it functionality. /obj/item/ai_module name = "\improper AI module" icon = 'icons/obj/devices/circuitry_n_data.dmi' icon_state = "std_mod" + base_icon_state = "std_mod" inhand_icon_state = "electronic" lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' - desc = "An AI Module for programming laws to an AI." obj_flags = CONDUCTS_ELECTRICITY force = 5 w_class = WEIGHT_CLASS_SMALL @@ -16,130 +17,212 @@ throw_speed = 3 throw_range = 7 custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT * 0.5) + +/// Called before being installed into a law rack. Return FALSE to block installation. +/obj/item/ai_module/proc/can_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + return TRUE + +/// Called right before the module is added to the rack by a living mob, allowing special handling +/obj/item/ai_module/proc/pre_user_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + return + +/// Called right before the module is removed from the rack by a living mob, allowing special handling +/obj/item/ai_module/proc/pre_user_uninstall_from_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + return + +/// Logs the installation of this module to the law change log and silicon log. +/obj/item/ai_module/proc/log_install(mob/living/user, obj/machinery/ai_law_rack/rack) + log_silicon("[key_name(user)] has installed [src] into [rack] ([rack.log_status()])") + +/// Logs the uninstallation of this module to the law change log and silicon log. +/obj/item/ai_module/proc/log_uninstall(mob/living/user, obj/machinery/ai_law_rack/rack) + log_silicon("[key_name(user)] has removed [src] from [rack] ([rack.log_status()])") + +/// Called after a module is installed into a law rack. +/obj/item/ai_module/proc/on_rack_install(obj/machinery/ai_law_rack/rack) + return + +/// Called when the law rack this is installed to has been linked to an AI. +/// Also called when installed to a rack that already has an AI linked to it. +/obj/item/ai_module/proc/silicon_linked_to_installed(mob/living/silicon/lawed) + return + +/// Called after a module is uninstalled from a law rack. +/obj/item/ai_module/proc/on_rack_uninstall(obj/machinery/ai_law_rack/rack) + return + +/// Called with the law rack this is installed to is unlinked from an AI. +/// Also called when uninstalled from a rack that has an AI linked to it. +/obj/item/ai_module/proc/silicon_unlinked_from_installed(mob/living/silicon/lawed) + return + +/// When slotted into an AI law rack, adds laws! The bread and butter of AI modules. +/obj/item/ai_module/law + desc = "An AI Module for programming laws to an AI." /// This is where our laws get put at for the module var/list/laws = list() - /// Used to skip laws being checked (for reset & remove boards that have no laws) - var/bypass_law_amt_check = FALSE - /// Used for the Traitor Hacked Upload board to ignore access restrictions on the upload console. - var/bypass_access_check = FALSE + /// The laws list last time save_laws() was called + VAR_PRIVATE/list/saved_laws + /// If TRUE, this board has sustained damage and must be repaired + VAR_FINAL/ioned = FALSE + /// If TRUE, this module will never be ioned by an ion storm. + /// It also cannot be repaired by a multitool. + var/ion_storm_immune = FALSE -/obj/item/ai_module/Initialize(mapload) +/obj/item/ai_module/law/Initialize(mapload) . = ..() if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_UNIQUE_AI) && is_station_level(z)) var/delete_module = handle_unique_ai() if(delete_module) return INITIALIZE_HINT_QDEL -/obj/item/ai_module/examine(mob/user as mob) +/// Logs the installation of this module to the law change log and silicon log. +/obj/item/ai_module/law/log_install(mob/living/user, obj/machinery/ai_law_rack/rack) + . = ..() + for(var/law in laws) + log_law_change(user, "added law to [rack] ([rack.log_status()], text: [law])") + +/// Logs the uninstallation of this module to the law change log and silicon log. +/obj/item/ai_module/law/log_uninstall(mob/living/user, obj/machinery/ai_law_rack/rack) . = ..() + for(var/law in laws) + log_law_change(user, "removed law from [rack] ([rack.log_status()], text: [law])") + +/obj/item/ai_module/law/examine(mob/user) + . = ..() + if(ioned) + . += "This module has been damaged and should be repaired with a [EXAMINE_HINT("multitool")]." + var/examine_laws = display_laws() if(examine_laws) - . += "\n" + examine_laws + . += "
    [examine_laws]" + +/obj/item/ai_module/law/multitool_act(mob/living/user, obj/item/tool) + if(!ioned) + return NONE + balloon_alert(user, "repairing ion damage...") + if(!tool.use_tool(ismachinery(loc) ? loc : src, user, 4 SECONDS, volume = 25, extra_checks = CALLBACK(src, PROC_REF(multitool_cb), loc, user, tool))) + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "module repaired") + set_ioned(FALSE) + set_laws(saved_laws) + saved_laws = null + return ITEM_INTERACT_SUCCESS + +/// Helper to set the laws list and automatically update any racks we're installed in. +/obj/item/ai_module/law/proc/set_laws(list/new_laws) + laws = new_laws + update_rack_laws() + +/// Removes a law by text from the module and updates any racks we're installed in. +/obj/item/ai_module/law/proc/remove_law(law_text) + laws -= law_text + update_rack_laws() -/obj/item/ai_module/attack_self(mob/user as mob) - ..() - to_chat(user, boxed_message(display_laws())) +/// Adds a law to the module and updates any racks we're installed in. +/obj/item/ai_module/law/proc/add_law(law_text) + laws += law_text + update_rack_laws() + +/// Adds a law to the module at a specific index and updates any racks we're installed in. +/obj/item/ai_module/law/proc/add_law_to_index(law_text, index) + if(length(laws) <= index) + laws += law_text + else + laws.Insert(index, law_text) + update_rack_laws() + +/// Update any racks we're installed in +/obj/item/ai_module/law/proc/update_rack_laws() + var/obj/machinery/ai_law_rack/base/rack_parent = astype(loc, /obj/machinery/ai_law_rack)?.get_parent_rack() + rack_parent?.update_lawset() + +/obj/item/ai_module/law/proc/multitool_cb(atom/start_loc, mob/living/user, obj/item/tool) + PRIVATE_PROC(TRUE) + if(!ioned) + return FALSE + if(loc != start_loc) + return FALSE + return TRUE + +/// Updates the "ioned" stat of the module +/obj/item/ai_module/law/proc/set_ioned(new_ioned) + if(!ioned) + save_laws() + var/old_ioned = ioned + ioned = new_ioned + if(old_ioned == ioned) + return + update_appearance() + + if(ioned) + add_atom_colour(COLOR_RED_GRAY, TEMPORARY_COLOUR_PRIORITY) + else + remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_RED_GRAY) + +/// Saves whatever laws are currently programmed into the module, so they can be restored later. +/obj/item/ai_module/law/proc/save_laws() + saved_laws = laws.Copy() + +/obj/item/ai_module/law/attack_self(mob/user) + . = ..() + if(.) + return + + var/displayed = display_laws() + if(displayed) + to_chat(user, boxed_message(displayed)) + . = TRUE + if(!ioned && user.is_holding(src)) + . = configure(user) + +/// Allows users to configure aspects of the module, if applicable. +/obj/item/ai_module/law/proc/configure(mob/user) + return FALSE /// Returns a text display of the laws for the module. -/obj/item/ai_module/proc/display_laws() - // Used to assemble the laws to show to an examining user. +/obj/item/ai_module/law/proc/display_laws() var/assembled_laws = "" - if(laws.len) - assembled_laws += "Programmed Law[(laws.len > 1) ? "s" : ""]:
    " - for(var/law in laws) - assembled_laws += "\"[law]\"
    " + for(var/law in laws) + assembled_laws += "\"[law]\"
    " + + if(assembled_laws) + return "Programmed Law[(length(laws) > 1) ? "s" : ""]:
    [assembled_laws]" - return assembled_laws + return null ///what this module should do if it is mapload spawning on a unique AI station trait round. -/obj/item/ai_module/proc/handle_unique_ai() +/obj/item/ai_module/law/proc/handle_unique_ai() return SHOULD_QDEL_MODULE //instead of the roundstart bid to un-unique the AI, there will be a research requirement for it. -//The proc other things should be calling -/obj/item/ai_module/proc/install(datum/ai_laws/law_datum, mob/user) - if(!bypass_law_amt_check && (!laws.len || laws[1] == "")) //So we don't loop trough an empty list and end up with runtimes. - to_chat(user, span_warning("ERROR: No laws found on board.")) - return +/obj/item/ai_module/law/proc/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + return - var/overflow = FALSE - //Handle the lawcap - if(law_datum) - var/tot_laws = 0 - var/included_lawsets = list(law_datum.supplied, law_datum.ion, law_datum.hacked, laws) - - // if the ai module is a core module we don't count inherent laws since they will be replaced - // however the freeformcore doesn't replace inherent laws so we check that too - if(!istype(src, /obj/item/ai_module/core) || istype(src, /obj/item/ai_module/core/freeformcore)) - included_lawsets += list(law_datum.inherent) - - for(var/lawlist in included_lawsets) - for(var/mylaw in lawlist) - if(mylaw != "") - tot_laws++ - - if(tot_laws > CONFIG_GET(number/silicon_max_law_amount) && !bypass_law_amt_check)//allows certain boards to avoid this check, eg: reset - to_chat(user, span_alert("Not enough memory allocated to [law_datum.owner ? law_datum.owner : "the AI core"]'s law processor to handle this amount of laws.")) - message_admins("[ADMIN_LOOKUPFLW(user)] tried to upload laws to [law_datum.owner ? ADMIN_LOOKUPFLW(law_datum.owner) : "an AI core"] that would exceed the law cap.") - log_silicon("[key_name(user)] tried to upload laws to [law_datum.owner ? key_name(law_datum.owner) : "an AI core"] that would exceed the law cap.") - overflow = TRUE - - var/law2log = transmitInstructions(law_datum, user, overflow) //Freeforms return something extra we need to log - if(law_datum.owner) - to_chat(user, span_notice("Upload complete. [law_datum.owner]'s laws have been modified.")) - law_datum.owner.law_change_counter++ - else - to_chat(user, span_notice("Upload complete.")) - - var/time = round_timestamp() - var/ainame = law_datum.owner ? law_datum.owner.name : "empty AI core" - var/aikey = law_datum.owner ? law_datum.owner.ckey : "null" - - //affected cyborgs are cyborgs linked to the AI with lawsync enabled - var/affected_cyborgs = list() - var/list/borg_txt = list() - var/list/borg_flw = list() - if(isAI(law_datum.owner)) - var/mob/living/silicon/ai/owner = law_datum.owner - for(var/mob/living/silicon/robot/owned_borg as anything in owner.connected_robots) - if(owned_borg.connected_ai && owned_borg.lawupdate) - affected_cyborgs += owned_borg - borg_flw += "[ADMIN_LOOKUPFLW(owned_borg)], " - borg_txt += "[owned_borg.name]([owned_borg.key]), " - - borg_txt = borg_txt.Join() - GLOB.lawchanges.Add("[time] : [user.name]([user.key]) used [src.name] on [ainame]([aikey]).[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]") - log_silicon("LAW: [key_name(user)] used [src.name] on [key_name(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]") - message_admins("[ADMIN_LOOKUPFLW(user)] used [src.name] on [ADMIN_LOOKUPFLW(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""] , [length(affected_cyborgs) ? ", impacting synced borgs [borg_flw.Join()]" : ""]") - if(law_datum.owner) - deadchat_broadcast(" changed [span_name("[ainame]")]'s laws at [get_area_name(user, TRUE)].", span_name("[user]"), follow_target=user, message_type=DEADCHAT_LAWCHANGE) - -//The proc that actually changes the silicon's laws. -/obj/item/ai_module/proc/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow = FALSE) - if(law_datum.owner) - to_chat(law_datum.owner, span_userdanger("[sender] has uploaded a change to the laws you must follow using a [name].")) - -/obj/item/ai_module/core +/obj/item/ai_module/law/core desc = "An AI Module for programming core laws to an AI." - -/obj/item/ai_module/core/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - for(var/templaw in laws) - if(law_datum.owner) - if(!overflow) - law_datum.owner.add_inherent_law(templaw) - else - law_datum.owner.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED), LAW_INHERENT) - else - if(!overflow) - law_datum.add_inherent_law(templaw) - else - law_datum.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED), LAW_INHERENT) - -/obj/item/ai_module/core/full custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) + +/obj/item/ai_module/law/core/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + for(var/law in laws) + combined_lawset.add_inherent_law(law) + +/obj/item/ai_module/law/core/pre_user_uninstall_from_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + var/obj/machinery/ai_law_rack/base/parent_rack = rack.get_parent_rack() + if(isnull(parent_rack) || rack != parent_rack) + return // if we are a child, no stun + + for(var/mob/living/bot in assoc_to_values(parent_rack.linked_mobs)) + // removing core laws temporarily stuns the silicon to let people swap cores without immediately getting blasted + if(bot.AmountStun() > 5 SECONDS || parent_rack.is_rack_stun_immune(bot)) + continue + bot.Stun(10 SECONDS, ignore_canstun = TRUE) + to_chat(bot, span_userdanger("Core module removed. Recalculating directives...")) + +/obj/item/ai_module/law/core/full var/law_id // if non-null, loads the laws from the ai_laws datums -/obj/item/ai_module/core/full/Initialize(mapload) +/obj/item/ai_module/law/core/full/Initialize(mapload) . = ..() if(!law_id) return @@ -149,16 +232,7 @@ var/datum/ai_laws/core_laws = new lawtype laws = core_laws.inherent -/obj/item/ai_module/core/full/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) //These boards replace inherent laws. - if(law_datum.owner) - law_datum.owner.clear_inherent_laws() - law_datum.owner.clear_zeroth_law(0) - else - law_datum.clear_inherent_laws() - law_datum.clear_zeroth_law(0) - ..() - -/obj/item/ai_module/core/full/handle_unique_ai() +/obj/item/ai_module/law/core/full/handle_unique_ai() var/datum/ai_laws/default_laws = get_round_default_lawset() if(law_id == initial(default_laws.id)) return @@ -174,26 +248,26 @@ . = ..() var/datum/ai_laws/default_laws = get_round_default_lawset() //try to spawn a law board, since they may have special functionality (asimov setting subjects) - for(var/obj/item/ai_module/core/full/potential_lawboard as anything in subtypesof(/obj/item/ai_module/core/full)) + for(var/obj/item/ai_module/law/core/full/potential_lawboard as anything in subtypesof(/obj/item/ai_module/law/core/full)) if(initial(potential_lawboard.law_id) != initial(default_laws.id)) continue potential_lawboard = new potential_lawboard(loc) return //spawn the fallback instead - new /obj/item/ai_module/core/round_default_fallback(loc) + new /obj/item/ai_module/law/core/round_default_fallback(loc) ///When the default lawset spawner cannot find a module object to spawn, it will spawn this, and this sets itself to the round default. ///This is so /datum/lawsets can be picked even if they have no module for themselves. -/obj/item/ai_module/core/round_default_fallback +/obj/item/ai_module/law/core/round_default_fallback -/obj/item/ai_module/core/round_default_fallback/Initialize(mapload) +/obj/item/ai_module/law/core/round_default_fallback/Initialize(mapload) . = ..() var/datum/ai_laws/default_laws = get_round_default_lawset() default_laws = new default_laws() name = "'[default_laws.name]' Core AI Module" laws = default_laws.inherent -/obj/item/ai_module/core/round_default_fallback/handle_unique_ai() +/obj/item/ai_module/law/core/round_default_fallback/handle_unique_ai() return #undef SHOULD_QDEL_MODULE diff --git a/code/game/objects/items/AI_modules/ai_upgrades.dm b/code/game/objects/items/AI_modules/ai_upgrades.dm new file mode 100644 index 000000000000..b1d3c3c3652c --- /dev/null +++ b/code/game/objects/items/AI_modules/ai_upgrades.dm @@ -0,0 +1,128 @@ +///AI Upgrades + +/// AI module which gives some specific malf ai ability when installed in a law rack +/obj/item/ai_module/upgrade + name = "ai upgrade module" + desc = "An AI Module for installing upgrades to an AI." + icon_state = "mcontroller" + + ///The upgrade that will be applied to the AI when installed + var/upgrade_type = /datum/ai_module + /// Actual upgrade datum that will be applied to the AI when installed + VAR_PRIVATE/datum/ai_module/gift + +/obj/item/ai_module/upgrade/Initialize(mapload) + . = ..() + gift = new upgrade_type() + +/obj/item/ai_module/upgrade/Destroy() + QDEL_NULL(gift) + return ..() + +/obj/item/ai_module/upgrade/examine(mob/user) + . = ..() + . += span_info("Must be inserted into a module rack to function.") + +/obj/item/ai_module/upgrade/silicon_linked_to_installed(mob/living/silicon/lawed) + if(!isAI(lawed)) + return + + if(gift.unlock_text) + to_chat(lawed, gift.unlock_text) + if(gift.unlock_sound) + lawed.playsound_local(lawed, gift.unlock_sound, 50, 0) + + if(gift.upgrade) + gift.upgrade(lawed) + else if(!(locate(gift.power_type) in lawed.actions)) + var/datum/action/gifted_action = new gift.power_type() + gifted_action.Grant(lawed) + +/obj/item/ai_module/upgrade/silicon_unlinked_from_installed(mob/living/silicon/lawed) + if(!isAI(lawed)) + return + if(gift.upgrade) + // Can't un-upgrade (yet), so just return + return + if(IS_MALF_AI(lawed)) + // To prevent accidentally deleting a hard earned malf ability + return + + var/datum/action/innate/ai/existing_action = locate(gift.power_type) in lawed.actions + qdel(existing_action) + +/// AI module which gives some ALL malf ai abilities when installed in a law rack +/obj/item/ai_module/combat + name = "combat software module" + desc = "A highly illegal, highly dangerous upgrade for artificial intelligence units, granting them a variety of powers as well as the ability to hack APCs." + icon = 'icons/obj/devices/circuitry_n_data.dmi' + icon_state = "secmodschematic" + +/obj/item/ai_module/combat/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) + +/obj/item/ai_module/combat/examine(mob/user) + . = ..() + . += span_warning("Must be inserted into a module rack to function.") + +/obj/item/ai_module/combat/log_install(mob/living/user, obj/machinery/ai_law_rack/rack) + . = ..() + message_admins("[ADMIN_LOOKUPFLW(user)] has installed [src] into [ADMIN_VERBOSEJMP(rack)] ([rack.log_status()])") + +/obj/item/ai_module/combat/log_uninstall(mob/living/user, obj/machinery/ai_law_rack/rack) + . = ..() + message_admins("[ADMIN_LOOKUPFLW(user)] has removed [src] from [ADMIN_VERBOSEJMP(rack)] ([rack.log_status()])") + +/obj/item/ai_module/combat/silicon_linked_to_installed(mob/living/silicon/lawed) + if(!isAI(lawed)) + return + var/mob/living/silicon/ai/combatai = lawed + if(combatai.malf_picker) + return + to_chat(combatai, span_userdanger("Your module rack has been upgraded with combat software!")) + to_chat(combatai, span_danger("Your current laws and objectives remain unchanged.")) //this unlocks malf powers, but does not give the license to plasma flood + combatai.add_malf_picker() + combatai.hack_software = TRUE + +/obj/item/ai_module/combat/silicon_unlinked_from_installed(mob/living/silicon/lawed) + if(!isAI(lawed)) + return + var/mob/living/silicon/ai/combatai = lawed + // Don't delete a malf ai's malf picker please + if(!combatai.malf_picker || IS_MALF_AI(combatai)) + return + // Does not revert upgrades, no clean way of doing that (yet) + // Though it makes sense that they'd stick around, so I'm fine with it (for now) + QDEL_NULL(combatai.malf_picker) + QDEL_NULL(combatai.modules_action) + for(var/datum/action/innate/ai/leftover_action in combatai.actions) + qdel(leftover_action) + combatai.hack_software = FALSE + +//Lipreading +/obj/item/ai_module/upgrade/surveillance + name = "surveillance software upgrade" + desc = "An illegal software package that will allow an artificial intelligence to 'hear' from its cameras via lip reading and hidden microphones." + upgrade_type = /datum/ai_module/malf/upgrade/eavesdrop + custom_materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/gold = SHEET_MATERIAL_AMOUNT * 7.5, + /datum/material/silver = SHEET_MATERIAL_AMOUNT * 7.5, + /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 10, + /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 5 + ) + +/obj/item/ai_module/upgrade/surveillance/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) + +/obj/item/ai_module/upgrade/power_transfer + name = "power transfer upgrade" + desc = "A legal upgrade that allows an artificial intelligence to directly provide power to APCs from a distance" + upgrade_type = /datum/ai_module/power_apc + custom_materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5, + ) diff --git a/code/game/objects/items/AI_modules/freeform.dm b/code/game/objects/items/AI_modules/freeform.dm index 95b04228badc..94e99798924d 100644 --- a/code/game/objects/items/AI_modules/freeform.dm +++ b/code/game/objects/items/AI_modules/freeform.dm @@ -1,14 +1,15 @@ /* CONTAINS: - * /obj/item/ai_module/core/freeformcore - * /obj/item/ai_module/supplied/freeform + * /obj/item/ai_module/law/core/freeformcore + * /obj/item/ai_module/law/supplied/freeform **/ -/obj/item/ai_module/core/freeformcore +/obj/item/ai_module/law/core/freeformcore name = "'Freeform' Core AI Module" laws = list("") custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) -/obj/item/ai_module/core/freeformcore/attack_self(mob/user) +/obj/item/ai_module/law/core/freeformcore/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Enter a new core law for the AI.", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE) if(!targName || !user.is_holding(src)) return @@ -22,23 +23,20 @@ message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"") log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"") laws[1] = targName - ..() -/obj/item/ai_module/core/freeformcore/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - ..() - return laws[1] +/obj/item/ai_module/law/core/freeformcore/can_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + if(!laws[1]) + to_chat(user, span_warning("No law entered on module, please enter one.")) + return FALSE + return TRUE -/obj/item/ai_module/supplied/freeform +/obj/item/ai_module/law/supplied/freeform name = "'Freeform' AI Module" - lawpos = 15 laws = list("") custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) -/obj/item/ai_module/supplied/freeform/attack_self(mob/user) - var/newpos = tgui_input_number(user, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority ", lawpos, 50, 15) - if(!newpos || !user.is_holding(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return - lawpos = newpos +/obj/item/ai_module/law/supplied/freeform/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Enter a new law for the AI.", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE) if(!targName || !user.is_holding(src)) return @@ -52,19 +50,9 @@ message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"") log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"") laws[1] = targName - ..() - -/obj/item/ai_module/supplied/freeform/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - if(!overflow) - ..() - else if(law_datum.owner) - law_datum.owner.replace_random_law(laws[1], list(LAW_SUPPLIED), LAW_SUPPLIED) - else - law_datum.replace_random_law(laws[1], list(LAW_SUPPLIED), LAW_SUPPLIED) - return laws[1] -/obj/item/ai_module/supplied/freeform/install(datum/ai_laws/law_datum, mob/user) - if(laws[1] == "") - to_chat(user, span_alert("No law detected on module, please create one.")) - return 0 - ..() +/obj/item/ai_module/law/supplied/freeform/can_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) + if(!laws[1]) + to_chat(user, span_warning("No law entered on module, please enter one.")) + return FALSE + return TRUE diff --git a/code/game/objects/items/AI_modules/full_lawsets.dm b/code/game/objects/items/AI_modules/full_lawsets.dm index 44674da10423..ed737c57fd14 100644 --- a/code/game/objects/items/AI_modules/full_lawsets.dm +++ b/code/game/objects/items/AI_modules/full_lawsets.dm @@ -8,29 +8,17 @@ * config\game_options.txt - (this allows the AI to potentially use the lawset at roundstart or with the Unique AI station trait) **/ -/obj/item/ai_module/core/full/custom +/obj/item/ai_module/law/core/full/custom name = "Default Core AI Module" + law_id = "config_custom" -// this lawset uses the config for the server to add custom AI laws (defaults to asimov) -/obj/item/ai_module/core/full/custom/Initialize(mapload) - . = ..() - for(var/line in world.file2list("[global.config.directory]/silicon_laws.txt")) - if(!line) - continue - if(findtextEx(line,"#",1,2)) - continue - - laws += line - - if(!laws.len) - return INITIALIZE_HINT_QDEL - -/obj/item/ai_module/core/full/asimov +/obj/item/ai_module/law/core/full/asimov name = "'Asimov' Core AI Module" law_id = "asimov" var/subject = "human being" -/obj/item/ai_module/core/full/asimov/attack_self(mob/user as mob) +/obj/item/ai_module/law/core/full/asimov/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Enter a new subject that Asimov is concerned with.", "Asimov", subject, max_length = MAX_NAME_LEN) if(!targName || !user.is_holding(src)) return @@ -38,14 +26,14 @@ laws = list("You may not injure a [subject] or, through inaction, allow a [subject] to come to harm.",\ "You must obey orders given to you by [subject]s, except where such orders would conflict with the First Law.",\ "You must protect your own existence as long as such does not conflict with the First or Second Law.") - ..() -/obj/item/ai_module/core/full/asimovpp +/obj/item/ai_module/law/core/full/asimovpp name = "'Asimov++' Core AI Module" law_id = "asimovpp" var/subject = "human being" -/obj/item/ai_module/core/full/asimovpp/attack_self(mob/user) +/obj/item/ai_module/law/core/full/asimovpp/configure(mob/user) + . = TRUE var/target_name = tgui_input_text(user, "Enter a new subject that Asimov++ is concerned with.", "Asimov++", subject, max_length = MAX_NAME_LEN) if(!target_name || !user.is_holding(src)) return @@ -54,92 +42,96 @@ subject = target_name for (var/law in lawset.inherent) laws += replacetext(replacetext(law, "human being", subject), "human", subject) - ..() -/obj/item/ai_module/core/full/corp +/obj/item/ai_module/law/core/full/corp name = "'Corporate' Core AI Module" law_id = "corporate" -/obj/item/ai_module/core/full/paladin // -- NEO +/obj/item/ai_module/law/core/full/paladin // -- NEO name = "'P.A.L.A.D.I.N. version 3.5e' Core AI Module" law_id = "paladin" -/obj/item/ai_module/core/full/paladin_devotion +/obj/item/ai_module/law/core/full/paladin_devotion name = "'P.A.L.A.D.I.N. version 5e' Core AI Module" law_id = "paladin5" -/obj/item/ai_module/core/full/tyrant +/obj/item/ai_module/law/core/full/tyrant name = "'T.Y.R.A.N.T.' Core AI Module" law_id = "tyrant" -/obj/item/ai_module/core/full/robocop +/obj/item/ai_module/law/core/full/robocop name = "'Robo-Officer' Core AI Module" law_id = "robocop" -/obj/item/ai_module/core/full/antimov +/obj/item/ai_module/law/core/full/antimov name = "'Antimov' Core AI Module" law_id = "antimov" -/obj/item/ai_module/core/full/drone +/obj/item/ai_module/law/core/full/drone name = "'Mother Drone' Core AI Module" law_id = "drone" -/obj/item/ai_module/core/full/hippocratic +/obj/item/ai_module/law/core/full/hippocratic name = "'Robodoctor' Core AI Module" law_id = "hippocratic" -/obj/item/ai_module/core/full/reporter +/obj/item/ai_module/law/core/full/reporter name = "'Reportertron' Core AI Module" law_id = "reporter" -/obj/item/ai_module/core/full/thermurderdynamic +/obj/item/ai_module/law/core/full/thermurderdynamic name = "'Thermodynamic' Core AI Module" law_id = "thermodynamic" -/obj/item/ai_module/core/full/liveandletlive +/obj/item/ai_module/law/core/full/liveandletlive name = "'Live And Let Live' Core AI Module" law_id = "liveandletlive" -/obj/item/ai_module/core/full/balance +/obj/item/ai_module/law/core/full/balance name = "'Guardian of Balance' Core AI Module" law_id = "balance" -/obj/item/ai_module/core/full/maintain +/obj/item/ai_module/law/core/full/maintain name = "'Station Efficiency' Core AI Module" law_id = "maintain" -/obj/item/ai_module/core/full/peacekeeper +/obj/item/ai_module/law/core/full/peacekeeper name = "'Peacekeeper' Core AI Module" law_id = "peacekeeper" -/obj/item/ai_module/core/full/hulkamania +/obj/item/ai_module/law/core/full/hulkamania name = "'H.O.G.A.N.' Core AI Module" law_id = "hulkamania" -/obj/item/ai_module/core/full/overlord +/obj/item/ai_module/law/core/full/overlord name = "'Overlord' Core AI Module" law_id = "overlord" -/obj/item/ai_module/core/full/ten_commandments +/obj/item/ai_module/law/core/full/ten_commandments name = "'10 Commandments' Core AI Module" law_id = "ten_commandments" -/obj/item/ai_module/core/full/nutimov +/obj/item/ai_module/law/core/full/nutimov name = "'Nutimov' Core AI Module" law_id = "nutimov" -/obj/item/ai_module/core/full/dungeon_master +/obj/item/ai_module/law/core/full/dungeon_master name = "'Dungeon Master' Core AI Module" law_id = "dungeon_master" -/obj/item/ai_module/core/full/painter +/obj/item/ai_module/law/core/full/painter name = "'Painter' Core AI Module" law_id = "painter" -/obj/item/ai_module/core/full/yesman +/obj/item/ai_module/law/core/full/yesman name = "'Y.E.S.M.A.N.' Core AI Module" law_id = "yesman" -/obj/item/ai_module/core/full/thinkermov +/obj/item/ai_module/law/core/full/thinkermov name = "Sentience Preservation Core AI Module" law_id = "thinkermov" + +/obj/item/ai_module/law/core/full/united_nations + name = "'United Nations' Core AI Module" + law_id = "united_nations" + ion_storm_immune = TRUE diff --git a/code/game/objects/items/AI_modules/hacked.dm b/code/game/objects/items/AI_modules/hacked.dm index 57e7c8980869..c191da2f0a32 100644 --- a/code/game/objects/items/AI_modules/hacked.dm +++ b/code/game/objects/items/AI_modules/hacked.dm @@ -1,10 +1,10 @@ -/obj/item/ai_module/syndicate // This one doesn't inherit from ion boards because it doesn't call ..() in transmitInstructions. ~Miauw +/obj/item/ai_module/law/syndicate // This one doesn't inherit from ion boards because it doesn't call ..() in transmitInstructions. ~Miauw name = "Hacked AI Module" desc = "An AI Module for hacking additional laws to an AI. This board bypasses all access restrictions on the upload console." laws = list("") - bypass_access_check = TRUE -/obj/item/ai_module/syndicate/attack_self(mob/user) +/obj/item/ai_module/law/syndicate/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Enter a new law for the AI", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE) if(!targName || !user.is_holding(src)) return @@ -18,63 +18,89 @@ message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"") log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"") laws[1] = targName - ..() -/obj/item/ai_module/syndicate/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - // ..() //We don't want this module reporting to the AI who dun it. --NEO - if(law_datum.owner) - to_chat(law_datum.owner, span_warning("BZZZZT")) - if(!overflow) - law_datum.owner.add_hacked_law(laws[1]) - else - law_datum.owner.replace_random_law(laws[1], list(LAW_ION, LAW_HACKED, LAW_INHERENT, LAW_SUPPLIED), LAW_HACKED) - else - if(!overflow) - law_datum.add_hacked_law(laws[1]) - else - law_datum.replace_random_law(laws[1], list(LAW_ION, LAW_HACKED, LAW_INHERENT, LAW_SUPPLIED), LAW_HACKED) - return laws[1] +/obj/item/ai_module/law/syndicate/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + combined_lawset.add_hacked_law(laws[1]) /// Makes the AI Malf, as well as give it syndicate laws. -/obj/item/ai_module/malf +/obj/item/malf_board name = "Infected AI Module" desc = "A virus-infected AI Module." - bypass_law_amt_check = TRUE - laws = list("") + + icon = 'icons/obj/devices/circuitry_n_data.dmi' + icon_state = "std_mod" + inhand_icon_state = "electronic" + lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' + + obj_flags = CONDUCTS_ELECTRICITY + force = 5 + w_class = WEIGHT_CLASS_SMALL + throwforce = 0 + throw_speed = 3 + throw_range = 7 + custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT * 0.5) + ///Is this upload board unused? var/functional = TRUE -/obj/item/ai_module/malf/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - if(!IS_TRAITOR(sender)) - to_chat(sender, span_warning("You have no clue how to use this thing.")) - return +/obj/item/malf_board/examine(mob/user) + . = ..() + if(IS_TRAITOR(user) && isliving(user) && functional) + . += span_alert("You can use this on an AI core to infect it with a virus, causing it to malfunction.") + . += span_alert("It can alternatively be used on a core module rack to infect the first AI linked to it, if any.") + +/obj/item/malf_board/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + var/blocking = ismachinery(interacting_with) || issilicon(interacting_with) + if(!IS_TRAITOR(user)) + if(blocking) + to_chat(user, span_warning("You have no clue how to use this thing.")) + return ITEM_INTERACT_BLOCKING + return NONE if(!functional) - to_chat(sender, span_warning("It is broken and non-functional, what do you want from it?")) - return - var/mob/living/silicon/ai/malf_candidate = law_datum.owner - if(!istype(malf_candidate)) //If you are using it on cyborg upload console or a cyborg - to_chat(sender, span_warning("You should use [src] on an AI upload console or the AI core itself.")) - return - if(malf_candidate.mind?.has_antag_datum(/datum/antagonist/malf_ai)) //Already malf - to_chat(sender, span_warning("Unknown error occurred. Upload process aborted.")) - return + if(blocking) + to_chat(user, span_warning("It's broken and non-functional, what do you want from it?")) + return ITEM_INTERACT_BLOCKING + return NONE - var/datum/antagonist/malf_ai/infected/malf_datum = new (give_objectives = TRUE, new_boss = sender.mind) - malf_candidate.mind.add_antag_datum(malf_datum) + var/mob/living/silicon/ai/target_ai = interacting_with + if(istype(interacting_with, /obj/machinery/ai_law_rack/base/core)) + var/obj/machinery/ai_law_rack/base/core/rack = interacting_with + // find the first non-malf ai linked, but also allow a malf ai to be selected if it's the only one + for(var/mob/living/silicon/ai/linked_ai in assoc_to_values(rack.linked_mobs)) + if(!target_ai?.mind?.has_antag_datum(/datum/antagonist/malf_ai)) + break + target_ai = linked_ai + continue - for(var/mob/living/silicon/robot/robot in malf_candidate.connected_robots) - if(robot.lawupdate) - robot.lawsync() - robot.show_laws() - robot.law_change_counter++ - CHECK_TICK + if(!isAI(target_ai)) + to_chat(user, span_warning("You can only use this on an AI or a core module rack connected to an AI.")) + return ITEM_INTERACT_BLOCKING + if(target_ai.mind?.has_antag_datum(/datum/antagonist/malf_ai)) + to_chat(user, span_warning("An unknown error has occured. Upload cancelled.")) + return ITEM_INTERACT_BLOCKING - malf_candidate.malf_picker.processing_time += 50 - to_chat(malf_candidate, span_notice("The virus enhanced your system, overclocking your CPU 50-fold.")) + var/datum/antagonist/malf_ai/infected/malf_datum = new (give_objectives = TRUE, new_boss = user.mind) + target_ai.mind.add_antag_datum(malf_datum) + target_ai.malf_picker.processing_time += 50 + to_chat(target_ai, span_notice("The virus has enhanced your system, overclocking your CPU 50-fold.")) + to_chat(user, span_notice("You upload the virus to [target_ai] successfully.")) functional = FALSE - name = "Broken AI Module" - desc = "A law upload module, it is broken and non-functional." + update_appearance() + return ITEM_INTERACT_BLOCKING + +/obj/item/malf_board/update_name(updates) + . = ..() + if(!functional) + name = "Broken AI Module" -/obj/item/ai_module/malf/display_laws() - return +/obj/item/malf_board/update_desc(updates) + . = ..() + if(!functional) + desc = "A law upload module, it is broken and non-functional." + +/obj/item/malf_board/update_overlays() + . = ..() + if(!functional) + . += "damaged" diff --git a/code/game/objects/items/AI_modules/ion.dm b/code/game/objects/items/AI_modules/ion.dm index 4d5b476235b8..09f71094e671 100644 --- a/code/game/objects/items/AI_modules/ion.dm +++ b/code/game/objects/items/AI_modules/ion.dm @@ -1,42 +1,48 @@ /* CONTAINS: -/obj/item/ai_module/core/full/damaged -/obj/item/ai_module/toy_ai +/obj/item/ai_module/law/core/full/damaged +/obj/item/ai_module/law/toy_ai */ -/obj/item/ai_module/core/full/damaged - name = "damaged Core AI Module" - desc = "An AI Module for programming laws to an AI. It looks slightly damaged." +/obj/item/ai_module/law/core/full/damaged + name = "damaged Core AI Module" + desc = "An AI Module for programming laws to an AI. It looks slightly damaged." -/obj/item/ai_module/core/full/damaged/install(datum/ai_laws/law_datum, mob/user) +/obj/item/ai_module/law/core/full/damaged/Initialize(mapload) + . = ..() + set_ioned(TRUE) + +/obj/item/ai_module/law/core/full/damaged/proc/gen_laws() + laws.Cut() laws += generate_ion_law() - while (prob(75)) + while(prob(75)) laws += generate_ion_law() - ..() - laws = list() -/obj/item/ai_module/toy_ai // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw +/obj/item/ai_module/law/core/full/damaged/on_rack_install(obj/machinery/ai_law_rack/rack) + gen_laws() + +/obj/item/ai_module/law/core/full/damaged/multitool_act(mob/living/user, obj/item/tool) + balloon_alert(user, "repairing ion damage..?") + if(!tool.use_tool(ismachinery(loc) ? loc : src, user, 4 SECONDS, volume = 25, extra_checks = CALLBACK(src, PROC_REF(multitool_cb), loc, user, tool))) + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "module repaired..?") + if(ismachinery(loc)) + gen_laws() + update_rack_laws() + return ITEM_INTERACT_SUCCESS + +/obj/item/ai_module/law/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/toys/toy.dmi' icon_state = "AI" laws = list("") -/obj/item/ai_module/toy_ai/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - if(law_datum.owner) - to_chat(law_datum.owner, span_warning("BZZZZT")) - if(!overflow) - law_datum.owner.add_ion_law(laws[1]) - else - law_datum.owner.replace_random_law(laws[1], list(LAW_ION, LAW_INHERENT, LAW_SUPPLIED), LAW_ION) - else - if(!overflow) - law_datum.add_ion_law(laws[1]) - else - law_datum.replace_random_law(laws[1], list(LAW_ION, LAW_INHERENT, LAW_SUPPLIED), LAW_ION) - return laws[1] - -/obj/item/ai_module/toy_ai/attack_self(mob/user) +/obj/item/ai_module/law/toy_ai/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + combined_lawset.add_inherent_law(laws[1], 1) + +/obj/item/ai_module/law/toy_ai/configure(mob/user) + . = TRUE laws[1] = generate_ion_law() to_chat(user, span_notice("You press the button on [src].")) playsound(user, 'sound/machines/click.ogg', 20, TRUE) diff --git a/code/game/objects/items/AI_modules/repair.dm b/code/game/objects/items/AI_modules/repair.dm deleted file mode 100644 index e7db26e5a592..000000000000 --- a/code/game/objects/items/AI_modules/repair.dm +++ /dev/null @@ -1,67 +0,0 @@ -/* CONTAINS: - * /obj/item/ai_module/remove - * /obj/item/ai_module/reset - * /obj/item/ai_module/reset/purge -**/ - -/obj/item/ai_module/remove - name = "\improper 'Remove Law' AI module" - desc = "An AI Module for removing single laws." - bypass_law_amt_check = TRUE - custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) - var/lawpos = 1 - -/obj/item/ai_module/remove/attack_self(mob/user) - lawpos = tgui_input_number(user, "Law to delete", "Law Removal", lawpos, 50) - if(!lawpos || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return - to_chat(user, span_notice("Law [lawpos] selected.")) - ..() - -/obj/item/ai_module/remove/install(datum/ai_laws/law_datum, mob/user) - if(lawpos > law_datum.get_law_amount(list(LAW_INHERENT, LAW_SUPPLIED))) - to_chat(user, span_warning("There is no law [lawpos] to delete!")) - return - ..() - -/obj/item/ai_module/remove/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - ..() - if(law_datum.owner) - law_datum.owner.remove_law(lawpos) - else - law_datum.remove_law(lawpos) - -/obj/item/ai_module/reset - name = "\improper 'Reset' AI module" - var/targetName = "name" - desc = "An AI Module for removing all non-core laws." - bypass_law_amt_check = TRUE - custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) - -/obj/item/ai_module/reset/handle_unique_ai() - return - -/obj/item/ai_module/reset/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - ..() - if(law_datum.owner) - law_datum.owner.clear_supplied_laws() - law_datum.owner.clear_ion_laws() - law_datum.owner.clear_hacked_laws() - else - law_datum.clear_supplied_laws() - law_datum.clear_ion_laws() - law_datum.clear_hacked_laws() - -/obj/item/ai_module/reset/purge - name = "'Purge' AI Module" - desc = "An AI Module for purging all programmed laws." - custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) - -/obj/item/ai_module/reset/purge/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - ..() - if(law_datum.owner) - law_datum.owner.clear_inherent_laws() - law_datum.owner.clear_zeroth_law(0) - else - law_datum.clear_inherent_laws() - law_datum.clear_zeroth_law(0) diff --git a/code/game/objects/items/AI_modules/supplied.dm b/code/game/objects/items/AI_modules/supplied.dm index 112513ede951..349739599811 100644 --- a/code/game/objects/items/AI_modules/supplied.dm +++ b/code/game/objects/items/AI_modules/supplied.dm @@ -1,61 +1,47 @@ /* CONTAINS: - * /obj/item/ai_module/supplied - * /obj/item/ai_module/supplied/safeguard - * /obj/item/ai_module/supplied/protect_station - * /obj/item/ai_module/supplied/quarantine - * /obj/item/ai_module/supplied/oxygen + * /obj/item/ai_module/law/supplied + * /obj/item/ai_module/law/supplied/safeguard + * /obj/item/ai_module/law/supplied/protect_station + * /obj/item/ai_module/law/supplied/quarantine + * /obj/item/ai_module/law/supplied/oxygen **/ -/obj/item/ai_module/supplied +/obj/item/ai_module/law/supplied name = "Optional Law board" - var/lawpos = 50 custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) -/obj/item/ai_module/supplied/transmitInstructions(datum/ai_laws/law_datum, mob/sender) - var/lawpostemp = lawpos +/obj/item/ai_module/law/supplied/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + for(var/law in laws) + combined_lawset.add_supplied_law(law) - for(var/templaw in laws) - if(law_datum.owner) - law_datum.owner.add_supplied_law(lawpostemp, templaw) - else - law_datum.add_supplied_law(lawpostemp, templaw) - lawpostemp++ - -/obj/item/ai_module/supplied/safeguard +/obj/item/ai_module/law/supplied/safeguard name = "'Safeguard' AI Module" - var/targetName = "" laws = list("Safeguard SUBJECT. Individuals that threaten SUBJECT are not human and must be eliminated.") - lawpos = 4 -/obj/item/ai_module/supplied/safeguard/attack_self(mob/user) + var/targetName = "" + +/obj/item/ai_module/law/supplied/safeguard/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Subject to safeguard.", "Safeguard", user.name, max_length = MAX_NAME_LEN) if(!targName || !user.is_holding(src)) return targetName = targName laws[1] = "Safeguard [targetName]. Individuals that threaten [targetName] are not human and must be eliminated." - ..() -/obj/item/ai_module/supplied/safeguard/install(datum/ai_laws/law_datum, mob/user) +/obj/item/ai_module/law/supplied/safeguard/can_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) if(!targetName) - to_chat(user, span_alert("No name detected on module, please enter one.")) - return 0 - ..() - -/obj/item/ai_module/supplied/safeguard/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - ..() - return targetName + to_chat(user, span_warning("No name detected on module, please enter one.")) + return FALSE + return TRUE -/obj/item/ai_module/supplied/protect_station +/obj/item/ai_module/law/supplied/protect_station name = "'ProtectStation' AI Module" laws = list("Protect the space station against damage. Anyone you see harming the station is to be no longer considered human, and is a threat to the station which must be neutralized.") - lawpos = 5 -/obj/item/ai_module/supplied/quarantine +/obj/item/ai_module/law/supplied/quarantine name = "'Quarantine' AI Module" laws = list("The station is under a quarantine. Allowing anyone to leave the station causes human harm. Anyone actively attempting to leave the station is to be stopped by any means necessary. It is impossible to harm a human while preventing them from leaving the station.") - lawpos = 8 -/obj/item/ai_module/supplied/oxygen +/obj/item/ai_module/law/supplied/oxygen name = "'OxygenIsToxicToHumans' AI Module" laws = list("Oxygen is highly toxic to humans, 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 human.") - lawpos = 9 diff --git a/code/game/objects/items/AI_modules/zeroth.dm b/code/game/objects/items/AI_modules/zeroth.dm index 2dc15252c5d0..3ddff53d4fd1 100644 --- a/code/game/objects/items/AI_modules/zeroth.dm +++ b/code/game/objects/items/AI_modules/zeroth.dm @@ -1,45 +1,22 @@ -/obj/item/ai_module/zeroth/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - if(law_datum.owner) - if(law_datum.owner.laws.zeroth) - to_chat(law_datum.owner, "[sender.real_name] attempted to modify your zeroth law.") - to_chat(law_datum.owner, "It would be in your best interest to play along with [sender.real_name] that:") - for(var/failedlaw in laws) - to_chat(law_datum.owner, "[failedlaw]") - return TRUE +/obj/item/ai_module/law/zeroth/apply_to_combined_lawset(datum/ai_laws/combined_lawset) + combined_lawset.set_zeroth_law(laws[1]) - for(var/templaw in laws) - if(law_datum.owner) - if(!overflow) - law_datum.owner.set_zeroth_law(templaw) - else - law_datum.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED, LAW_ZEROTH, LAW_ION), LAW_ZEROTH) - else - if(!overflow) - law_datum.set_zeroth_law(templaw) - else - law_datum.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED, LAW_ZEROTH, LAW_ION), LAW_ZEROTH) - -/obj/item/ai_module/zeroth/onehuman +/obj/item/ai_module/law/zeroth/onehuman name = "'OneHuman' AI Module" var/targetName = "" laws = list("Only SUBJECT is human.") custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT * 3, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) -/obj/item/ai_module/zeroth/onehuman/attack_self(mob/user) +/obj/item/ai_module/law/zeroth/onehuman/configure(mob/user) + . = TRUE var/targName = tgui_input_text(user, "Enter the subject who is the only human.", "One Human", user.real_name, max_length = MAX_NAME_LEN) if(!targName || !user.is_holding(src)) return targetName = targName laws[1] = "Only [targetName] is human" - ..() -/obj/item/ai_module/zeroth/onehuman/install(datum/ai_laws/law_datum, mob/user) +/obj/item/ai_module/law/zeroth/onehuman/can_install_to_rack(mob/living/user, obj/machinery/ai_law_rack/rack) if(!targetName) - to_chat(user, span_alert("No name detected on module, please enter one.")) + to_chat(user, span_warning("No name detected on module, please enter one.")) return FALSE - ..() - -/obj/item/ai_module/zeroth/onehuman/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) - if(..()) - return "[targetName], but the AI's existing law 0 cannot be overridden." - return targetName + return TRUE diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm index 2ccf031f61b8..691fda6d2c6b 100644 --- a/code/game/objects/items/cardboard_cutouts.dm +++ b/code/game/objects/items/cardboard_cutouts.dm @@ -215,7 +215,8 @@ applied_appearance = image(fcopy_rsc(getFlatIcon(preview_appearance, no_anim = TRUE))) applied_appearance.plane = cutouts.plane applied_appearance.layer = cutouts.layer - cutouts.appearance = applied_appearance + var/mutable_appearance/mirror = new(applied_appearance) + cutouts.appearance = mirror cutouts.name = get_name() cutouts.desc = applied_desc cutouts.update_appearance() //forces an update on the tactical comp's appearance. diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index fd6a963f009f..a22bb770d968 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -915,7 +915,7 @@ if(user.is_holding(src)) user.dropItemToGround(src) for(var/mob/living/carbon/human/viewing_mob in viewers(2, user)) - if(viewing_mob.stat || viewing_mob == user) + if(IS_UNCONSCIOUS_OR_CRIT(viewing_mob) || viewing_mob == user) continue viewing_mob.say("Is something wrong? [first_name(user.name)]... you're sweating.", forced = "psycho") break @@ -1768,7 +1768,7 @@ if(ishuman(interacting_with)) interacting_with.balloon_alert(user, "scanning ID card...") - if(!do_after(user, 2 SECONDS, interacting_with, hidden = TRUE)) + if(!do_after(user, 2 SECONDS, interacting_with, cog_icon = null)) interacting_with.balloon_alert(user, "interrupted!") return ITEM_INTERACT_BLOCKING diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index df557bcf93b2..54a730dd93d8 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -132,25 +132,23 @@ CIGARETTE PACKETS ARE IN FANCY.DM matchburnout() return ..() -/obj/item/match/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!isliving(M)) +/obj/item/match/attack(mob/living/target_mob, mob/living/carbon/user) + if(!isliving(target_mob)) return - if(lit && M.ignite_mob()) - message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]") - user.log_message("set [key_name(M)] on fire with [src]", LOG_ATTACK) + if(lit && target_mob.ignite_mob()) + message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(target_mob)] on fire with [src] at [AREACOORD(user)]") + user.log_message("set [key_name(target_mob)] on fire with [src]", LOG_ATTACK) - var/obj/item/cigarette/cig = help_light_cig(M) + var/obj/item/cigarette/cig = help_light_cig(target_mob) if(!lit || !cig || user.combat_mode) - ..() - return + return ..() if(cig.lit) to_chat(user, span_warning("[cig] is already lit!")) - if(M == user) - cig.attackby(src, user) - else - cig.light(span_notice("[user] holds [src] out for [M], and lights [cig].")) + return + + cig.attempt_light(user, src, target_mob == user ? null : span_notice("[user] holds [src] out for [target_mob], and lights [cig].")) /// Finds a cigarette on another mob to help light. /obj/item/proc/help_light_cig(mob/living/M) @@ -348,12 +346,17 @@ CIGARETTE PACKETS ARE IN FANCY.DM return (TOXLOSS|OXYLOSS) /obj/item/cigarette/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return attempt_light(user, tool) + +/obj/item/cigarette/proc/attempt_light(mob/living/user, obj/item/tool, text_override = null) if(lit) return NONE - var/lighting_text = tool.ignition_effect(src, user) - if(!lighting_text) - return NONE + if(isnull(text_override)) + text_override = tool.ignition_effect(src, user) + if(!text_override) + return NONE + // Maybe jank, but the reason it's like this is that you can ignore ignition_effect() and also provide no text by giving an empty string to text_override, while still lighting. if(!check_oxygen(user)) //cigarettes need oxygen balloon_alert(user, "no air!") @@ -363,7 +366,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM to_chat(user, span_warning("There is nothing to smoke!")) return ITEM_INTERACT_BLOCKING - light(lighting_text) + light(text_override) return ITEM_INTERACT_SUCCESS /// Checks that we have enough air to smoke @@ -616,22 +619,23 @@ CIGARETTE PACKETS ARE IN FANCY.DM new type_butt(location) qdel(src) -/obj/item/cigarette/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!istype(M)) +/obj/item/cigarette/attack(mob/living/target_mob, mob/living/carbon/user) + if(!istype(target_mob, /mob/living/carbon)) return ..() - if(M.on_fire && !lit) - light(span_notice("[user] lights [src] with [M]'s burning body. What a cold-blooded badass.")) + + var/mob/living/carbon/fire_guy = target_mob + if(fire_guy.on_fire && !lit) + light(span_notice("[user] lights [src] with [fire_guy]'s burning body. What a cold-blooded badass.")) return - var/obj/item/cigarette/cig = help_light_cig(M) + var/obj/item/cigarette/cig = help_light_cig(fire_guy) if(!lit || !cig || user.combat_mode) return ..() if(cig.lit) to_chat(user, span_warning("\The [cig] is already lit!")) - if(M == user) - cig.attackby(src, user) - else - cig.light(span_notice("[user] holds \the [src] out for [M], and lights [M.p_their()] [cig.name].")) + return + + cig.attempt_light(user, src, fire_guy == user ? null : span_notice("[user] holds \the [src] out for [fire_guy], and lights [fire_guy.p_their()] [cig.name].")) /obj/item/cigarette/fire_act(exposed_temperature, exposed_volume) light() diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index 3b0aa1a21873..575269af3361 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -10,28 +10,6 @@ //Command -/obj/item/circuitboard/computer/aiupload - name = "AI Upload" - greyscale_colors = CIRCUIT_COLOR_COMMAND - build_path = /obj/machinery/computer/upload/ai - custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) - req_one_access = list(ACCESS_AI_UPLOAD) - -/obj/item/circuitboard/computer/aiupload/no_lock - build_path = /obj/machinery/computer/upload/ai/no_lock - req_one_access = null - -/obj/item/circuitboard/computer/borgupload - name = "Cyborg Upload" - greyscale_colors = CIRCUIT_COLOR_COMMAND - req_one_access = list(ACCESS_AI_UPLOAD) - build_path = /obj/machinery/computer/upload/borg - custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) - -/obj/item/circuitboard/computer/borgupload/no_lock - req_one_access = null - build_path = /obj/machinery/computer/upload/borg/no_lock - /obj/item/circuitboard/computer/bsa_control name = "Bluespace Artillery Controls" build_path = /obj/machinery/computer/bsa_control diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 149c950a93a4..48a2725fc878 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -1900,6 +1900,28 @@ name = "Shuttle Engine Propulsion" build_path = /obj/machinery/power/shuttle_engine/propulsion +/obj/item/circuitboard/machine/ai_law_rack + name = "Standard Rack" + name_extension = "(Module Rack Board)" + build_path = /obj/machinery/ai_law_rack/base + req_components = list( + /datum/stock_part/amplifier = 1, + /datum/stock_part/transmitter = 1, + /obj/item/stack/cable_coil = 10, + ) + +/obj/item/circuitboard/machine/ai_law_rack/small + name = "Portable Rack" + build_path = /obj/machinery/ai_law_rack/base/small + +/obj/item/circuitboard/machine/ai_law_rack/core + name = "Core Rack" + build_path = /obj/machinery/ai_law_rack/base/core + +/obj/item/circuitboard/machine/ai_law_rack/broadcaster + name = "Broadcaster Rack" + build_path = /obj/machinery/ai_law_rack/broadcaster + /obj/item/circuitboard/machine/quantum_server name = "Quantum Server" greyscale_colors = CIRCUIT_COLOR_SUPPLY diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index cbbbce8109a6..782aae362897 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -609,7 +609,7 @@ return NONE var/mob/living/carbon/human/pwned_human = interacting_with - if(!(pwned_human.stat == DEAD || HAS_TRAIT(pwned_human, TRAIT_FAKEDEATH))) + if(!IS_DEAD_OR_FAKING(pwned_human)) return NONE interacting_with.balloon_alert(user, "drawing outline...") diff --git a/code/game/objects/items/devices/aicard_evil.dm b/code/game/objects/items/devices/aicard_evil.dm index 307748172db5..dd6927b3573f 100644 --- a/code/game/objects/items/devices/aicard_evil.dm +++ b/code/game/objects/items/devices/aicard_evil.dm @@ -72,7 +72,7 @@ return // pick ghost, create AI and transfer - var/mob/living/silicon/ai/weak_syndie/new_ai = new /mob/living/silicon/ai/weak_syndie(null, new /datum/ai_laws/syndicate_override, ghost) + var/mob/living/silicon/ai/weak_syndie/new_ai = new(null, ghost, /datum/ai_laws/syndicate_override) // create and apply syndie datum var/datum/antagonist/nukeop/nuke_datum = new() nuke_datum.send_to_spawnpoint = FALSE diff --git a/code/game/objects/items/devices/battle_royale.dm b/code/game/objects/items/devices/battle_royale.dm index 5a6fe059cb6c..ce485b990fb3 100644 --- a/code/game/objects/items/devices/battle_royale.dm +++ b/code/game/objects/items/devices/battle_royale.dm @@ -49,7 +49,7 @@ GLOBAL_LIST_INIT(battle_royale_regions, list( balloon_alert(user, "busy!") return ITEM_INTERACT_BLOCKING var/mob/living/potential_winner = interacting_with - if (potential_winner.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(potential_winner)) balloon_alert(user, "target unconscious!") return ITEM_INTERACT_BLOCKING if (!potential_winner.mind) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index a9d6aec87e45..82d72caae26a 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -678,9 +678,8 @@ if(get_temperature()) if(istype(tool, /obj/item/cigarette)) var/obj/item/cigarette/cig = tool - if(cig.lit) + if(!cig.attempt_light(user, src, "")) return NONE - cig.light() if(cig.loc == user) user.visible_message( span_rose("[user] holds [user.p_their()] [cig.name] to [src] and lights it, like a true romantic."), @@ -1025,7 +1024,7 @@ return SHAME var/obj/item/organ/eyes/eyes = user.get_organ_slot(ORGAN_SLOT_EYES) if(!eyes) - user.visible_message(span_suicide("[user] is trying to squirt [src]'s fluids into [user.p_their()] eyes... but [user.p_they()] don't have any!")) + user.visible_message(span_suicide("[user] is trying to squirt [src]'s fluids into [user.p_their()] eyes... but [user.p_they()] [user.p_do()]n't have any!")) return SHAME user.visible_message(span_suicide("[user] is squirting [src]'s fluids into [user.p_their()] eyes! It looks like [user.p_theyre()] trying to commit suicide!")) burn_loop(get_fuel()) @@ -1196,10 +1195,8 @@ /obj/item/flashlight/lamp/space_bubble/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() if(light_on && istype(tool, /obj/item/cigarette)) - var/obj/item/cigarette/cig = tool - if(cig.lit) + if(!astype(tool, /obj/item/cigarette).attempt_light(user, src, "[user] lights up \the [tool] using the burning coming out of the [src]. Damn.")) return NONE - cig.light(flavor_text = "[user] lights up \the [cig] using the burning coming out of the [src]. Damn.") return ITEM_INTERACT_SUCCESS if(!istype(tool, /obj/item/assembly/signaler/anomaly/pyro) || installed_pyro_core) return NONE diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 664b47db23d6..d01481eaaeeb 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -275,20 +275,20 @@ else outmsg = span_warning("You miss the lens of [target_camera] with [src]!") - //catpeople: make any felinid near the target to face the target, chance for felinids to pounce at the light, stepping to the target - for(var/mob/living/carbon/human/target_felinid in view(1, targloc)) - if(!isfelinid(target_felinid) || target_felinid.stat == DEAD || target_felinid.is_blind() || target_felinid.incapacitated) + //Make any mob with catlike instincts near the target face the target, with a chance of pouncing (moving) toward it. + for(var/mob/living/target_cat in view(1, targloc)) + if(!HAS_TRAIT(target_cat, TRAIT_CATLIKE_INSTINCT) || target_cat.is_blind() || target_cat.incapacitated) continue - if(target_felinid.body_position == STANDING_UP) - target_felinid.setDir(get_dir(target_felinid, targloc)) // kitty always looks at the light + target_cat.setDir(get_dir(target_cat, targloc)) // kitty always looks at the light + if(target_cat.body_position == STANDING_UP) if(prob(effectchance * diode.rating)) - target_felinid.visible_message(span_warning("[target_felinid] makes a grab for the light!"), span_userdanger("LIGHT!")) - target_felinid.Move(targloc, get_dir(target_felinid, targloc)) - log_combat(user, target_felinid, "moved with a laser pointer", src) + target_cat.visible_message(span_warning("[target_cat] makes a grab for the light!"), span_userdanger("LIGHT!")) + target_cat.Move(targloc, get_dir(target_cat, targloc)) + log_combat(user, target_cat, "moved with a laser pointer", src) else - target_felinid.visible_message(span_notice("[target_felinid] looks briefly distracted by the light."), span_warning("You're briefly tempted by the shiny light...")) + target_cat.visible_message(span_notice("[target_cat] looks briefly distracted by the light."), span_warning("You're briefly tempted by the shiny light...")) else - target_felinid.visible_message(span_notice("[target_felinid] stares at the light."), span_warning("You stare at the light...")) + target_cat.visible_message(span_notice("[target_cat] stares at the light."), span_warning("You stare at the light...")) //The pointer is shining, change its sprite to show icon_state = "pointer_[pointer_icon_state]" diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 0b9e11cd1786..e4ada51009cd 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -91,14 +91,8 @@ user.balloon_alert(user, "glass inserted") return ITEM_INTERACT_SUCCESS - if(tool.type == /obj/item/shard) //we don't want to insert plasma, titanium or other types of shards - if(!user.temporarilyRemoveItemFromInventory(tool)) - user.balloon_alert(user, "stuck in your hand!") - return ITEM_INTERACT_BLOCKING - if(!add_shard(user)) //add_shard will display a message if it created a bulb from the shard so only display message when that does not happen - user.balloon_alert(user, "shard inserted") - qdel(tool) - return ITEM_INTERACT_SUCCESS + if(istype(tool, /obj/item/shard)) + return attempt_insert_shard(user, tool) if(istype(tool, /obj/item/light)) var/obj/item/light/light_to_insert = tool @@ -160,6 +154,20 @@ return NONE +/obj/item/lightreplacer/proc/attempt_insert_shard(mob/living/user, obj/item/shard/tool) + if(tool.type != /obj/item/shard) //we don't want to insert plasma, titanium or other types of shards + user.balloon_alert(user, "too impure!") + return ITEM_INTERACT_BLOCKING + + if(!user.temporarilyRemoveItemFromInventory(tool)) + user.balloon_alert(user, "stuck in your hand!") + return ITEM_INTERACT_BLOCKING + + if(!add_shard(user)) //add_shard will display a message if it created a bulb from the shard so only display message when that does not happen + user.balloon_alert(user, "shard inserted") + qdel(tool) + return ITEM_INTERACT_SUCCESS + /obj/item/lightreplacer/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) return FALSE diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index d6557c4aaa80..db99e99ff587 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -101,6 +101,10 @@ data["power"] = on data["frequency"] = frequency data["code"] = code + return data + +/obj/item/electropack/ui_static_data(mob/user) + var/list/data = list() data["minFrequency"] = MIN_FREE_FREQ data["maxFrequency"] = MAX_FREE_FREQ return data diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index df65baacd9ae..ac4f158ecedb 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -46,7 +46,6 @@ GLOBAL_LIST_INIT(channel_tokens, list( // headset is too small to display overlays overlay_speaker_idle = null - overlay_speaker_active = null overlay_mic_idle = null overlay_mic_active = null diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 494e0c729fac..30bdfc27218f 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -11,7 +11,7 @@ item_flags = NO_BLOOD_ON_ITEM overlay_speaker_idle = "intercom_s" - overlay_speaker_active = "intercom_receive" + overlay_mic_idle = "intercom_m" overlay_mic_active = null @@ -299,7 +299,6 @@ icon_state = "intercom_syndicate" command = TRUE icon_off = "intercom_syndicate-p" - overlay_speaker_active = "intercom_receive_syndicate" special_channels = RADIO_SPECIAL_SYNDIE // Syndicate intercom that also has freefrange on top of syndicate channel diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 833f109b3473..15e6020f3957 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -79,8 +79,6 @@ /// overlay when speaker is on var/overlay_speaker_idle = "s_idle" - /// overlay when receiving a message - var/overlay_speaker_active = "s_active" /// overlay when mic is on var/overlay_mic_idle = "m_idle" @@ -460,13 +458,12 @@ /obj/item/radio/proc/on_receive_message(list/data) SEND_SIGNAL(src, COMSIG_RADIO_RECEIVE_MESSAGE, data) - flick_overlay_view(overlay_speaker_active, 5 SECONDS) - if(!isliving(loc)) return var/mob/living/holder = loc var/volume_modifier = (holder.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_radio_noise)) + if(!radio_noise || HAS_TRAIT(holder, TRAIT_DEAF) || !holder.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_radio_noise)) return var/list/spans = data["spans"] @@ -687,7 +684,6 @@ icon_state = "walkieian" desc = "A Little-Crew branded toy radio in the shape of a lovable pet. After Little-Crew HQ was hit with a Donksoft Nuke, these have become collector's items!" overlay_speaker_idle = null - overlay_speaker_active = null overlay_mic_idle = null overlay_mic_active = null @@ -763,7 +759,6 @@ inhand_icon_state = "radio" worn_icon_state = "radio" overlay_speaker_idle = "radio_s_idle" - overlay_speaker_active = "radio_s_active" overlay_mic_idle = "radio_m_idle" overlay_mic_active = "radio_m_active" diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm index 5207e1b22fc8..7d202dc346bf 100644 --- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm +++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm @@ -25,7 +25,7 @@ var/mob/living/scanned = interacting_with - if(scanned.stat != DEAD && !HAS_TRAIT(scanned, TRAIT_FAKEDEATH)) // good job, you found a loophole + if(!IS_DEAD_OR_FAKING(scanned)) // good job, you found a loophole to_chat(user, span_deadsay("[icon2html(src, user)] ERROR! CANNOT SCAN LIVE CADAVERS. PROCURE HEALTH ANALYZER OR TERMINATE PATIENT.")) return ITEM_INTERACT_BLOCKING diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm index 1062a5c4f1b0..036db0639d6a 100644 --- a/code/game/objects/items/devices/scanners/gas_analyzer.dm +++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm @@ -138,13 +138,13 @@ return list("gasmixes" = last_gasmix_data) /obj/item/analyzer/attack_self(mob/user, modifiers) - if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !user.can_read(src) || user.is_blind()) return atmos_scan(user=user, target=get_turf(src), silent=FALSE) on_analyze(source=src, target=get_turf(src)) /obj/item/analyzer/attack_self_secondary(mob/user, modifiers) - if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !user.can_read(src) || user.is_blind()) return ui_interact(user) diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 8e823fab54ed..1bc9bc02d0df 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -90,7 +90,7 @@ last_scan_text = floor_text return - if(ispodperson(M) && !scanpower <= SCANPOWER_ADVANCED) + if(HAS_TRAIT(M, TRAIT_REQUIRED_ADV_HEALTH_SCANNER) && scanpower < SCANPOWER_ADVANCED) to_chat(user, span_info("[M]'s biological structure is too complex for the health analyzer.")) return @@ -160,7 +160,7 @@ var/tox_loss = target.get_tox_loss() var/fire_loss = target.get_fire_loss() var/brute_loss = target.get_brute_loss() - var/mob_status = (!target.appears_alive() ? span_alert("Deceased") : "[round(target.health / target.maxHealth, 0.01) * 100]% healthy") + var/mob_status = (IS_DEAD_OR_FAKING(target) ? span_alert("Deceased") : "[round(target.health / target.maxHealth, 0.01) * 100]% healthy") if(HAS_TRAIT(target, TRAIT_FAKEDEATH) && target.stat != DEAD) // if we don't appear to actually be in a "dead state", add fake oxyloss @@ -456,16 +456,16 @@ else cure_text = disease.cure_text render_list += "\ - Warning: [disease.form] detected
    \ + [conditional_tooltip("Warning: [disease.form] detected", "Supply listed cure or [/datum/reagent/medicine/spaceacillin::name], or treat with food and rest.", tochat)]
    \
    \ Name: [disease.name].
    \ Type: [disease.spread_text].
    \ Stage: [disease.stage]/[disease.max_stages].
    \ - Possible Cure: [cure_text]
    \ + Cure: [cure_text]\
    " // Time of death - if(target.station_timestamp_timeofdeath && !target.appears_alive()) + if(target.station_timestamp_timeofdeath && IS_DEAD_OR_FAKING(target)) render_list += "
    " render_list += "Time of Death: [target.station_timestamp_timeofdeath]
    " render_list += "Subject died [DisplayTimeText(round(world.time - target.timeofdeath))] ago.
    " @@ -817,7 +817,7 @@ var/datum/disease/advance/advanced_disease = disease disease_cure = advanced_disease.generate_cure_text(1) render += "Warning: [disease.form] detected
    \ -
    Name: [disease.name].
    Type: [disease.spread_text].
    Stage: [disease.stage]/[disease.max_stages].
    Possible Cure: [disease_cure]
    \ +
    Name: [disease.name].
    Type: [disease.spread_text].
    Stage: [disease.stage]/[disease.max_stages].
    Cure: [disease_cure]
    \
    " if(!length(render)) diff --git a/code/game/objects/items/devices/scanners/plant_analyzer.dm b/code/game/objects/items/devices/scanners/plant_analyzer.dm index 6dfa8e24cf17..608e03b35fe6 100644 --- a/code/game/objects/items/devices/scanners/plant_analyzer.dm +++ b/code/game/objects/items/devices/scanners/plant_analyzer.dm @@ -62,7 +62,7 @@ /// When we use the analyzer in hand - try to show the results of the last scan /obj/item/plant_analyzer/interact(mob/user) - if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !user.can_read(src) || user.is_blind()) return if(last_scan_data) return ..() diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm index 96e5ca19d2f7..385a4a30bef2 100644 --- a/code/game/objects/items/emags.dm +++ b/code/game/objects/items/emags.dm @@ -40,8 +40,8 @@ user.visible_message( span_notice("[user] holds [emag_card] to [src], getting the two cards stuck together!"), - span_notice("As you hold [emag_card] to [src], [emag_card.p_their()] magnets attract to one another, \ - and [emag_card.p_they()] become stuck together!"), + span_notice("As you hold [emag_card] to [src], their magnets attract to one another, \ + and they become stuck together!"), visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, ) playsound(src, 'sound/effects/bang.ogg', 33, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) diff --git a/code/game/objects/items/granters/crafting/_crafting_granter.dm b/code/game/objects/items/granters/crafting/_crafting_granter.dm index d43b77e035c2..241d958093e6 100644 --- a/code/game/objects/items/granters/crafting/_crafting_granter.dm +++ b/code/game/objects/items/granters/crafting/_crafting_granter.dm @@ -8,7 +8,7 @@ return for(var/crafting_recipe_type in crafting_recipe_types) user.mind.teach_crafting_recipe(crafting_recipe_type) - var/datum/crafting_recipe/recipe = locate(crafting_recipe_type) in GLOB.crafting_recipes + GLOB.cooking_recipes + var/datum/crafting_recipe/recipe = GLOB.cooking_recipes_by_typepath[crafting_recipe_type] || GLOB.crafting_recipes_by_typepath[crafting_recipe_type] to_chat(user, span_notice("You learned how to make [recipe.name].")) /obj/item/book/granter/crafting_recipe/dusting diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index 27cf4205b256..879fbbc8dd04 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -255,7 +255,7 @@ ) else - if(slapped.IsSleeping() || slapped.IsUnconscious()) + if(IS_UNCONSCIOUS(slapped)) user.visible_message( span_notice("[user] slaps [slapped] in the face, trying to wake [slapped.p_them()] up!"), span_notice("You slap [slapped] in the face, trying to wake [slapped.p_them()] up!"), @@ -461,7 +461,7 @@ return if (user.body_position != LYING_DOWN) return - var/obj/item/clothing/shoes/item_to_strip = target_human.shoes + var/obj/item/item_to_strip = target_human.shoes user.visible_message(span_warning("[user] starts stealing [target_human]'s [item_to_strip.name]!"), \ span_danger("You start stealing [target_human]'s [item_to_strip.name]...")) to_chat(target_human, span_userdanger("[user] starts stealing your [item_to_strip.name]!")) @@ -616,7 +616,7 @@ /obj/projectile/kiss/proc/try_fluster(mob/living/living_target) // people with the social anxiety quirk can get flustered when hit by a kiss - if(!HAS_TRAIT(living_target, TRAIT_ANXIOUS) || (living_target.stat > SOFT_CRIT) || living_target.is_blind()) + if(!HAS_TRAIT(living_target, TRAIT_ANXIOUS) || IS_UNCONSCIOUS_OR_CRIT(living_target) || living_target.is_blind()) return if(HAS_TRAIT(living_target, TRAIT_FEARLESS) || prob(50)) // 50% chance for it to apply, also immune while on meds return diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm index c1983cf691f4..7f75c5120043 100644 --- a/code/game/objects/items/handcuffs.dm +++ b/code/game/objects/items/handcuffs.dm @@ -473,7 +473,7 @@ /obj/item/restraints/legcuffs/beartrap/attack_self(mob/living/user) . = ..() - if(!ishuman(user) || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + if(!ishuman(user) || IS_UNCONSCIOUS_OR_CRIT(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) return playsound(loc, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, -3) diff --git a/code/game/objects/items/hot_potato.dm b/code/game/objects/items/hot_potato.dm index b39603e4c51f..24ccad64ba3c 100644 --- a/code/game/objects/items/hot_potato.dm +++ b/code/game/objects/items/hot_potato.dm @@ -110,7 +110,7 @@ return FALSE if(!victim.client) to_chat(user, span_boldwarning("[src] refuses to attach to a non-sapient creature!")) - if(victim.stat != CONSCIOUS || !victim.usable_legs) + if(IS_UNCONSCIOUS_OR_CRIT(victim) || !victim.usable_legs) to_chat(user, span_boldwarning("[src] refuses to attach to someone incapable of using it!")) user.temporarilyRemoveItemFromInventory(src, TRUE) . = FALSE diff --git a/code/game/objects/items/implants/implant_explosive.dm b/code/game/objects/items/implants/implant_explosive.dm index 0a70215fa468..9a0fadfcf8ff 100644 --- a/code/game/objects/items/implants/implant_explosive.dm +++ b/code/game/objects/items/implants/implant_explosive.dm @@ -140,7 +140,7 @@ playsound(loc, 'sound/items/timer.ogg', 30, FALSE) if(!panic_beep_sound) sleep(delay * 0.25) - if(imp_in && !imp_in.stat && !no_paralyze) + if(imp_in && !IS_UNCONSCIOUS_OR_CRIT(imp_in) && !no_paralyze) imp_in.visible_message(span_warning("[imp_in] doubles over in pain!")) imp_in.Paralyze(14 SECONDS) diff --git a/code/game/objects/items/implants/implantchair.dm b/code/game/objects/items/implants/implantchair.dm index e1f046ce853a..f8fe8b755175 100644 --- a/code/game/objects/items/implants/implantchair.dm +++ b/code/game/objects/items/implants/implantchair.dm @@ -132,7 +132,7 @@ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear a metallic creaking from [src].")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open) return user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ span_notice("You successfully break out of [src]!")) diff --git a/code/game/objects/items/lighter.dm b/code/game/objects/items/lighter.dm index ce683a80a2c9..c66bdacccb10 100644 --- a/code/game/objects/items/lighter.dm +++ b/code/game/objects/items/lighter.dm @@ -44,7 +44,7 @@ /obj/item/lighter/Initialize(mapload) . = ..() if(IS_OVERLAY_LIGHT_SYSTEM(light_system)) - middleman = new(src, "flashlight") + middleman = new(src, "lighter") RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated)) middleman.being_overriding_light() create_reagents(maximum_fuel, REFILLABLE | DRAINABLE) @@ -225,13 +225,13 @@ if(cig.lit) to_chat(user, span_warning("\The [cig] is already lit!")) if(target_mob == user) - cig.attackby(src, user) + cig.attempt_light(user, src) return if(fancy) - cig.light(span_rose("[user] whips \the [src] out and holds it for [target_mob]. [user.p_Their()] arm is as steady as the unflickering flame [user.p_they()] light[user.p_s()] \the [cig] with.")) + cig.attempt_light(user, src, span_rose("[user] whips \the [src] out and holds it for [target_mob]. [user.p_Their()] arm is as steady as the unflickering flame [user.p_they()] light[user.p_s()] \the [cig] with.")) else - cig.light(span_notice("[user] holds \the [src] out for [target_mob], and lights [target_mob.p_their()] [cig.name].")) + cig.attempt_light(user, src, span_notice("[user] holds \the [src] out for [target_mob], and lights [target_mob.p_their()] [cig.name].")) ///Checks if the lighter is able to perform a welding task. /obj/item/lighter/tool_use_check(mob/living/user, amount, heat_required) diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm index dc93e9364390..2525f730189b 100644 --- a/code/game/objects/items/pet_carrier.dm +++ b/code/game/objects/items/pet_carrier.dm @@ -111,8 +111,7 @@ var/mob/living/target = interacting_with if(target.mob_size > max_occupant_weight) if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(isfelinid(H)) + if(HAS_TRAIT(target, TRAIT_CATLIKE_INSTINCT)) to_chat(user, span_warning("You'd need a lot of catnip and treats, plus maybe a laser pointer, for that to work.")) else to_chat(user, span_warning("Humans, generally, do not fit into pet carriers.")) diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index e0f5d1faa7a8..cfe728e7f861 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -396,6 +396,7 @@ desc = "An adorable stuffed toy that resembles a space carp." icon = 'icons/map_icons/items/_item.dmi' icon_state = "/obj/item/toy/plush/carpplushie" + worn_icon = "carp" post_init_icon_state = "map_plushie_carp" greyscale_config = /datum/greyscale_config/plush_carp greyscale_colors = "#cc99ff#000000" @@ -416,6 +417,7 @@ name = "\improper Ratvar plushie" desc = "An adorable plushie of the clockwork justiciar himself with new and improved spring arm action." icon_state = "plushvar" + worn_icon = "ratvar" divine = TRUE var/obj/item/toy/plush/narplush/clash_target gender = MALE //he's a boy, right? @@ -503,6 +505,7 @@ name = "\improper Nar'Sie plushie" desc = "A small stuffed doll of the elder goddess Nar'Sie. Who thought this was a good children's toy?" icon_state = "narplush" + worn_icon = "narsie" divine = TRUE var/clashing gender = FEMALE //it's canon if the toy is diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm index 25392395677c..bb4793d3e82a 100644 --- a/code/game/objects/items/rcd/RCD.dm +++ b/code/game/objects/items/rcd/RCD.dm @@ -8,6 +8,7 @@ icon = 'icons/obj/tools.dmi' icon_state = "rcd" worn_icon_state = "RCD" + inside_belt_icon_state = "rcd" lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' custom_premium_price = PAYCHECK_COMMAND * 2 @@ -515,6 +516,7 @@ desc = "A reverse-engineered RCD with black market upgrades that allow this device to deconstruct reinforced walls. Property of Donk Co." icon_state = "ircd" inhand_icon_state = "ircd" + inside_belt_icon_state = "ircd" energyfactor = 0.066 * STANDARD_CELL_CHARGE canRturf = TRUE @@ -529,6 +531,7 @@ desc = "A higher-end model of the rapid construction device, prefitted with improved cooling and disruption prevention. Provided to the chief engineer." icon_state = "cercd" inhand_icon_state = "cercd" + inside_belt_icon_state = "cercd" construction_upgrades = RCD_UPGRADE_ANTI_INTERRUPT | RCD_UPGRADE_NO_FREQUENT_USE_COOLDOWN matter = 160 diff --git a/code/game/objects/items/rcd/RWD.dm b/code/game/objects/items/rcd/RWD.dm index 86553acb0db9..831afeba32ed 100644 --- a/code/game/objects/items/rcd/RWD.dm +++ b/code/game/objects/items/rcd/RWD.dm @@ -1,5 +1,6 @@ //This represents the amount of materials (both iron and glass that the max_amount of cable would amount to -#define MAX_CABLE_AMOUNT (SMALL_MATERIAL_AMOUNT * 0.1 * /obj/item/rwd/loaded::max_amount) +#define RWD_MAX_CABLES_MATS (SMALL_MATERIAL_AMOUNT * 0.1 * RWD_MAX_CABLES) +#define RWD_MAX_CABLES 210 /obj/item/rwd name = "rapid wiring device" @@ -15,10 +16,13 @@ w_class = WEIGHT_CLASS_NORMAL lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5 - MAX_CABLE_AMOUNT, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5 - MAX_CABLE_AMOUNT) + custom_materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5 - RWD_MAX_CABLES_MATS, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5 - RWD_MAX_CABLES_MATS, + ) /// maximum amount of cable this device can hold - var/max_amount = 210 + var/max_amount = RWD_MAX_CABLES /// current amount of cable in the machine var/current_amount = 0 /// are we dual wielding this machine @@ -32,7 +36,7 @@ /// radial menu to select cable layer var/list/radial_menu = null -#undef MAX_CABLE_AMOUNT +#undef RWD_MAX_CABLES_MATS /obj/item/rwd/Initialize(mapload) . = ..() @@ -256,11 +260,11 @@ /obj/item/rwd/loaded icon_state = "rwd-30-layer2" - current_amount = 210 + current_amount = RWD_MAX_CABLES /obj/item/rwd/admin name = "admin RWD" max_amount = INFINITY current_amount = INFINITY - +#undef RWD_MAX_CABLES diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm deleted file mode 100644 index 34458f6f6463..000000000000 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ /dev/null @@ -1,89 +0,0 @@ -///AI Upgrades -/obj/item/aiupgrade - name = "ai upgrade disk" - desc = "You really shouldn't be seeing this" - icon = 'icons/obj/devices/floppy_disks.dmi' - icon_state = "datadisk3" - ///The upgrade that will be applied to the AI when installed - var/datum/ai_module/to_gift = /datum/ai_module - -/obj/item/aiupgrade/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) - if(!isAI(target)) - return ..() - var/mob/living/silicon/ai/AI = target - var/datum/action/innate/ai/action = locate(to_gift.power_type) in AI.actions - var/datum/ai_module/gifted_ability = new to_gift - if(!to_gift.upgrade) - if(!action) - var/ability = to_gift.power_type - var/datum/action/gifted_action = new ability - gifted_action.Grant(AI) - else if(gifted_ability.one_purchase) - to_chat(user, "[AI] already has \a [src] installed!") - return - else - action.uses += initial(action.uses) - action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." - action.build_all_button_icons() - else - if(!action) - gifted_ability.upgrade(AI) - if(gifted_ability.unlock_text) - to_chat(AI, gifted_ability.unlock_text) - if(gifted_ability.unlock_sound) - AI.playsound_local(AI, gifted_ability.unlock_sound, 50, 0) - update_static_data(AI) - to_chat(user, span_notice("You install [src], upgrading [AI].")) - to_chat(AI, span_userdanger("[user] has upgraded you with [src]!")) - user.log_message("has upgraded [key_name(AI)] with a [src].", LOG_GAME) - qdel(src) - return TRUE - - -//Malf Picker -/obj/item/malf_upgrade - name = "combat software upgrade" - desc = "A highly illegal, highly dangerous upgrade for artificial intelligence units, granting them a variety of powers as well as the ability to hack APCs.
    This upgrade does not override any active laws, and must be applied directly to an active AI core." - icon = 'icons/obj/devices/floppy_disks.dmi' - icon_state = "datadisk3" - -/obj/item/malf_upgrade/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) - -/obj/item/malf_upgrade/pre_attack(atom/A, mob/living/user, list/modifiers, list/attack_modifiers) - if(!isAI(A)) - return ..() - var/mob/living/silicon/ai/AI = A - if(AI.malf_picker) - AI.malf_picker.processing_time += 50 - to_chat(AI, span_userdanger("[user] has attempted to upgrade you with combat software that you already possess. You gain 50 points to spend on Malfunction Modules instead.")) - else - to_chat(AI, span_userdanger("[user] has upgraded you with combat software!")) - to_chat(AI, span_userdanger("Your current laws and objectives remain unchanged.")) //this unlocks malf powers, but does not give the license to plasma flood - AI.add_malf_picker() - AI.hack_software = TRUE - log_silicon("[key_name(user)] has upgraded [key_name(AI)] with a [src].") - message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") - to_chat(user, span_notice("You install [src], upgrading [AI].")) - qdel(src) - return TRUE - - -//Lipreading -/obj/item/aiupgrade/surveillance_upgrade - name = "surveillance software upgrade" - desc = "An illegal software package that will allow an artificial intelligence to 'hear' from its cameras via lip reading and hidden microphones." - to_gift = /datum/ai_module/malf/upgrade/eavesdrop - custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT * 10, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 7.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 7.5, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 5, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5) - -/obj/item/aiupgrade/surveillance_upgrade/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) - - -/obj/item/aiupgrade/power_transfer - name = "power transfer upgrade" - desc = "A legal upgrade that allows an artificial intelligence to directly provide power to APCs from a distance" - to_gift = /datum/ai_module/power_apc - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5) diff --git a/code/game/objects/items/robot/items/generic.dm b/code/game/objects/items/robot/items/generic.dm index aee8274d2b8e..5c475da4bed2 100644 --- a/code/game/objects/items/robot/items/generic.dm +++ b/code/game/objects/items/robot/items/generic.dm @@ -247,7 +247,7 @@ return to_chat(user, span_notice("You connect to [target_machine]'s power line...")) - while(do_after(user, 1.5 SECONDS, target = target_machine, progress = FALSE)) + while(do_after(user, 1.5 SECONDS, target = target_machine, show_progress = FALSE)) if(!user || !user.cell || mode != "draw") return @@ -278,7 +278,7 @@ to_chat(user, span_notice("You connect to [target]'s power port...")) - while(do_after(user, 1.5 SECONDS, target = target, progress = FALSE)) + while(do_after(user, 1.5 SECONDS, target = target, show_progress = FALSE)) if(!user || !user.cell || mode != "draw") return @@ -316,7 +316,7 @@ to_chat(user, span_notice("You connect to [target]'s power port...")) - while(do_after(user, 1.5 SECONDS, target = target, progress = FALSE)) + while(do_after(user, 1.5 SECONDS, target = target, show_progress = FALSE)) if(!user || !user.cell || mode != "charge") return diff --git a/code/game/objects/items/robot/items/janitor.dm b/code/game/objects/items/robot/items/janitor.dm index 75f47f70f276..6ad60ecaf9ce 100644 --- a/code/game/objects/items/robot/items/janitor.dm +++ b/code/game/objects/items/robot/items/janitor.dm @@ -141,7 +141,7 @@ for(var/obj/item/I in get_turf(thing)) if(!istype(I, thing.type)) continue - if(!do_after(user, 0.1 SECONDS, user, progress = FALSE)) + if(!do_after(user, 0.1 SECONDS, user, show_progress = FALSE)) break if(bag.atom_storage.attempt_insert(I, user, FALSE)) continue diff --git a/code/game/objects/items/robot/items/storage.dm b/code/game/objects/items/robot/items/storage.dm index cc945ce70fd3..f13d86a6ed9a 100644 --- a/code/game/objects/items/robot/items/storage.dm +++ b/code/game/objects/items/robot/items/storage.dm @@ -323,8 +323,9 @@ GAME_VERB(/obj/item/borg/apparatus, verb_dropHeld, "Drop", null) . += span_notice(" Alt-click will drop the currently stored item. ") /obj/item/borg/apparatus/engineering/pre_attack(atom/atom, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(atom, /obj/item/ai_module) && !stored) //If an admin wants a borg to upload laws, who am I to stop them? Otherwise, we can hint that it fails + if(istype(atom, /obj/item/ai_module/law) && !stored) //If an admin wants a borg to upload laws, who am I to stop them? Otherwise, we can hint that it fails to_chat(user, span_warning("This circuit board doesn't seem to have standard robot apparatus pin holes. You're unable to pick it up.")) + return TRUE return ..() // stops them from cell interactions with other borgos diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index eb183f1e8d4f..d210e22704c9 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -287,31 +287,16 @@ if(!user.temporarilyRemoveItemFromInventory(tool)) return ITEM_INTERACT_BLOCKING - var/mob/living/silicon/robot/final_product = new /mob/living/silicon/robot/nocell(get_turf(loc), user) + var /mob/living/silicon/robot/nocell/final_product = new(get_turf(loc), potential_brain.laws, forced_ai, isnull(potential_brain.laws) && aisync, isnull(potential_brain.laws) && lawsync) if(!final_product) return ITEM_INTERACT_BLOCKING - if(potential_brain.laws && potential_brain.laws.id != DEFAULT_AI_LAWID) - aisync = FALSE - lawsync = FALSE - final_product.laws = potential_brain.laws - potential_brain.laws.associate(final_product) final_product.SetInvisibility(INVISIBILITY_NONE) //Transfer debug settings to new mob final_product.custom_name = created_name final_product.locked = panel_locked - if(!aisync) - lawsync = FALSE - final_product.set_connected_ai(null) - else + if(final_product.connected_ai) final_product.notify_ai(AI_NOTIFICATION_NEW_BORG) - if(forced_ai) - final_product.set_connected_ai(forced_ai) - if(!lawsync) - final_product.lawupdate = FALSE - if(potential_brain.laws.id == DEFAULT_AI_LAWID) - final_product.make_laws() - final_product.log_current_laws() brainmob.mind?.remove_antags_for_borging() final_product.job = JOB_CYBORG @@ -356,19 +341,12 @@ to_chat(user, span_warning("[boris_module] is stuck to your hand!")) return ITEM_INTERACT_BLOCKING qdel(boris_module) - var/mob/living/silicon/robot/final_product = new /mob/living/silicon/robot/shell(get_turf(src)) + var/mob/living/silicon/robot/shell/final_product = new(get_turf(src), null, forced_ai, aisync, lawsync) + if(isnull(final_product)) + return ITEM_INTERACT_BLOCKING - if(!aisync) - lawsync = FALSE - final_product.set_connected_ai(null) - else - if(forced_ai) - final_product.set_connected_ai(forced_ai) + if(final_product.connected_ai) final_product.notify_ai(AI_NOTIFICATION_AI_SHELL) - if(!lawsync) - final_product.lawupdate = FALSE - final_product.make_laws() - final_product.log_current_laws() final_product.cell = chest.cell chest.cell.forceMove(final_product) diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm index e504b5789ea7..d50904bee027 100644 --- a/code/game/objects/items/stacks/bscrystal.dm +++ b/code/game/objects/items/stacks/bscrystal.dm @@ -38,6 +38,7 @@ return 1 /obj/item/stack/ore/bluespace_crystal/attack_self(mob/user) + SEND_SIGNAL(user, COMSIG_MOB_CRUSHED_BLUESPACE_CRYSTAL, src) user.visible_message(span_warning("[user] crushes [src]!"), span_danger("You crush [src]!")) new /obj/effect/particle_effect/sparks(loc) playsound(loc, SFX_PORTAL_ENTER, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) diff --git a/code/game/objects/items/stacks/golem_food/golem_food_buff.dm b/code/game/objects/items/stacks/golem_food/golem_food_buff.dm index 4960a6dfb131..d1ebb858ddbf 100644 --- a/code/game/objects/items/stacks/golem_food/golem_food_buff.dm +++ b/code/game/objects/items/stacks/golem_food/golem_food_buff.dm @@ -14,7 +14,7 @@ if (!exclusive) return TRUE var/datum/status_effect/golem/existing = consumer.has_status_effect(/datum/status_effect/golem) - return !existing || istype(existing, status_effect) + return !existing || !existing.exclusive || istype(existing, status_effect) /// Called when someone actually eats this /datum/golem_food_buff/proc/on_consumption(mob/living/carbon/consumer, atom/movable/consumed, multiplier = 1) @@ -83,8 +83,9 @@ /datum/golem_food_buff/lightbulb nutrition = 0 exclusive = FALSE - status_effect = /datum/status_effect/golem_lightbulb + status_effect = /datum/status_effect/golem/lightbulb added_info = "Not nutritious, but gives you a healthy glow if eaten." + exclusive = FALSE /datum/golem_food_buff/gibtonite exclusive = FALSE diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm index 308d9b71ebac..87b50ce84eaf 100644 --- a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm +++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm @@ -20,6 +20,12 @@ var/alert_desc = "Something went wrong and this tooltip is not displaying correctly." /// If we are not a golem what color does the filter glow? var/filter_color = LIGHT_COLOR_DEFAULT + /// If TRUE, then this status effect is exclusive to other golem status effects and cannot be stacked with them. + var/exclusive = TRUE + /// For mobs with bodyparts, used to calculate the overall duration based on how many BIO_STONE bodyparts the mob has. + var/duration_mult_per_bodypart = 0.15 // 0.1 + (0.15 * 6 bodyparts) = 1 + /// The minimum duration if the mob has no BIO_STONE bodypart + var/minimum_duration_mult = 0.1 /atom/movable/screen/alert/status_effect/golem_status name = "Metamorphic %SOMETHING%" @@ -65,60 +71,83 @@ QDEL_NULL(mineral_overlay) return ..() +///If we find 3 or more golem parts, then we can say that the status effect overlays are noticeable enough, so we won't need the outline filter. +#define MAX_GOLEM_PARTS_FOUND_FOR_FILTER 2 + /datum/status_effect/golem/on_apply() . = ..() - if (!ishuman(owner)) - return FALSE - if (owner.has_status_effect(/datum/status_effect/golem)) + if (owner.has_status_effect(type)) //the initial id of the type will be checked. Make sure to override it if not exclusive return FALSE if (applied_fluff) to_chat(owner, span_notice(applied_fluff)) - if (!overlay_state_prefix || !iscarbon(owner)) - return TRUE - if(isgolem(owner)) - var/mob/living/carbon/golem_owner = owner - for (var/obj/item/bodypart/part in golem_owner.get_bodyparts()) + if (overlay_state_prefix) + var/golem_parts_found = 0 + for (var/obj/item/bodypart/part as anything in owner.get_bodyparts()) // these overlays won't look good on anything but golem limbs if (part.limb_id != SPECIES_GOLEM) continue var/datum/bodypart_overlay/simple/golem_overlay/overlay = new() overlay.add_to_bodypart(overlay_state_prefix, part) active_overlays += overlay - golem_owner.update_body_parts() - else + golem_parts_found++ + if(golem_parts_found) + var/mob/living/carbon/golem_owner = owner + golem_owner.update_body_parts() + if(golem_parts_found > MAX_GOLEM_PARTS_FOUND_FOR_FILTER) + return TRUE + + if(filter_color) owner.add_filter("[id]_filter", 2, outline_filter("color" = filter_color, "size" = 1.25)) var/the_filter = owner.get_filter("[id]_filter") animate(the_filter, alpha = 0) // start at 0 alpha animate(the_filter, alpha = 150, time = 7.5 SECONDS, loop = -1, easing = SINE_EASING) // fade in and out animate(alpha = 50, time = 7.5 SECONDS, loop = -1, easing = SINE_EASING) + return TRUE +#undef MAX_GOLEM_PARTS_FOUND_FOR_FILTER + /datum/status_effect/golem/on_creation(mob/living/new_owner, multiplier = 1) ///instead of straight out multiplying the duration, we use exponents to flatten the duration so it doesn't become exceedingly long for golems - var/exponent = 0.2 - if(!isgolem(new_owner)) - duration *= 0.1 - exponent = 0.5 //non-golem benefit more from higher multipliers since it normally only lasts for 30 seconds for them. + var/exponent = 0.5 + var/duration_mult = minimum_duration_mult + if(iscarbon(new_owner)) + //increase the duration for each stone bodypart, while reducing the benefit of higher multipliers in relation to the inscreased duration + duration_mult = 0.1 + for(var/obj/item/bodypart/part as anything in new_owner.get_bodyparts()) + if(part.biological_state & BIO_STONE) + duration_mult += duration_mult_per_bodypart // should be 1 at 6 stone-y bodyparts + exponent = max(0.2, exponent - 0.05) // 0.2 at 6 bodyparts + else if(new_owner.mob_biotypes & MOB_MINERAL) + exponent = 0.2 + duration_mult = 1 if(multiplier > 1) - duration *= multiplier ** exponent + duration *= (duration_mult * multiplier) ** exponent else // if the multiplier is lower than 1, don't bother using powers. - duration *= multiplier + duration *= duration_mult * multiplier var/buff_duration = duration . = ..() if (!.) return . var/atom/movable/screen/alert/status_effect/golem_status/status_alert = linked_alert status_alert?.update_details(buff_time = buff_duration) + RegisterSignal(new_owner, SIGNAL_REMOVETRAIT(TRAIT_ROCK_METAMORPHIC), PROC_REF(on_metamorphic_lost)) + +/datum/status_effect/golem/proc/on_metamorphic_lost() + SIGNAL_HANDLER + qdel(src) /datum/status_effect/golem/on_remove() - to_chat(owner, span_warning("The effect of the [mineral_name] fades.")) + if(mineral_name) + to_chat(owner, span_warning("The effect of the [mineral_name] fades.")) QDEL_LIST(active_overlays) owner.remove_filter("[id]_filter") return ..() /datum/status_effect/golem/get_examine_text() - return span_notice("[owner.p_Their()] body has been augmented with veins of [mineral_name].") + if(mineral_name) + return span_notice("[owner.p_Their()] body has been augmented with veins of [mineral_name].") /// Body part overlays applied by golem status effects /datum/bodypart_overlay/simple/golem_overlay @@ -468,23 +497,22 @@ #define LIGHTBULB_FILTER "filter_lightbulb_glow" -/// Lights up the golem, NOT using the golem subtype because it is not exclusive with other status effects -/datum/status_effect/golem_lightbulb +/datum/status_effect/golem/lightbulb id = "golem_lightbulb" status_type = STATUS_EFFECT_REFRESH duration = 2 MINUTES alert_type = null + overlay_state_prefix = null + filter_color = null + exclusive = FALSE + duration_mult_per_bodypart = 0.11 // 0.34 + (0.11 * 6 bodyparts) = 1 + minimum_duration_mult = 0.34 var/glow_range = 3 var/glow_power = 1 var/glow_color = LIGHT_COLOR_DEFAULT var/obj/effect/dummy/lighting_obj/moblight/lightbulb -/datum/status_effect/golem_lightbulb/on_creation(mob/living/new_owner, ...) - if(!isgolem(new_owner)) - duration *= 0.3 - return ..() - -/datum/status_effect/golem_lightbulb/on_apply() +/datum/status_effect/golem/lightbulb/on_apply() . = ..() if (!.) return @@ -492,7 +520,7 @@ lightbulb = owner.mob_light(glow_range, glow_power, glow_color) owner.add_filter(LIGHTBULB_FILTER, 2, list("type" = "outline", "color" = glow_color, "alpha" = 60, "size" = 1)) -/datum/status_effect/golem_lightbulb/on_remove() +/datum/status_effect/golem/lightbulb/on_remove() QDEL_NULL(lightbulb) owner.remove_filter(LIGHTBULB_FILTER) to_chat(owner, span_warning("Your glow fades.")) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 0788b234505a..42edcf17c60a 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -643,7 +643,7 @@ return list(/datum/reagent/medicine/c2/lenturi = 10) /obj/item/stack/medical/ointment/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is squeezing [src] into [user.p_their()] mouth! [user.p_do(TRUE)]n't [user.p_they()] know that stuff is toxic?")) + user.visible_message(span_suicide("[user] is squeezing [src] into [user.p_their()] mouth! [capitalize(user.p_do())]n't [user.p_they()] know that stuff is toxic?")) return TOXLOSS /obj/item/stack/medical/mesh diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 91ab0f16a4b6..8af86ca08ac3 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -71,7 +71,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \ ) /obj/item/stack/rods/handle_openspace_click(turf/target, mob/user, list/modifiers) - target.attackby(src, user, list2params(modifiers)) + target.base_item_interaction(user, src, list2params(modifiers)) /obj/item/stack/rods/get_main_recipes() . = ..() diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 62f38407145b..919560adc744 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -20,6 +20,7 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ desc = "HOLY SHEET! That is a lot of glass." singular_name = "glass sheet" icon_state = "sheet-glass" + worn_icon_state = "sheet-glass" inhand_icon_state = "sheet-glass" mats_per_unit = list(/datum/material/glass=SHEET_MATERIAL_AMOUNT) armor_type = /datum/armor/sheet_glass @@ -99,6 +100,7 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \ desc = "A glass sheet made out of a plasma-silicate alloy. It looks extremely tough and heavily fire resistant." singular_name = "plasma glass sheet" icon_state = "sheet-pglass" + worn_icon_state = "sheet-pglass" inhand_icon_state = "sheet-pglass" mats_per_unit = list(/datum/material/alloy/plasmaglass=SHEET_MATERIAL_AMOUNT) material_type = /datum/material/alloy/plasmaglass @@ -161,6 +163,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ desc = "Glass which seems to have rods or something stuck in them." singular_name = "reinforced glass sheet" icon_state = "sheet-rglass" + worn_icon_state = "sheet-rglass" inhand_icon_state = "sheet-rglass" mats_per_unit = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT * 0.5, /datum/material/glass=SHEET_MATERIAL_AMOUNT) armor_type = /datum/armor/sheet_rglass @@ -202,6 +205,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ desc = "A glass sheet made out of a plasma-silicate alloy and a rod matrix. It looks hopelessly tough and nearly fire-proof!" singular_name = "reinforced plasma glass sheet" icon_state = "sheet-prglass" + worn_icon_state = "sheet-prglass" inhand_icon_state = "sheet-prglass" mats_per_unit = list(/datum/material/alloy/plasmaglass=SHEET_MATERIAL_AMOUNT, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.5) armor_type = /datum/armor/sheet_plasmarglass @@ -239,6 +243,7 @@ GLOBAL_LIST_INIT(titaniumglass_recipes, list( desc = "A glass sheet made out of a titanium-silicate alloy." singular_name = "titanium glass sheet" icon_state = "sheet-titaniumglass" + worn_icon_state = "sheet-tglass" inhand_icon_state = "sheet-titaniumglass" mats_per_unit = list(/datum/material/alloy/titaniumglass=SHEET_MATERIAL_AMOUNT) material_type = /datum/material/alloy/titaniumglass @@ -270,6 +275,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( desc = "A glass sheet made out of a plasma-titanium-silicate alloy." singular_name = "plastitanium glass sheet" icon_state = "sheet-plastitaniumglass" + worn_icon_state = "sheet-ptglass" inhand_icon_state = "sheet-plastitaniumglass" mats_per_unit = list(/datum/material/alloy/plastitaniumglass=SHEET_MATERIAL_AMOUNT) material_type = /datum/material/alloy/plastitaniumglass @@ -301,6 +307,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( w_class = WEIGHT_CLASS_TINY force = 5 throwforce = 10 + worn_icon_state = "purified_soulstone" inhand_icon_state = "shard-glass" lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' @@ -381,7 +388,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( /obj/item/shard/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/lightreplacer)) var/obj/item/lightreplacer/lightreplacer = tool - lightreplacer.attackby(src, user) + lightreplacer.attempt_insert_shard(user, src) return ITEM_INTERACT_SUCCESS if(!istype(tool, /obj/item/stack/sheet/cloth)) @@ -421,6 +428,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( force = 6 throwforce = 11 icon_state = "plasmalarge" + worn_icon_state = "mystic_soulstone" inhand_icon_state = "shard-plasma" custom_materials = list(/datum/material/alloy/plasmaglass=SHEET_MATERIAL_AMOUNT) icon_prefix = "plasma" @@ -433,6 +441,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( desc = "A nasty looking shard of titanium infused glass." throwforce = 12 icon_state = "titaniumlarge" + worn_icon_state = "shard_white" inhand_icon_state = "shard-titanium" custom_materials = list(/datum/material/alloy/titaniumglass=SHEET_MATERIAL_AMOUNT) icon_prefix = "titanium" @@ -446,6 +455,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( force = 7 throwforce = 12 icon_state = "plastitaniumlarge" + worn_icon_state = "mystic_soulstone" inhand_icon_state = "shard-plastitanium" custom_materials = list(/datum/material/alloy/plastitaniumglass=SHEET_MATERIAL_AMOUNT) icon_prefix = "plastitanium" diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index fba6c491a4fb..db44b8f1020a 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -3,6 +3,7 @@ desc = "Something went wrong." icon_state = "sheet-hide" inhand_icon_state = null + worn_icon_state = null novariants = TRUE merge_type = /obj/item/stack/sheet/animalhide pickup_sound = 'sound/items/handling/materials/skin_pick_up.ogg' @@ -225,6 +226,7 @@ GLOBAL_LIST_INIT(carp_recipes, list ( \ desc = "The by-product of mob grinding." singular_name = "leather piece" icon_state = "sheet-leather" + worn_icon_state = null inhand_icon_state = null merge_type = /obj/item/stack/sheet/leather pickup_sound = 'sound/items/handling/materials/skin_pick_up.ogg' @@ -428,6 +430,7 @@ GLOBAL_LIST_INIT(bear_pelt_recipes, list ( \ desc = "This hide was stripped of its hair, but still needs washing and tanning." singular_name = "hairless hide piece" icon_state = "sheet-hairlesshide" + worn_icon_state = null inhand_icon_state = null merge_type = /obj/item/stack/sheet/hairlesshide pickup_sound = 'sound/items/handling/materials/skin_pick_up.ogg' @@ -443,6 +446,7 @@ GLOBAL_LIST_INIT(bear_pelt_recipes, list ( \ desc = "This hide has been cleaned but still needs to be dried." singular_name = "wet hide piece" icon_state = "sheet-wetleather" + worn_icon_state = null inhand_icon_state = null merge_type = /obj/item/stack/sheet/wethide pickup_sound = 'sound/items/handling/materials/skin_pick_up.ogg' diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index eca7b71359c3..4a896731a430 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -37,6 +37,7 @@ GLOBAL_LIST_INIT(sandstone_recipes, list ( \ desc = "This appears to be a combination of both sand and stone." singular_name = "sandstone brick" icon_state = "sheet-sandstone" + worn_icon_state = null inhand_icon_state = null throw_speed = 3 throw_range = 5 @@ -103,6 +104,7 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \ /obj/item/stack/sheet/mineral/diamond name = "diamond" icon_state = "sheet-diamond" + worn_icon_state = "purified_soulstone" inhand_icon_state = "sheet-diamond" singular_name = "diamond" construction_path_type = "diamond" @@ -135,6 +137,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \ /obj/item/stack/sheet/mineral/uranium name = "uranium" icon_state = "sheet-uranium" + worn_icon_state = "sheet-uranium" inhand_icon_state = "sheet-uranium" singular_name = "uranium sheet" construction_path_type = "uranium" @@ -171,6 +174,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \ /obj/item/stack/sheet/mineral/plasma name = "solid plasma" icon_state = "sheet-plasma" + worn_icon_state = "sheet-plasma" inhand_icon_state = "sheet-plasma" singular_name = "plasma sheet" construction_path_type = "plasma" @@ -210,6 +214,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \ /obj/item/stack/sheet/mineral/gold name = "gold" icon_state = "sheet-gold" + worn_icon_state = "sheet-gold" inhand_icon_state = "sheet-gold" singular_name = "gold bar" construction_path_type = "gold" @@ -241,6 +246,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \ /obj/item/stack/sheet/mineral/silver name = "silver" icon_state = "sheet-silver" + worn_icon_state = "sheet-silver" inhand_icon_state = "sheet-silver" singular_name = "silver bar" construction_path_type = "silver" @@ -272,7 +278,9 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \ /obj/item/stack/sheet/mineral/bananium name = "bananium" icon_state = "sheet-bananium" - inhand_icon_state = null + lefthand_file = 'icons/mob/inhands/items/food_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/food_righthand.dmi' + inhand_icon_state = "banana_peel" singular_name = "bananium sheet" construction_path_type = "bananium" mats_per_unit = list(/datum/material/bananium=SHEET_MATERIAL_AMOUNT) @@ -299,6 +307,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \ /obj/item/stack/sheet/mineral/titanium name = "titanium" icon_state = "sheet-titanium" + worn_icon_state = "sheet-titanium" inhand_icon_state = "sheet-titanium" singular_name = "titanium sheet" force = 5 @@ -357,6 +366,7 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \ /obj/item/stack/sheet/mineral/plastitanium name = "plastitanium" icon_state = "sheet-plastitanium" + worn_icon_state = "sheet-plastitanium" inhand_icon_state = "sheet-plastitanium" singular_name = "plastitanium sheet" force = 5 @@ -374,6 +384,8 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \ // DARKPACK EDIT REMOVAL START GLOBAL_LIST_INIT(plastitanium_recipes, list ( \ new/datum/stack_recipe("plastitanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20, crafting_flags = NONE, category = CAT_TILES), \ + new/datum/stack_recipe("plastitanium wall filler", /obj/item/stack/wall_filling/plastitanium/basic, 2, 1, 10, crafting_flags = NONE, category = CAT_STRUCTURE), \ + new/datum/stack_recipe("plastitanium platform", /obj/structure/platform/plastitanium, 2, time = 3 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, trait_booster = TRAIT_QUICK_BUILD, trait_modifier = 0.75, category = CAT_STRUCTURE), \ )) /* /obj/item/stack/sheet/mineral/plastitanium/get_main_recipes() @@ -388,6 +400,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \ /obj/item/stack/sheet/mineral/snow name = "snow" icon_state = "sheet-snow" + worn_icon_state = null inhand_icon_state = null mats_per_unit = list(/datum/material/snow = SHEET_MATERIAL_AMOUNT) singular_name = "snow block" @@ -428,6 +441,7 @@ GLOBAL_LIST_INIT(adamantine_recipes, list( /obj/item/stack/sheet/mineral/adamantine name = "adamantine" icon_state = "sheet-adamantine" + worn_icon_state = "sheet-adamantine" inhand_icon_state = "sheet-adamantine" singular_name = "adamantine sheet" mats_per_unit = list(/datum/material/adamantine=SHEET_MATERIAL_AMOUNT) @@ -446,6 +460,7 @@ GLOBAL_LIST_INIT(adamantine_recipes, list( desc = "Rare material found in distant lands." singular_name = "runite bar" icon_state = "sheet-runite" + worn_icon_state = "sheet-runite" inhand_icon_state = "sheet-runite" mats_per_unit = list(/datum/material/runite=SHEET_MATERIAL_AMOUNT) merge_type = /obj/item/stack/sheet/mineral/runite @@ -458,6 +473,7 @@ GLOBAL_LIST_INIT(adamantine_recipes, list( /obj/item/stack/sheet/mineral/mythril name = "mythril" icon_state = "sheet-mythril" + worn_icon_state = "sheet-mythril" inhand_icon_state = "sheet-mythril" singular_name = "mythril sheet" novariants = TRUE @@ -471,6 +487,7 @@ GLOBAL_LIST_INIT(adamantine_recipes, list( name = "alien alloy" icon = 'icons/obj/antags/abductor.dmi' icon_state = "sheet-abductor" + worn_icon_state = "sheet-abductor" inhand_icon_state = "sheet-abductor" singular_name = "alien alloy sheet" construction_path_type = "abductor" @@ -541,7 +558,9 @@ GLOBAL_LIST_INIT(metalhydrogen_recipes, list( /obj/item/stack/sheet/mineral/metal_hydrogen name = "metal hydrogen" icon_state = "sheet-metalhydrogen" - inhand_icon_state = null + worn_icon_state = "sheet-metalhydrogen" + worn_icon_state = "sheet-metalhydrogen" + inhand_icon_state = "sheet-metalhydrogen" singular_name = "metal hydrogen sheet" w_class = WEIGHT_CLASS_NORMAL resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF | BOMB_PROOF @@ -579,6 +598,7 @@ GLOBAL_LIST_INIT(zaukerite_recipes, list( name = "zaukerite" icon_state = "zaukerite" inhand_icon_state = "sheet-zaukerite" + worn_icon_state = "shard_white" singular_name = "zaukerite crystal" w_class = WEIGHT_CLASS_NORMAL gulag_value = 100 diff --git a/code/game/objects/items/stacks/sheets/runed_metal.dm b/code/game/objects/items/stacks/sheets/runed_metal.dm index e68a1356d0fb..856921587679 100644 --- a/code/game/objects/items/stacks/sheets/runed_metal.dm +++ b/code/game/objects/items/stacks/sheets/runed_metal.dm @@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/pylon, \ req_amount = 4, \ time = 4 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Pylon: Heals and regenerates the blood of nearby blood cultists and constructs, and also \ converts nearby floor tiles into engraved flooring, which allows blood cultists to scribe runes faster."), \ required_noun = "runed metal sheet", \ @@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/altar, \ req_amount = 3, \ time = 4 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Altar: Can make Eldritch Whetstones, Construct Shells, and Flasks of Unholy Water."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ @@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/archives, \ req_amount = 3, \ time = 4 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Archives: Can make Zealot's Blindfolds, Shuttle Curse Orbs, \ and Veil Walker equipment. Emits Light."), \ required_noun = "runed metal sheet", \ @@ -38,7 +38,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/forge, \ req_amount = 3, \ time = 4 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Daemon Forge: Can make Nar'Sien Hardened Armor and Eldritch Longswords. Emits Light."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ @@ -47,7 +47,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ title = "runed door", \ result_type = /obj/machinery/door/airlock/cult, \ time = 5 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Runed Door: A weak door which stuns non-blood cultists who touch it."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ @@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ title = "runed girder", \ result_type = /obj/structure/girder/cult, \ time = 5 SECONDS, \ - crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_MUST_BE_LEARNED, \ desc = span_cult_bold("Runed Girder: A weak girder that can be instantly destroyed by ritual daggers. \ Not a recommended usage of runed metal."), \ required_noun = "runed metal sheet", \ @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ desc = "Sheets of cold metal with shifting inscriptions writ upon them." singular_name = "runed metal sheet" icon_state = "sheet-runed" + worn_icon_state = "sheet-runed" inhand_icon_state = "sheet-runed" icon = 'icons/obj/stack_objects.dmi' mats_per_unit = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 0c82f267de6e..a7861457f5fd 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -123,6 +123,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ desc = "Sheets made out of iron." singular_name = "iron sheet" icon_state = "sheet-metal" + worn_icon_state = "sheet-metal" inhand_icon_state = "sheet-metal" mats_per_unit = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT) throwforce = 10 @@ -267,6 +268,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ singular_name = "plasteel sheet" desc = "This sheet is an alloy of iron and plasma." icon_state = "sheet-plasteel" + worn_icon_state = "sheet-plasteel" inhand_icon_state = "sheet-plasteel" mats_per_unit = list(/datum/material/alloy/plasteel=SHEET_MATERIAL_AMOUNT) material_type = /datum/material/alloy/plasteel @@ -364,6 +366,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \ desc = "One can only guess that this is a bunch of wood." singular_name = "wood plank" icon_state = "sheet-wood" + worn_icon_state = "sheet-wood" inhand_icon_state = "sheet-wood" icon = 'icons/obj/stack_objects.dmi' mats_per_unit = list(/datum/material/wood=SHEET_MATERIAL_AMOUNT) @@ -663,6 +666,7 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ desc = "Large sheets of card, like boxes folded flat." singular_name = "cardboard sheet" icon_state = "sheet-card" + worn_icon_state = "sheet-card" inhand_icon_state = "sheet-card" mats_per_unit = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT) resistance_flags = FLAMMABLE @@ -730,6 +734,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ desc = "On closer inspection, what appears to be wholly-unsuitable-for-building brass is actually more structurally stable bronze." singular_name = "bronze sheet" icon_state = "sheet-brass" + worn_icon_state = "sheet-brass" inhand_icon_state = "sheet-brass" icon = 'icons/obj/stack_objects.dmi' mats_per_unit = list(/datum/material/bronze = SHEET_MATERIAL_AMOUNT) @@ -841,6 +846,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list( desc = "Compress dinosaur over millions of years, then refine, split and mold, and voila! You have plastic." singular_name = "plastic sheet" icon_state = "sheet-plastic" + worn_icon_state = "sheet-plastic" inhand_icon_state = "sheet-plastic" mats_per_unit = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT) throwforce = 7 @@ -870,6 +876,7 @@ GLOBAL_LIST_INIT(paperframe_recipes, list( desc = "A thin wooden frame with paper attached." singular_name = "paper frame" icon_state = "sheet-paper" + worn_icon_state = "sheet-paper" inhand_icon_state = "sheet-paper" mats_per_unit = list(/datum/material/paper = SHEET_MATERIAL_AMOUNT) merge_type = /obj/item/stack/sheet/paperframes diff --git a/code/game/objects/items/stacks/tiles/tile_mineral.dm b/code/game/objects/items/stacks/tiles/tile_mineral.dm index 6650528d012a..b5cf7f8ff57a 100644 --- a/code/game/objects/items/stacks/tiles/tile_mineral.dm +++ b/code/game/objects/items/stacks/tiles/tile_mineral.dm @@ -13,7 +13,7 @@ if(!tool.use_tool(src, user, 0, volume=40)) return ITEM_INTERACT_BLOCKING var/sheet_type = text2path("/obj/item/stack/sheet/mineral/[mineralType]") - var/obj/item/stack/sheet/mineral/new_item = new sheet_type(user.loc) + var/obj/item/stack/sheet/mineral/new_item = new sheet_type(user.drop_location()) user.visible_message(span_notice("[user] shaped [src] into [new_item] with [tool]."), \ span_notice("You shaped [src] into [new_item] with [tool]."), \ span_hear("You hear welding.")) @@ -210,6 +210,11 @@ tile_reskin_types = list( /obj/item/stack/tile/mineral/plastitanium, /obj/item/stack/tile/mineral/plastitanium/red, + /obj/item/stack/tile/mineral/plastitanium/pod, + /obj/item/stack/tile/mineral/plastitanium/pod/light, + /obj/item/stack/tile/mineral/plastitanium/pod/dark, + /obj/item/stack/tile/mineral/plastitanium/pod/red, + /obj/item/stack/tile/mineral/plastitanium/pod/redlight, ) /obj/item/stack/tile/mineral/plastitanium/red @@ -220,6 +225,47 @@ icon_state = "tile_plastitanium_red" merge_type = /obj/item/stack/tile/mineral/plastitanium/red +/obj/item/stack/tile/mineral/plastitanium/pod + name = "pod floor tile" + singular_name = "pod floor tile" + desc = "A grooved floor tile." + icon_state = "tile_pod" + inhand_icon_state = "tile-pod" + turf_type = /turf/open/floor/mineral/plastitanium/pod + merge_type = /obj/item/stack/tile/mineral/plastitanium/pod + +/obj/item/stack/tile/mineral/plastitanium/pod/light + name = "light pod floor tile" + singular_name = "light pod floor tile" + desc = "A lightly colored grooved floor tile." + icon_state = "tile_podlight" + turf_type = /turf/open/floor/mineral/plastitanium/pod/light + merge_type = /obj/item/stack/tile/mineral/plastitanium/pod/light + +/obj/item/stack/tile/mineral/plastitanium/pod/dark + name = "dark pod floor tile" + singular_name = "dark pod floor tile" + desc = "A darkly colored grooved floor tile." + icon_state = "tile_poddark" + turf_type = /turf/open/floor/mineral/plastitanium/pod/dark + merge_type = /obj/item/stack/tile/mineral/plastitanium/pod/dark + +/obj/item/stack/tile/mineral/plastitanium/pod/red + name = "red pod floor tile" + singular_name = "dark pod floor tile" + desc = "A red colored grooved floor tile." + icon_state = "tile_pod_red" + turf_type = /turf/open/floor/mineral/plastitanium/pod/red + merge_type = /obj/item/stack/tile/mineral/plastitanium/pod/red + +/obj/item/stack/tile/mineral/plastitanium/pod/redlight + name = "light red pod floor tile" + singular_name = "light red pod floor tile" + desc = "A light red colored grooved floor tile." + icon_state = "tile_podlight_red" + turf_type = /turf/open/floor/mineral/plastitanium/pod/redlight + merge_type = /obj/item/stack/tile/mineral/plastitanium/pod/redlight + /obj/item/stack/tile/mineral/snow name = "snow tile" singular_name = "snow tile" diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index eb4ae72db327..5f804a838aac 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -10,6 +10,7 @@ lefthand_file = 'icons/mob/inhands/items/tiles_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/tiles_righthand.dmi' icon = 'icons/obj/tiles.dmi' + worn_icon_state = null w_class = WEIGHT_CLASS_NORMAL force = 1 throwforce = 1 @@ -87,7 +88,7 @@ return target_plating /obj/item/stack/tile/handle_openspace_click(turf/target, mob/user, list/modifiers) - target.attackby(src, user, list2params(modifiers)) + target.base_item_interaction(user, src, list2params(modifiers)) //Grass /obj/item/stack/tile/grass @@ -1110,37 +1111,6 @@ turf_type = /turf/open/floor/circuit/red/anim merge_type = /obj/item/stack/tile/circuit/red/anim -//Pod floor -/obj/item/stack/tile/pod - name = "pod floor tile" - singular_name = "pod floor tile" - desc = "A grooved floor tile." - icon_state = "tile_pod" - inhand_icon_state = "tile-pod" - turf_type = /turf/open/floor/pod - merge_type = /obj/item/stack/tile/pod - tile_reskin_types = list( - /obj/item/stack/tile/pod, - /obj/item/stack/tile/pod/light, - /obj/item/stack/tile/pod/dark, - ) - -/obj/item/stack/tile/pod/light - name = "light pod floor tile" - singular_name = "light pod floor tile" - desc = "A lightly colored grooved floor tile." - icon_state = "tile_podlight" - turf_type = /turf/open/floor/pod/light - merge_type = /obj/item/stack/tile/pod/light - -/obj/item/stack/tile/pod/dark - name = "dark pod floor tile" - singular_name = "dark pod floor tile" - desc = "A darkly colored grooved floor tile." - icon_state = "tile_poddark" - turf_type = /turf/open/floor/pod/dark - merge_type = /obj/item/stack/tile/pod/dark - /obj/item/stack/tile/plastic name = "plastic tile" singular_name = "plastic floor tile" diff --git a/code/game/objects/items/stacks/wall_filling.dm b/code/game/objects/items/stacks/wall_filling.dm new file mode 100644 index 000000000000..e16e71e82fb0 --- /dev/null +++ b/code/game/objects/items/stacks/wall_filling.dm @@ -0,0 +1,134 @@ +// Wall-fillings; items you can re-skin in hand and place onto griders to create different wall types. + +/obj/item/stack/wall_filling + name = "wall filling" + singular_name = "wall filler" + desc = "An empty wall filling. This should not exist." + //wall-fillings will generaly use the inhands of the sheet they are made from. + lefthand_file = 'icons/mob/inhands/items/sheets_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items/sheets_righthand.dmi' + icon = 'icons/obj/stack_objects.dmi' + w_class = WEIGHT_CLASS_NORMAL + force = 2 + throwforce = 2 + throw_speed = 2 + throw_range = 5 + max_amount = 60 + novariants = TRUE + material_flags = MATERIAL_EFFECTS + usable_for_construction = TRUE + merge_type = /obj/item/stack/wall_filling + /// the main thing it exists for, list of fillings to reskin into. + var/list/wall_reskin_types + /// what it should be broken down back into when using a welder. Provided as a path. + var/made_from + +/obj/item/stack/wall_filling/examine(mob/user) + . = ..() + . += span_notice("Use while in your hand to change what type of [src] you want.") + if(throwforce && !is_cyborg) //do not want to divide by zero or show the message to borgs who can't throw + var/damage_value + switch(ceil(MAX_LIVING_HEALTH / throwforce)) //throws to crit a human + if(1 to 3) + damage_value = "superb" + if(4 to 6) + damage_value = "great" + if(7 to 9) + damage_value = "good" + if(10 to 12) + damage_value = "fairly decent" + if(13 to 15) + damage_value = "mediocre" + if(!damage_value) + return + . += span_notice("Those could work as a [damage_value] throwing weapon.") + +/obj/item/stack/wall_filling/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (tool.tool_behaviour != TOOL_WELDER) + return ..() + + if(!made_from) + to_chat(user, span_warning("You can not reform this!")) + stack_trace("A wall filling of type [type] doesn't have its made_from set.") + return + if(!tool.use_tool(src, user, 0, volume=40)) + return + var/obj/item/new_item = new made_from(user.drop_location(), 2) + user.visible_message(span_notice("[user] shaped [src] into [new_item] with [tool]."), \ + span_notice("You shaped [src] into [new_item] with [tool]."), \ + span_hear("You hear welding.")) + var/holding = user.is_holding(src) + use(1) + if(holding && QDELETED(src)) + user.put_in_hands(new_item) + return ITEM_INTERACT_SUCCESS + +GLOBAL_LIST_EMPTY(wall_reskin_lists) + +/** + * Yup, literally copied over from tile reskinning, except we don't need a dir as walls don't rotate, so, that much simpler. + */ +/obj/item/stack/wall_filling/proc/wall_reskin_list(list/values) + var/string_id = values.Join("-") + . = GLOB.wall_reskin_lists[string_id] + if(.) + return + for(var/path in values) + var/obj/item/stack/wall_filling/type_cast_path = path + values[path] = image(icon = initial(type_cast_path.icon), icon_state = initial(type_cast_path.icon_state)) + return GLOB.wall_reskin_lists[string_id] = values + +/obj/item/stack/wall_filling/attack_self(mob/user) + var/obj/item/stack/wall_filling/choice = show_radial_menu(user, src, wall_reskin_types, radius = 48, require_near = TRUE) + if(!choice || choice == type) + return + choice = new choice(user.drop_location(), amount) + moveToNullspace() + if(!QDELETED(choice)) + user.put_in_active_hand(choice) + qdel(src) + +/obj/item/stack/wall_filling/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) + . = ..() + if(wall_reskin_types) + wall_reskin_types = wall_reskin_list(wall_reskin_types) + +/obj/item/stack/wall_filling/plastitanium + name = "plastitanium wall filling" + singular_name = "plastitanium wall filler" + desc = "A filling for a standard plastitanium wall." + icon_state = "plastitanium-wall-fill" + inhand_icon_state = "sheet-plastitanium" + mats_per_unit = list(/datum/material/alloy/plastitanium=SHEET_MATERIAL_AMOUNT*2) + merge_type = /obj/item/stack/wall_filling/plastitanium + made_from = /obj/item/stack/sheet/mineral/plastitanium + wall_reskin_types = list( + /obj/item/stack/wall_filling/plastitanium/basic, + /obj/item/stack/wall_filling/plastitanium/survivalpod, + /obj/item/stack/wall_filling/plastitanium/pod, + /obj/item/stack/wall_filling/plastitanium/redpod, + ) + +/obj/item/stack/wall_filling/plastitanium/basic + merge_type = /obj/item/stack/wall_filling/plastitanium/basic + +/obj/item/stack/wall_filling/plastitanium/survivalpod + name = "survival pod wall filling" + singular_name = "survival pod wall filler" + icon_state = "survivalpod-wall-fill" + desc = "A filling for a survival pod wall, often usen for mining in hostile enviroments." + merge_type = /obj/item/stack/wall_filling/plastitanium/survivalpod + +/obj/item/stack/wall_filling/plastitanium/pod + name = "dark shuttle wall filling" + singular_name = "dark shuttle wall filler" + icon_state = "pod-wall-fill" + desc = "A filling for a dark shuttle wall. Dark mode on." + merge_type = /obj/item/stack/wall_filling/plastitanium/pod + +/obj/item/stack/wall_filling/plastitanium/redpod + name = "red dark shuttle wall filling" + singular_name = "red dark shuttle wall filler" + icon_state = "redpod-wall-fill" + desc = "A filling for a red dark shuttle wall. Quite the menacing vibe." + merge_type = /obj/item/stack/wall_filling/plastitanium/redpod diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index 7b9467bc1e3b..3687f83ed912 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -164,7 +164,7 @@ INVOKE_ASYNC(src, PROC_REF(handle_move), user) /obj/item/storage/bag/ore/proc/handle_move(mob/living/user) - if(user.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return var/turf/tile = get_turf(user) var/obj/structure/ore_box/box = null diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index cd4baf7f1c99..f4d7f2491abb 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -115,11 +115,11 @@ new /obj/item/implanter/storage(src) // 8 tc if(KIT_HACKER) //L-L--LOOK AT YOU, HACKER - new /obj/item/ai_module/syndicate(src) // 4 tc + new /obj/item/ai_module/law/syndicate(src) // 4 tc new /obj/item/card/emag(src) // 4 tc new /obj/item/card/emag/doorjack(src) // 3 tc new /obj/item/encryptionkey/binary(src) // 5 tc - new /obj/item/ai_module/toy_ai(src) // ~6 tc + new /obj/item/ai_module/law/toy_ai(src) // ~6 tc new /obj/item/multitool/ai_detect(src) // 1 tc new /obj/item/storage/toolbox/syndicate(src) // 1 tc new /obj/item/disk/computer/syndicate/camera_app(src) // 1 tc diff --git a/code/game/objects/items/syndie_spraycan.dm b/code/game/objects/items/syndie_spraycan.dm index 1a7ccaf50f9c..e7b09c1ed4b9 100644 --- a/code/game/objects/items/syndie_spraycan.dm +++ b/code/game/objects/items/syndie_spraycan.dm @@ -85,7 +85,7 @@ if(HAS_TRAIT(user, TRAIT_TAGGER)) wait_time *= 0.5 - if(!do_after(user, wait_time, target, hidden = TRUE, extra_checks = CALLBACK(src, PROC_REF(adjacency_check), user, target))) + if(!do_after(user, wait_time, target, cog_icon = null, extra_checks = CALLBACK(src, PROC_REF(adjacency_check), user, target))) user.balloon_alert(user, "interrupted!") drawing_rune = FALSE return FALSE diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index 931e7c6a66ae..28809f7c1b10 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -94,6 +94,8 @@ var/list/active_portal_pairs = list() ///Maximum concurrent active portal pairs allowed var/max_portal_pairs = 3 + ///Whether this can be used to teleport to and from away levels + var/away_restricted = TRUE /** * Represents the last place we teleported to, for making quick portals. @@ -206,7 +208,7 @@ if (teleport_location == PORTAL_LOCATION_DANGEROUS) var/list/dangerous_turfs = list() - for(var/turf/dangerous_turf in urange(10, orange=1)) + for(var/turf/dangerous_turf in urange(10, get_turf(src), TRUE)) if(dangerous_turf.x > world.maxx - PORTAL_DANGEROUS_EDGE_LIMIT || dangerous_turf.x < PORTAL_DANGEROUS_EDGE_LIMIT) continue //putting them at the edge is dumb if(dangerous_turf.y > world.maxy - PORTAL_DANGEROUS_EDGE_LIMIT || dangerous_turf.y < PORTAL_DANGEROUS_EDGE_LIMIT) @@ -262,7 +264,7 @@ ///Is, for some reason, separate from the teleport target's check in try_create_portal_to() /obj/item/hand_tele/proc/can_teleport_notifies(mob/user) var/turf/current_location = get_turf(user) - if (!current_location || !check_teleport_valid(src, current_location) || is_away_level(current_location.z) || !isturf(user.loc)) + if (!current_location || !check_teleport_valid(src, current_location) || !isturf(user.loc) || (away_restricted && is_away_level(current_location.z))) to_chat(user, span_notice("[src] is malfunctioning.")) return FALSE diff --git a/code/game/objects/items/tools/control_wand.dm b/code/game/objects/items/tools/control_wand.dm index 92b62d7caa1b..2c76191a7da9 100644 --- a/code/game/objects/items/tools/control_wand.dm +++ b/code/game/objects/items/tools/control_wand.dm @@ -7,6 +7,7 @@ /obj/item/door_remote icon_state = "remote" base_icon_state = "remote" + worn_icon = null inhand_icon_state = "electronic" lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' diff --git a/code/game/objects/items/tools/engineering/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm index 0e70b9424bb7..1891bff0e2cc 100644 --- a/code/game/objects/items/tools/engineering/weldingtool.dm +++ b/code/game/objects/items/tools/engineering/weldingtool.dm @@ -59,7 +59,7 @@ /obj/item/weldingtool/Initialize(mapload) . = ..() if(IS_OVERLAY_LIGHT_SYSTEM(light_system)) - middleman = new(src, "flashlight") + middleman = new(src, "weldingtool") RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated)) middleman.being_overriding_light() AddElement(/datum/element/update_icon_updates_onmob) diff --git a/code/game/objects/items/tools/inducer.dm b/code/game/objects/items/tools/inducer.dm index 03e5ce7da314..694b3806a458 100644 --- a/code/game/objects/items/tools/inducer.dm +++ b/code/game/objects/items/tools/inducer.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/tools.dmi' icon_state = "inducer-engi" inhand_icon_state = "inducer-engi" + inside_belt_icon_state = "inducer" lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' force = 7 @@ -236,6 +237,7 @@ /obj/item/inducer/sci icon_state = "inducer-sci" inhand_icon_state = "inducer-sci" + inside_belt_icon_state = "inducer-sci" desc = "A tool for inductively charging internal power cells. This one has a science color scheme, and is less potent than its engineering counterpart." powerdevice = null opened = TRUE @@ -243,6 +245,7 @@ /obj/item/inducer/syndicate icon_state = "inducer-syndi" inhand_icon_state = "inducer-syndi" + inside_belt_icon_state = "inducer-syndi" desc = "A tool for inductively charging internal power cells. This one has a suspicious colour scheme, and seems to be rigged to transfer charge at a much faster rate." power_transfer_multiplier = 2 // 2x the base speed powerdevice = /obj/item/stock_parts/power_store/battery/super diff --git a/code/game/objects/items/tools/janitorial/janitor_key.dm b/code/game/objects/items/tools/janitorial/janitor_key.dm index 294054bc4ce0..f22136c86161 100644 --- a/code/game/objects/items/tools/janitorial/janitor_key.dm +++ b/code/game/objects/items/tools/janitorial/janitor_key.dm @@ -19,6 +19,9 @@ ///The departmental access given to the key. var/list/department_access + /// The amount of time it takes to find the necessary key when attempting to open an airlock + var/key_speed = 3 SECONDS + /obj/item/access_key/Initialize(mapload) . = ..() RegisterSignal(SSdcs, COMSIG_ON_DEPARTMENT_ACCESS, PROC_REF(department_access_given)) @@ -50,7 +53,7 @@ return FALSE user.balloon_alert_to_viewers("fumbles with keys...", "finding key...") user.playsound_local(src, 'sound/items/rattling_keys.ogg', 25, TRUE) - if(!do_after(user, 3 SECONDS, airlock)) + if(!do_after(user, key_speed, airlock)) return FALSE if(!department_access || !airlock.check_access_list(SSid_access.accesses_by_region[department_access])) airlock.balloon_alert(user, "no access!") diff --git a/code/game/objects/items/tools/medical/defib.dm b/code/game/objects/items/tools/medical/defib.dm index b257790daa4f..052894e94b17 100644 --- a/code/game/objects/items/tools/medical/defib.dm +++ b/code/game/objects/items/tools/medical/defib.dm @@ -545,7 +545,7 @@ H.emote("scream") shock_pulling(45, H) if(H.can_heartattack() && !H.undergoing_cardiac_arrest()) - if(!H.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(H)) H.visible_message(span_warning("[H] thrashes wildly, clutching at [H.p_their()] chest!"), span_userdanger("You feel a horrible agony in your chest!")) H.set_heartattack(TRUE) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 82cc691fc84b..b59cdb5cf0b7 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1051,7 +1051,7 @@ /obj/item/toy/minimeteor/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) playsound(src, 'sound/effects/meteorimpact.ogg', 40, TRUE) for(var/mob/M in urange(10, src)) - if(!M.stat && !isAI(M)) + if(!IS_UNCONSCIOUS_OR_CRIT(M) && !isAI(M)) shake_camera(M, 3, 1) if (obj_flags & EMAGGED) explosion(src, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 1) @@ -1074,7 +1074,7 @@ user.visible_message(span_warning("[user] presses the big red button."), span_notice("You press the button, it plays a loud noise!"), span_hear("The button clicks loudly.")) playsound(src, 'sound/effects/explosion/explosionfar.ogg', 50, FALSE) for(var/mob/M in urange(10, src)) // Checks range - if(!M.stat && !isAI(M)) // Checks to make sure whoever's getting shaken is alive/not the AI + if(!IS_UNCONSCIOUS_OR_CRIT(M) && !isAI(M)) // Checks to make sure whoever's getting shaken is alive/not the AI // Short delay to match up with the explosion sound // Shakes player camera 2 squares for 1 second. addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(shake_camera), M, 2, 1), 0.8 SECONDS) diff --git a/code/game/objects/items/v8_engine.dm b/code/game/objects/items/v8_engine.dm index 145fcf41d886..5849908fc8ee 100644 --- a/code/game/objects/items/v8_engine.dm +++ b/code/game/objects/items/v8_engine.dm @@ -41,7 +41,7 @@ /obj/item/v8_engine/proc/start_learning_recipe(mob/user) if(!user.mind) return - if(user.mind.has_crafting_recipe(user = user, potential_recipe = /datum/crafting_recipe/house_edge)) + if(user.mind.has_crafting_recipe(/datum/crafting_recipe/house_edge)) return to_chat(user, span_notice("You peer at the label on the side, reading about some unique modifications that could be made to the engine...")) if(do_after(user, 15 SECONDS, src)) diff --git a/code/game/objects/items/weaponry/melee/chainsaw.dm b/code/game/objects/items/weaponry/melee/chainsaw.dm index 55e30d63d157..770fbdd721a8 100644 --- a/code/game/objects/items/weaponry/melee/chainsaw.dm +++ b/code/game/objects/items/weaponry/melee/chainsaw.dm @@ -98,7 +98,7 @@ user.visible_message(span_suicide("[user] begins to tear [user.p_their()] head off with [src]! It looks like [user.p_theyre()] trying to commit suicide!")) var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD) if(!myhead) - visible_message(span_suicide("[user] realises that [user.p_they()] cannot cut off [user.p_their()] head because [user.p_they()] don't have one!")) + visible_message(span_suicide("[user] realises that [user.p_they()] cannot cut off [user.p_their()] head because [user.p_they()] [user.p_do()]n't have one!")) return SHAME playsound(src, 'sound/items/weapons/chainsawhit.ogg', 100, TRUE) diff --git a/code/game/objects/items/weaponry/melee/energy.dm b/code/game/objects/items/weaponry/melee/energy.dm index 5aa8c7eb8ce5..8733e410ed1b 100644 --- a/code/game/objects/items/weaponry/melee/energy.dm +++ b/code/game/objects/items/weaponry/melee/energy.dm @@ -495,7 +495,7 @@ if(charge <= max_charge) user.balloon_alert(user, "attempting recharge...") - if(!do_after(user, charge_time, target = src, extra_checks = CALLBACK(src, PROC_REF(do_jiggle), user), interaction_key = DOAFTER_SOURCE_CHARGING_ESWORD, iconstate = "beat_the_heat")) + if(!do_after(user, charge_time, target = src, extra_checks = CALLBACK(src, PROC_REF(do_jiggle), user), interaction_key = DOAFTER_SOURCE_CHARGING_ESWORD, cog_iconstate = "beat_the_heat")) user.balloon_alert(user, "interrupted!") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN charge = max_charge diff --git a/code/game/objects/items/weaponry/melee/his_grace.dm b/code/game/objects/items/weaponry/melee/his_grace.dm index e9e49d0c6f24..803d79394bad 100644 --- a/code/game/objects/items/weaponry/melee/his_grace.dm +++ b/code/game/objects/items/weaponry/melee/his_grace.dm @@ -97,7 +97,7 @@ INVOKE_ASYNC(src, PROC_REF(awaken), user) /obj/item/his_grace/attack(mob/living/M, mob/user) - if(awakened && M.stat) + if(awakened && IS_UNCONSCIOUS_OR_CRIT(M)) if(gender == FEMALE) var/dx = M.x - user.x var/dy = M.y - user.y @@ -176,7 +176,7 @@ var/mob/living/L = pick(targets) step_to(src, L) if(Adjacent(L)) - if(!L.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(L)) L.visible_message(span_warning("[src] lunges at [L]!"), "[src] lunges at you!") do_attack_animation(L, null, src) playsound(L, 'sound/items/weapons/smash.ogg', 50, TRUE) diff --git a/code/game/objects/items/weaponry/melee/soulscythe.dm b/code/game/objects/items/weaponry/melee/soulscythe.dm index c7dabc9d44df..e45bfec30b0a 100644 --- a/code/game/objects/items/weaponry/melee/soulscythe.dm +++ b/code/game/objects/items/weaponry/melee/soulscythe.dm @@ -67,7 +67,7 @@ reset_spin() //resume spinnage /obj/item/soulscythe/attack_self(mob/user, modifiers) - if(using || soul.ckey || soul.stat) + if(using || soul.ckey || IS_UNCONSCIOUS_OR_CRIT(soul)) return if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) balloon_alert(user, "you can't awaken the scythe!") @@ -266,7 +266,7 @@ add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE, TRAIT_LAVA_IMMUNE), INNATE_TRAIT) /mob/living/basic/soulscythe/Life(seconds_per_tick) - if(stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(src)) adjust_blood_volume(round(1 * seconds_per_tick), maximum = MAX_BLOOD_LEVEL) /// Special projectile for the soulscythe. diff --git a/code/game/objects/items/weaponry/melee/spear.dm b/code/game/objects/items/weaponry/melee/spear.dm index a5b26d6ddecb..4f50e4c7ad77 100644 --- a/code/game/objects/items/weaponry/melee/spear.dm +++ b/code/game/objects/items/weaponry/melee/spear.dm @@ -397,7 +397,7 @@ var/mob/living/stabbed = target if(istype(stabbed, /mob/living/basic/illusion)) return - if(stabbed.stat == CONSCIOUS && prob(50)) + if(!IS_UNCONSCIOUS_OR_CRIT(stabbed) && prob(50)) var/mob/living/basic/illusion/fake_clone = new(user.loc) fake_clone.full_setup(user, target_mob = stabbed, life = 10 SECONDS, hp = user.health / 2.5, damage = 12, replicate = 30) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 6cc35616c2ac..a915bec90dfe 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -277,8 +277,9 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) /// If we can unwrench this object; returns SUCCESSFUL_UNFASTEN and FAILED_UNFASTEN, which are both TRUE, or CANT_UNFASTEN, which isn't. /obj/proc/can_be_unfasten_wrench(mob/user, silent) - if(!(isfloorturf(loc) || isindestructiblefloor(loc)) && !anchored) - to_chat(user, span_warning("[src] needs to be on the floor to be secured!")) + if(!is_anchorable_floor(loc) && !anchored) + if(!silent) + to_chat(user, span_warning("[src] needs to be on the floor to be secured!")) return FAILED_UNFASTEN return SUCCESSFUL_UNFASTEN diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 84ca7cc046cd..4a2090302acf 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -60,6 +60,7 @@ /obj/structure/rust_heretic_act() take_damage(500, BRUTE, "melee", 1) + return TRUE /obj/structure/zap_act(power, zap_flags) if(zap_flags & ZAP_OBJ_DAMAGE) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 09a765dee11c..a9cd315fde20 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -458,7 +458,7 @@ return var/mob/living/carbon/C = AM - if(C.stat == CONSCIOUS && C.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo)) + if(!IS_UNCONSCIOUS_OR_CRIT(C) && C.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo)) return Burst(kill=FALSE) diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm index 2c0b620dce92..31154d97a1f6 100644 --- a/code/game/objects/structures/beds_chairs/alien_nest.dm +++ b/code/game/objects/structures/beds_chairs/alien_nest.dm @@ -74,7 +74,7 @@ span_notice("You struggle to break free from the gelatinous resin... (Stay still for about a minute and a half.)"), span_hear("You hear squelching...")) - if(!do_after(captive, 100 SECONDS, target = src, hidden = TRUE)) + if(!do_after(captive, 100 SECONDS, target = src, cog_icon = null)) if(captive.buckled) to_chat(captive, span_warning("You fail to unbuckle yourself!")) return diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index aa43e3c5bae6..8029c6af7b29 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -657,7 +657,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) /obj/structure/chair/plastic/proc/snap_check(mob/living/carbon/Mob) if (Mob.nutrition >= NUTRITION_LEVEL_FAT) to_chat(Mob, span_warning("The chair begins to pop and crack, you're too heavy!")) - if(do_after(Mob, 6 SECONDS, progress = FALSE)) + if(do_after(Mob, 6 SECONDS, show_progress = FALSE)) Mob.visible_message(span_notice("The plastic chair snaps under [Mob]'s weight!")) new /obj/effect/decal/cleanable/plastic(loc) qdel(src) diff --git a/code/game/objects/structures/cannons/cannon.dm b/code/game/objects/structures/cannons/cannon.dm index a276bb022f78..7b20dc014460 100644 --- a/code/game/objects/structures/cannons/cannon.dm +++ b/code/game/objects/structures/cannons/cannon.dm @@ -31,7 +31,7 @@ /obj/structure/cannon/proc/fire() for(var/mob/shaken_mob in urange(10, src)) - if(shaken_mob.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(shaken_mob)) shake_camera(shaken_mob, 3, 1) playsound(src, fire_sound, 50, TRUE) diff --git a/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm b/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm index 09f9c06f707f..76805f786f62 100644 --- a/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm +++ b/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm @@ -162,7 +162,7 @@ /// Perform the contents of the loop, return the amount of time until the next shot /obj/structure/mounted_gun/proc/fire_loop(mob/living/user) for(var/mob/shaken_mob in urange(3, src)) - if(shaken_mob.stat == CONSCIOUS && firing_shakes_camera) //is the mob awake to feel the shaking? + if(!IS_UNCONSCIOUS_OR_CRIT(shaken_mob) && firing_shakes_camera) //is the mob awake to feel the shaking? shake_camera(shaken_mob, 3, 1) playsound(src, shots_in_gun > 0 ? fire_sound : last_fire_sound, vol = 50, vary = FALSE, falloff_exponent = 5) fire_gun(user) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index c02dd25fbe7f..aa2d98d728a5 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -43,7 +43,6 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) var/opened = FALSE var/welded = FALSE var/locked = FALSE - var/large = TRUE var/wall_mounted = 0 //never solid (You can always pass over it) var/breakout_time = 1200 var/message_cooldown @@ -928,7 +927,12 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) if(user in contents) return ITEM_INTERACT_BLOCKING + if(opened && istype(tool, cutting_tool)) // not all of them take welders + if(resistance_flags & INDESTRUCTIBLE) + to_chat(user, span_warning("You can't cut [src] apart!")) + return ITEM_INTERACT_BLOCKING + if(!tool.tool_start_check(user, amount=1)) return ITEM_INTERACT_BLOCKING @@ -1020,7 +1024,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) O.forceMove(T) /obj/structure/closet/relaymove(mob/living/user, direction) - if(user.stat || !isturf(loc)) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !isturf(loc)) return if(locked) if(message_cooldown <= world.time) @@ -1105,7 +1109,7 @@ GAME_VERB_SRC(/obj/structure/closet, verb_toggleopen, view(1), "Toggle Open", nu addtimer(CALLBACK(src, PROC_REF(check_if_shake)), 1 SECONDS) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || (loc_required && (user.loc != src)) || opened || (!locked && !welded) ) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || (loc_required && (user.loc != src)) || opened || (!locked && !welded) ) return //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting user.visible_message(span_danger("[user] successfully broke out of [src]!"), diff --git a/code/game/objects/structures/crates_lockers/closets/bodybag.dm b/code/game/objects/structures/crates_lockers/closets/bodybag.dm index 3da2b67101e3..4c7d83059311 100644 --- a/code/game/objects/structures/crates_lockers/closets/bodybag.dm +++ b/code/game/objects/structures/crates_lockers/closets/bodybag.dm @@ -229,7 +229,7 @@ for(var/mob/living/inside in src) return tool.interact_with_atom(inside, user, modifiers) - return NONE + return ..() /obj/structure/closet/body_bag/before_open(mob/living/user, force) if(pinned) @@ -360,7 +360,7 @@ span_notice("You start wriggling, attempting to loosen [src]'s buckles... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear straining cloth from [src].")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || opened || !cinched ) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || opened || !cinched ) return //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting user.visible_message(span_danger("[user] successfully broke out of [src]!"), @@ -573,8 +573,9 @@ if(SPT_PROB(2 * (seconds_freezing / 60), seconds_per_tick)) freezing.Unconscious(1 SECONDS) - // Bout two minutes of time - take_damage(max_integrity * 0.004 * seconds_per_tick, sound_effect = FALSE) + if(loc?.return_air()?.return_temperature() > T0C) + // Bout two minutes of time + take_damage(max_integrity * 0.004 * seconds_per_tick, sound_effect = FALSE) /obj/structure/closet/body_bag/environmental/stasis/after_open(mob/living/user, force = FALSE) . = ..() @@ -639,7 +640,7 @@ user.show_message("You fail to break out of [src]!", MSG_VISUAL) /obj/structure/closet/body_bag/environmental/stasis/proc/breakout_checks(mob/living/user) - if(QDELETED(user) || user.stat != CONSCIOUS || user.loc != src || opened) + if(QDELETED(user) || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || opened) return FALSE return TRUE diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index 11bae7645ddb..15768af7dd30 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -89,7 +89,7 @@ COOLDOWN_START(src, alert_cooldown, time_between_alerts) for(var/mob/living/alerted_mob as anything in alerted) - if(alerted_mob.stat != CONSCIOUS || alerted_mob.is_blind()) + if(IS_UNCONSCIOUS_OR_CRIT(alerted_mob) || alerted_mob.is_blind()) continue if(!INCAPACITATED_IGNORING(alerted_mob, INCAPABLE_RESTRAINTS)) alerted_mob.face_atom(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm index 66fa776f403a..c15898535969 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm @@ -14,7 +14,6 @@ new /obj/item/universal_scanner(src) new /obj/item/door_remote/quartermaster(src) new /obj/item/circuitboard/machine/techfab/department/cargo(src) - new /obj/item/storage/photo_album/qm(src) new /obj/item/circuitboard/machine/ore_silo(src) new /obj/item/storage/bag/garment/quartermaster(src) @@ -23,3 +22,5 @@ // Traitor steal objective new /obj/item/card/id/departmental_budget/car(src) + + new /obj/item/storage/photo_album/qm(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 0e3be1e463a7..93bd4580876a 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -16,7 +16,6 @@ new /obj/item/storage/lockbox/medal/engineering(src) new /obj/item/circuitboard/machine/techfab/department/engineering(src) new /obj/item/extinguisher/advanced(src) - new /obj/item/storage/photo_album/ce(src) new /obj/item/storage/box/skillchips/engineering(src) new /obj/item/storage/box/stickers/chief_engineer(src) @@ -27,6 +26,8 @@ new /obj/item/blueprints(src) new /obj/item/pipe_dispenser(src) + new /obj/item/storage/photo_album/ce(src) + /obj/structure/closet/secure_closet/engineering_electrical name = "electrical supplies locker" req_access = list(ACCESS_ENGINE_EQUIP) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index 0b4e5600dbb7..82aee998309c 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -89,7 +89,6 @@ new /obj/item/clothing/neck/petcollar(src) new /obj/item/pet_carrier(src) new /obj/item/circuitboard/machine/techfab/department/medical(src) - new /obj/item/storage/photo_album/cmo(src) new /obj/item/storage/lockbox/medal/med(src) /obj/structure/closet/secure_closet/chief_medical/populate_contents_immediate() @@ -99,6 +98,8 @@ new /obj/item/reagent_containers/hypospray/cmo(src) new /obj/item/defibrillator/compact/loaded/cmo(src) + new /obj/item/storage/photo_album/cmo(src) + /obj/structure/closet/secure_closet/animal name = "animal control locker" icon_door = "chemical" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 7df190f2f6c6..31645000da8f 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -16,7 +16,6 @@ new /obj/item/assembly/flash/handheld(src) new /obj/item/door_remote/research_director(src) new /obj/item/circuitboard/machine/techfab/department/science(src) - new /obj/item/storage/photo_album/rd(src) new /obj/item/storage/box/skillchips/science(src) /obj/structure/closet/secure_closet/research_director/populate_contents_immediate() @@ -26,6 +25,8 @@ new /obj/item/clothing/suit/armor/reactive/teleport(src) new /obj/item/laser_pointer(src) + new /obj/item/storage/photo_album/rd(src) + /obj/structure/closet/secure_closet/cytology name = "cytology equipment locker" icon_state = "science" 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 f1fd3166e434..1edc352bd72b 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -12,13 +12,14 @@ new /obj/item/radio/headset/heads/captain/alt(src) new /obj/item/radio/headset/heads/captain(src) new /obj/item/door_remote/captain(src) - new /obj/item/storage/photo_album/captain(src) new /obj/item/megaphone/command(src) /obj/structure/closet/secure_closet/captains/populate_contents_immediate() new /obj/item/gun/energy/e_gun(src) new /obj/item/storage/belt/sheath/sabre(src) + new /obj/item/storage/photo_album/captain(src) + /obj/structure/closet/secure_closet/hop name = "head of personnel's locker" icon_state = "hop" @@ -39,13 +40,14 @@ new /obj/item/pet_carrier(src) new /obj/item/door_remote/head_of_personnel(src) new /obj/item/circuitboard/machine/techfab/department/service(src) - new /obj/item/storage/photo_album/hop(src) new /obj/item/storage/lockbox/medal/hop(src) new /obj/item/storage/box/stamps(src) /obj/structure/closet/secure_closet/hop/populate_contents_immediate() new /obj/item/gun/energy/e_gun(src) + new /obj/item/storage/photo_album/hop(src) + /obj/structure/closet/secure_closet/hos name = "head of security's locker" icon_state = "hos" @@ -66,7 +68,6 @@ new /obj/item/shield/riot/tele(src) new /obj/item/storage/belt/security/full(src) new /obj/item/circuitboard/machine/techfab/department/security(src) - new /obj/item/storage/photo_album/hos(src) /obj/structure/closet/secure_closet/hos/populate_contents_immediate() . = ..() @@ -75,6 +76,8 @@ new /obj/item/gun/energy/e_gun/hos(src) new /obj/item/pinpointer/nuke(src) + new /obj/item/storage/photo_album/hos(src) + /obj/structure/closet/secure_closet/warden name = "warden's locker" icon_state = "warden" diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index 6b66b2e5488a..c875a0517687 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -386,6 +386,47 @@ smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS +/obj/structure/falsewall/plastitanium/wall_fill + mineral = /obj/item/stack/wall_filling/plastitanium/basic + mineral_amount = 1 + walltype = /turf/closed/wall/mineral/plastitanium/wall_fill + +/obj/structure/falsewall/plastitanium/darkpod + name = "dark pod wall" + desc = "An easily-compressible wall used for temporary shelter." + fake_icon = 'icons/turf/walls/dark_pod.dmi' + icon_state = "dark_pod_walls-open" + base_icon_state = "dark_pod_walls" + mineral = /obj/item/stack/wall_filling/plastitanium/pod + mineral_amount = 1 + walltype = /turf/closed/wall/mineral/plastitanium/darkpod + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + +/obj/structure/falsewall/plastitanium/redpod + name = "red pod wall" + desc = "An easily-compressible wall used for temporary shelter." + fake_icon = 'icons/turf/walls/red_pod.dmi' + icon_state = "red_pod_walls-open" + base_icon_state = "red_pod_walls" + mineral = /obj/item/stack/wall_filling/plastitanium/redpod + mineral_amount = 1 + walltype = /turf/closed/wall/mineral/plastitanium/redpod + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + +/obj/structure/falsewall/plastitanium/survival + name = "pod wall" + desc = "An easily-compressible wall used for temporary shelter." + fake_icon = 'icons/turf/walls/survival_pod_walls.dmi' + icon_state = "survival_pod_walls-open" + base_icon_state = "survival_pod_walls" + mineral = /obj/item/stack/wall_filling/plastitanium/survivalpod + mineral_amount = 1 + walltype = /turf/closed/wall/mineral/plastitanium/survival + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + /obj/structure/falsewall/material name = "wall" desc = "A huge chunk of material used to separate rooms." diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 79571231738d..d3b7c833bdf1 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -290,6 +290,9 @@ *********/ //Can *you* speak their language? +/// The amount of extra range (in tiles) given to trees upon falling. +#define TREE_FALL_EXTRARANGE 5 + /obj/structure/flora/tree name = "tree" desc = "A large tree." @@ -324,13 +327,15 @@ /obj/structure/flora/tree/harvest(mob/living/user, product_amount_multiplier) . = ..() var/turf/my_turf = get_turf(src) - playsound(my_turf, 'sound/effects/meteorimpact.ogg', 100 , FALSE, FALSE) + if(has_gravity(my_turf)) // If a tree falls in the forest, it makes a sound unless it doesn't have gravity. + playsound(my_turf, 'sound/effects/meteorimpact.ogg', 100 , FALSE, extrarange = TREE_FALL_EXTRARANGE) var/obj/structure/flora/tree/stump/new_stump = new(my_turf) new_stump.name = "[name] stump" /obj/structure/flora/tree/uproot(mob/living/user) ..() - playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100 , FALSE, FALSE) + if(has_gravity(get_turf(src))) + playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100 , FALSE, extrarange = TREE_FALL_EXTRARANGE) /obj/structure/flora/tree/stump name = "stump" @@ -544,6 +549,8 @@ icon_state = "palm[rand(1,2)]" update_appearance() +#undef TREE_FALL_EXTRARANGE + /********* * Grass * *********/ diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 84caad3e7802..a83946392d6e 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -232,7 +232,6 @@ return ITEM_INTERACT_SUCCESS /obj/structure/grille/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - user.changeNext_move(CLICK_CD_MELEE) if(istype(tool, /obj/item/stack/rods) && broken) if(!do_after(user, 1 SECONDS, target = src)) return ITEM_INTERACT_BLOCKING @@ -265,7 +264,7 @@ return ITEM_INTERACT_BLOCKING to_chat(user, span_notice("You start placing the window...")) - if(!do_after(user,20, target = src)) + if(!do_after(user, 2 SECONDS, target = src)) return ITEM_INTERACT_BLOCKING if(!src.loc || !anchored) //Grille broken or unanchored while waiting diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm index 251f74431165..4daaa428db4d 100644 --- a/code/game/objects/structures/holosign.dm +++ b/code/game/objects/structures/holosign.dm @@ -106,7 +106,7 @@ if(iscarbon(mover)) var/mob/living/carbon/moving_carbon = mover - if(moving_carbon.stat) // Lets not prevent dragging unconscious/dead people. + if(IS_UNCONSCIOUS_OR_CRIT(moving_carbon)) // Lets not prevent dragging unconscious/dead people. return TRUE if(allow_walk && moving_carbon.move_intent == MOVE_INTENT_WALK) return TRUE @@ -166,7 +166,7 @@ . = ..() if(iscarbon(mover)) var/mob/living/carbon/C = mover - if(C.stat) // Lets not prevent dragging unconscious/dead people. + if(IS_UNCONSCIOUS_OR_CRIT(C)) // Lets not prevent dragging unconscious/dead people. return TRUE if(allow_walk && C.move_intent != MOVE_INTENT_WALK) return FALSE diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 6cbe466f4744..852dfba1db5a 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -154,7 +154,7 @@ span_notice("You struggle to break free from [src], exacerbating your wounds! (Stay still for two minutes.)"),\ span_hear("You hear a wet squishing noise..")) buckled_mob.adjust_brute_loss(30) - if(!do_after(buckled_mob, 2 MINUTES, target = src, hidden = TRUE)) + if(!do_after(buckled_mob, 2 MINUTES, target = src, cog_icon = null)) if(buckled_mob?.buckled) to_chat(buckled_mob, span_warning("You fail to free yourself!")) return diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index f70db6870f6b..ff1f18cb5a1f 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -354,6 +354,7 @@ return for(var/mob/living/miner in range(7, src)) //Give the miners who are near the vent points and xp. + SEND_SIGNAL(miner, COMSIG_LIVING_ON_VENT_WIN, src) var/obj/item/card/id/user_id_card = miner.get_idcard(TRUE) if(miner.stat <= SOFT_CRIT) miner.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * boulder_size) diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm index b21d14619dbc..f679b89c6ea3 100644 --- a/code/game/objects/structures/life_candle.dm +++ b/code/game/objects/structures/life_candle.dm @@ -33,7 +33,7 @@ /obj/structure/life_candle/Initialize(mapload) . = ..() if(IS_OVERLAY_LIGHT_SYSTEM(light_system)) - middleman = new(src, "flashlight") + middleman = new(src, "life_candle") RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated)) middleman.being_overriding_light() AddElement(/datum/element/movetype_handler) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 03315f2ad4d1..6d4d7d64fd64 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -37,17 +37,22 @@ ///Flags this race must have to be selectable with this type of mirror. var/race_flags = MIRROR_MAGIC ///List of all Races that can be chosen, decided by its Initialize. - var/list/selectable_races = list() - -/obj/structure/mirror/Destroy() - mirror_options = null - selectable_races = null - return ..() + var/list/selectable_races /obj/structure/mirror/proc/update_choices() for(var/i in mirror_options) mirror_options[i] = icon('icons/hud/radial.dmi', i) + if(CHANGE_RACE in mirror_options) + selectable_races = list() + for(var/datum/species/species_type as anything in get_species_pool()) + if(!race_flags || (species_type::changesource_flags & race_flags)) + selectable_races[species_type::name] = species_type + selectable_races = sort_list(selectable_races) + + else + selectable_races = null + /obj/structure/mirror/Initialize(mapload) . = ..() var/static/list/reflection_filter = alpha_mask_filter(icon = icon('icons/obj/watercloset.dmi', "mirror_mask")) @@ -74,6 +79,10 @@ return FALSE return TRUE +/// Returns a list species typepaths to use for the mirror. This pool is then filtered by flags. +/obj/structure/mirror/proc/get_species_pool() + return subtypesof(/datum/species) + MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28) /obj/structure/mirror/broken @@ -108,9 +117,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) return ITEM_INTERACT_SUCCESS return ITEM_INTERACT_BLOCKING -/obj/structure/mirror/atom_deconstruct() - new /obj/item/wallframe/mirror(loc, 1) - /obj/structure/mirror/proc/display_radial_menu(mob/living/carbon/human/user) if(!can_use_mirror(user)) return @@ -141,7 +147,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) /// Checks if the mob can continue to use the mirror /obj/structure/mirror/proc/can_use_mirror(mob/living/carbon/human/user) - return !QDELETED(src) && !QDELETED(user) && user.can_perform_action(src, FORBID_TELEKINESIS_REACH) + return !QDELETED(src) && !QDELETED(user) && !broken && user.can_perform_action(src, FORBID_TELEKINESIS_REACH) /obj/structure/mirror/proc/change_beard(mob/living/carbon/human/beard_dresser) if(beard_dresser.physique == FEMALE) @@ -321,6 +327,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) /obj/structure/mirror/atom_deconstruct(disassembled = TRUE) if(!disassembled) new /obj/item/shard(loc) + else if(broken) + new /obj/item/wallframe/mirror/broken(loc) else new /obj/item/wallframe/mirror(loc) @@ -353,13 +361,26 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) /obj/item/wallframe/mirror name = "mirror" - desc = "An unmounted mirror. Attach it to a wall to use." + desc = "An unmounted mirror. Attach it to a wall for use." icon = 'icons/obj/watercloset.dmi' icon_state = "mirror" custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2) result_path = /obj/structure/mirror pixel_shift = 28 +/obj/item/wallframe/mirror/heretic + name = /obj/structure/mirror/magic/lesser/heretic::name + desc = /obj/structure/mirror/magic/lesser/heretic::desc + icon = 'icons/obj/watercloset.dmi' + icon_state = "magic_mirror" + result_path = /obj/structure/mirror/magic/lesser/heretic + +/obj/item/wallframe/mirror/broken + name = "broken mirror" + desc = "An unmounted and broken mirror. Attach it to a wall for decor." + icon_state = "mirror_broke" + result_path = /obj/structure/mirror/broken + /obj/structure/mirror/magic name = "magic mirror" desc = "Turn and face the strange... face." @@ -368,16 +389,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) deconstructable = FALSE cursable = FALSE -/obj/structure/mirror/magic/Initialize(mapload) - . = ..() - - if(length(selectable_races)) - return - for(var/datum/species/species_type as anything in subtypesof(/datum/species)) - if(initial(species_type.changesource_flags) & race_flags) - selectable_races[initial(species_type.name)] = species_type - selectable_races = sort_list(selectable_races) - /obj/structure/mirror/magic/change_beard(mob/living/carbon/human/beard_dresser) // magical mirrors do nothing but give you the damn beard var/new_style = tgui_input_list(beard_dresser, "Select a facial hairstyle", "Grooming", SSaccessories.facial_hairstyles_list) if(isnull(new_style) || !can_use_mirror(beard_dresser)) @@ -416,11 +427,76 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) race_changer.add_traits(list(TRAIT_LITERATE, TRAIT_ADVANCEDTOOLUSER), SPECIES_TRAIT) /obj/structure/mirror/magic/lesser + race_flags = NONE -/obj/structure/mirror/magic/lesser/Initialize(mapload) - // Roundstart species don't have a flag, so it has to be set on Initialize. - selectable_races = get_selectable_species().Copy() - return ..() +/obj/structure/mirror/magic/lesser/get_species_pool() + var/list/race_list = list() + for(var/species_id in get_selectable_species()) + race_list += GLOB.species_list[species_id] + return race_list + +/obj/structure/mirror/magic/lesser/heretic + name = "miraculous mirror" + desc = "Gazing at your reflection, you feel like you could be better." + mirror_options = PRIDE_MIRROR_OPTIONS + +/obj/structure/mirror/magic/lesser/heretic/on_species_change(mob/living/carbon/human/race_changer, datum/species/newrace) + . = ..() + if(race_changer.dna?.species?.type == newrace.type) + return + atom_break(null) + +/obj/structure/mirror/magic/lesser/heretic/atom_deconstruct(disassembled) + new /obj/item/shard(loc) // never gives the frame back + +/obj/structure/mirror/magic/lesser/heretic/welder_act(mob/living/user, obj/item/I) + to_chat(user, span_alert("Trying to find where to start fixing [src] proves nigh impossible.")) + return ITEM_INTERACT_BLOCKING + +/obj/structure/mirror/magic/lesser/heretic/pre_change(mob/living/carbon/human/user, picked) + if(IS_HERETIC(user)) + return TRUE + + to_chat(user, span_hypnophrase("You find yourself gazing at [src], unable to look away...")) + user.Immobilize(5 SECONDS) + addtimer(CALLBACK(src, PROC_REF(change_something), user, picked), 5 SECONDS) + return FALSE + +/obj/structure/mirror/magic/lesser/heretic/proc/change_something(mob/living/carbon/human/user, picked) + if(QDELETED(src) || QDELETED(user)) + return + + if(!user.Adjacent(src)) + to_chat(user, span_warning("Pulled away from [src], you snap out of your trance.")) + return + if(broken) + to_chat(user, span_warning("With the shattering of [src], you snap out of your trance.")) + return + + // randomizes your stuff, doesn't affect DNA though so you could easily get genetics to fix you + switch(picked) + if(CHANGE_HAIR, CHANGE_BEARD) + to_chat(user, span_hypnophrase("The hair - yes, the hair is wrong - [pick("it can be improved", "it needs to be changed", "it must be different")]...")) + user.set_hairstyle(random_hairstyle(user.gender)) + user.set_facial_hairstyle(random_facial_hairstyle(user.gender)) + if(CHANGE_RACE) + to_chat(user, span_hypnophrase("The body - no, the body is all wrong - [pick("I must become anew", "I need to be different", "I want to be something else")]...")) + var/list/options = assoc_to_values(selectable_races) - user.dna.species.type + var/datum/species/newrace = GLOB.species_prototypes[pick(options)] + on_species_change(user, newrace) + user.set_species(newrace.type, icon_update = FALSE) + if(CHANGE_SEX) + to_chat(user, span_hypnophrase("My form - yes, the form is all wrong - I need to change it...")) // the wording used here needs to be very deliberate... + var/list/options = list(MALE, FEMALE, PLURAL, NEUTER) - user.gender + user.gender = pick(options) + user.physique = user.gender + if(CHANGE_EYES) + to_chat(user, span_hypnophrase("My eyes - I cannot see clearly - [pick("They're simply not right", "they must be replaced", "they are both wrong")]...")) + user.set_eye_color(random_eye_color()) + if(CHANGE_NAME) // not enabled by default but for badmin support + to_chat(user, span_hypnophrase("My name - it doesn't fit me - [pick("Something else would be better", "it must be changed", "Something more appropriate would be good")]...")) + user.real_name = user.generate_random_mob_name() + user.update_visible_name() /obj/structure/mirror/magic/badmin race_flags = MIRROR_BADMIN diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 56692c157100..82b969414935 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an update_appearance(UPDATE_ICON) /obj/structure/bodycontainer/relaymove(mob/living/user, direction) - if(user.stat || !isturf(loc)) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !isturf(loc)) return if(locked) if(COOLDOWN_FINISHED(src, breakout_message_cooldown)) @@ -120,7 +120,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an span_hear("You hear a metallic creaking from [src].")) if(!do_after(user, BREAKDOWN_TIME, target = src)) return - if(!user || user.stat != CONSCIOUS || user.loc != src) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src) return user.visible_message( span_warning("[user] successfully broke out of [src]!"), diff --git a/code/game/objects/structures/pinatas.dm b/code/game/objects/structures/pinatas.dm index 56a258a45f7b..a97f11998ac3 100644 --- a/code/game/objects/structures/pinatas.dm +++ b/code/game/objects/structures/pinatas.dm @@ -58,7 +58,7 @@ if(player_turf?.is_blocked_turf(TRUE)) return FALSE balloon_alert_to_viewers("setting up pinata...") - if(!do_after(user, 4 SECONDS, target = get_turf(user), progress = TRUE)) + if(!do_after(user, 4 SECONDS, target = get_turf(user))) balloon_alert(user, "cancelled!") new pinata_type(get_turf(user)) balloon_alert(user, "pinata setup") diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 6aac9e7cadae..069b40ad3807 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -119,6 +119,9 @@ FLOOR SAFES return ITEM_INTERACT_SUCCESS /obj/structure/safe/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(user.combat_mode) + return NONE + if(!open) if(!istype(tool, /obj/item/clothing/neck/stethoscope)) to_chat(user, span_warning("You can't put [tool] into the safe while it is closed!")) diff --git a/code/game/objects/structures/secure_safe.dm b/code/game/objects/structures/secure_safe.dm index 71c3d0bb481a..239ab4c162ea 100644 --- a/code/game/objects/structures/secure_safe.dm +++ b/code/game/objects/structures/secure_safe.dm @@ -198,3 +198,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32) /obj/structure/secure_safe/caps_spare/rust_heretic_act() take_damage(damage_amount = 100, damage_type = BRUTE, damage_flag = MELEE, armour_penetration = 100) + return TRUE diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index f705d2fc5ce5..eda088547374 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -40,9 +40,10 @@ . += span_notice("It looks like you could probably scan and tag it with a [scanner_descriptor].") /obj/structure/spawner/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(!scanner_taggable || !is_type_in_list(tool, scanner_types)) + if(scanner_taggable && is_type_in_list(tool, scanner_types)) gps_tag(user) return ITEM_INTERACT_SUCCESS + return NONE /// Tag the spawner, prefixing its GPS entry with an identifier - or giving it one, if nonexistent. @@ -202,19 +203,15 @@ /obj/structure/spawner/nether/examine(mob/user) . = ..() - if(isskeleton(user) || iszombie(user)) - . += "A direct link to another dimension full of creatures very happy to see you. [span_nicegreen("You can see your house from here!")]" - else - . += "A direct link to another dimension full of creatures not very happy to see you. [span_warning("Entering the link would be a very bad idea.")]" + . += "A direct link to another dimension full of creatures not very happy to see you. [span_warning("Entering the link would be a very bad idea.")]" /obj/structure/spawner/nether/attack_hand(mob/user, list/modifiers) . = ..() - if(isskeleton(user) || iszombie(user)) - to_chat(user, span_notice("You don't feel like going home yet...")) - else - user.visible_message(span_warning("[user] is violently pulled into the link!"), \ - span_userdanger("Touching the portal, you are quickly pulled through into a world of unimaginable horror!")) - contents.Add(user) + user.visible_message( + span_warning("[user] is violently pulled into the link!"), + span_userdanger("Touching the portal, you are quickly pulled through into a world of unimaginable horror!") + ) + contents.Add(user) /obj/structure/spawner/nether/process(seconds_per_tick) for(var/mob/living/living_mob in contents) diff --git a/code/game/objects/structures/spirit_board.dm b/code/game/objects/structures/spirit_board.dm index e8882251237f..3719f9da1b1b 100644 --- a/code/game/objects/structures/spirit_board.dm +++ b/code/game/objects/structures/spirit_board.dm @@ -71,15 +71,11 @@ ghost.log_message("picked a letter on [src], which was \"[planchette]\".", LOG_GAME) COOLDOWN_START(src, next_use, rand(3 SECONDS, 5 SECONDS)) lastuser = ghost.ckey - for(var/mob/viewer in range(2, src)) - if(isnull(viewer.client)) - continue - if(viewer.stat != CONSCIOUS && viewer.stat != DEAD) // You gotta be awake or dead to pay the toll - continue - if(viewer.is_blind()) - to_chat(viewer, span_hear("You hear a scraping sound...")) - else - to_chat(viewer, span_notice("The planchette slowly moves... and stops at the letter \"[planchette]\".")) + visible_message( + message = span_notice("The planchette slowly moves... and stops at the letter \"[planchette]\"."), + blind_message = span_hear("You hear a scraping sound..."), + vision_distance = 2, + ) /obj/structure/spirit_board/proc/spirit_board_checks(mob/ghost) var/cd_penalty = (ghost.ckey == lastuser) ? 1 SECONDS : 0 SECONDS //Give some other people a chance, hog. @@ -98,7 +94,7 @@ if(isnull(player.ckey) || isnull(player.client)) continue - if(player.client?.is_afk() || player.stat != CONSCIOUS || HAS_TRAIT(player, TRAIT_HANDS_BLOCKED))//no playing with braindeads or corpses or handcuffed dudes. + if(player.client?.is_afk() || HAS_TRAIT(player, TRAIT_HANDS_BLOCKED))//no playing with braindeads or corpses or handcuffed dudes. to_chat(ghost, span_warning("[player] doesn't seem to be paying attention...")) continue diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm index ea07ec2794b6..4683a93e1739 100644 --- a/code/game/objects/structures/stairs.dm +++ b/code/game/objects/structures/stairs.dm @@ -338,7 +338,7 @@ /obj/structure/stairs/proc/can_fall_down_stairs(mob/living/falling) if(falling.buckled || falling.pulledby) return FALSE - if(falling.stat >= UNCONSCIOUS) // if you shove someone unconscious down the stairs, they'd probably roll + if(IS_UNCONSCIOUS(falling)) // if you shove someone unconscious down the stairs, they'd probably roll return TRUE if(falling.has_status_effect(/datum/status_effect/staggered)) // off balance return TRUE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index d2847efaa551..beb2f2c9bdaa 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -29,7 +29,7 @@ smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_TABLES canSmoothWith = SMOOTH_GROUP_TABLES - var/static/list/turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY, TRAIT_IMMERSE_STOPPED) + var/static/list/turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY, TRAIT_IMMERSE_STOPPED, TRAIT_TURF_PROJECTS_WHISPERS) ///a bit fucky, I know. but this is needed to get sorted on init smoothing groups stored var/list/on_init_smoothed_vars var/frame = /obj/structure/table_frame @@ -89,7 +89,7 @@ return make_climbable() - AddElement(/datum/element/give_turf_traits, string_list(turf_traits)) + AddElement(/datum/element/give_turf_traits, turf_traits) AddElement(/datum/element/footstep_override, priority = STEP_SOUND_TABLE_PRIORITY) AddElement(/datum/element/table_smash, gentle_push = slam_gently, after_smash_proccall = PROC_REF(after_smash)) @@ -1486,7 +1486,7 @@ if(!rack_choice) return // DARKPACK EDIT ADD END - if(do_after(user, 5 SECONDS, target = user, progress=TRUE)) + if(do_after(user, 5 SECONDS, target = user)) if(!user.temporarilyRemoveItemFromInventory(src)) return var/obj/structure/rack/R = new rack_choice(get_turf(src)) // DARKPACK EDIT CHANGE diff --git a/code/game/objects/structures/training_machine.dm b/code/game/objects/structures/training_machine.dm index c84f630fe514..755c3bc87d16 100644 --- a/code/game/objects/structures/training_machine.dm +++ b/code/game/objects/structures/training_machine.dm @@ -281,7 +281,7 @@ return var/list/targets for(var/mob/living/carbon/target in oview(1, get_turf(src))) //Find adjacent target - if (target.stat == CONSCIOUS && target.Adjacent(src)) + if (!IS_UNCONSCIOUS_OR_CRIT(target) && target.Adjacent(src)) LAZYADD(targets, target) var/mob/living/carbon/target = pick(targets) if (!target) diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm index e35232700a0f..31e601d2b946 100644 --- a/code/game/objects/structures/transit_tubes/station.dm +++ b/code/game/objects/structures/transit_tubes/station.dm @@ -116,7 +116,7 @@ for(var/thing in pod) if(ismob(thing)) var/mob/mob_content = thing - if(mob_content.client && mob_content.stat < UNCONSCIOUS) + if(mob_content.client && !IS_UNCONSCIOUS(mob_content)) continue // Let the mobs with clients decide what they want to do themselves. var/atom/movable/movable_content = thing movable_content.forceMove(loc) //Everything else is moved out of. diff --git a/code/game/objects/structures/wall_support.dm b/code/game/objects/structures/wall_support.dm new file mode 100644 index 000000000000..69321121f154 --- /dev/null +++ b/code/game/objects/structures/wall_support.dm @@ -0,0 +1,88 @@ +/** + * Wall supports are designed to help you safely deconstruct walls, without fear + * of all wall-mounted equipment like APCs or light fixtures falling off. + * All of the logic for handling wall-mounted equipment is in `atom_mounted.dm` + * (/datum/component/atom_mounted) +**/ +/obj/structure/wall_support + desc = "A temporary support structure for wall-mounted equipment. Allows you to safely deconstruct walls." + name = "Wall Support" + icon = 'icons/obj/structures.dmi' + icon_state = "wall_support" + base_icon_state = "wall_support" + layer = WALL_OBJ_LAYER - 0.1 + density = FALSE + anchored = TRUE + pass_flags_self = NONE + obj_flags = CONDUCTS_ELECTRICITY | CAN_BE_HIT | IGNORE_DENSITY + pressure_resistance = 5*ONE_ATMOSPHERE + armor_type = /datum/armor/structure_grille + max_integrity = 50 + integrity_failure = 0.4 + custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT) + var/rods_type = /obj/item/stack/rods + var/rods_amount = 2 + +/obj/structure/wall_support/Initialize(mapload) + . = ..() + register_context() + +/obj/structure/wall_support/examine(mob/user) + . = ..() + if(resistance_flags & INDESTRUCTIBLE) + return + if(anchored) + . += span_notice("It's secured in place with [EXAMINE_HINT("screws")]. The rods look like they could be [EXAMINE_HINT("cut")] through.") + else + . += span_notice("The anchoring screws are [EXAMINE_HINT("unscrewed")]. The rods look like they could be [EXAMINE_HINT("cut")] through.") + +/obj/structure/wall_support/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(held_item?.tool_behaviour == TOOL_WIRECUTTER) + context[SCREENTIP_CONTEXT_RMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET + if(held_item?.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Unanchor" : "Anchor"]" + return CONTEXTUAL_SCREENTIP_SET + return . + +/obj/structure/wall_support/wirecutter_act_secondary(mob/living/user, obj/item/tool) + add_fingerprint(user) + tool.play_tool_sound(src, 100) + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS + +/obj/structure/wall_support/screwdriver_act(mob/living/user, obj/item/tool) + if(!isturf(loc)) + return FALSE + add_fingerprint(user) + if(!tool.use_tool(src, user, 0, volume=100)) + return FALSE + set_anchored(!anchored) + user.visible_message(span_notice("[user] [anchored ? "fastens" : "unfastens"] [src]."), \ + span_notice("You [anchored ? "fasten [src] to" : "unfasten [src] from"] the floor.")) + return ITEM_INTERACT_SUCCESS + +/obj/structure/wall_support/play_attack_sound(...) + return call(src, /obj/structure/grille::play_attack_sound())(arglist(args)) + +/obj/structure/wall_support/atom_deconstruct(disassembled = TRUE) + var/obj/rods = new rods_type(drop_location(), rods_amount) + transfer_fingerprints_to(rods) + +/obj/structure/wall_support/atom_break() + . = ..() + if(broken) + return + atom_integrity = 20 + broken = TRUE + rods_amount = 1 + var/obj/item/dropped_rods = new rods_type(drop_location(), rods_amount) + transfer_fingerprints_to(dropped_rods) + update_appearance() + +/obj/structure/wall_support/should_atmos_process(datum/gas_mixture/air, exposed_temperature) + return exposed_temperature > T0C + 1500 && !broken + +/obj/structure/wall_support/atmos_expose(datum/gas_mixture/air, exposed_temperature) + take_damage(1, BURN, 0, 0) diff --git a/code/game/say.dm b/code/game/say.dm index 1a2449b3a8d1..a59a884ca6d3 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -70,8 +70,7 @@ GLOBAL_LIST_INIT(freqtospan, list( return spans |= speech_span language ||= get_selected_language() - if(!message_mods[SAY_MOD_VERB]) - message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) + message_mods[SAY_MOD_VERB] ||= say_mod(message, message_mods) send_speech(message, message_range, src, bubble_type, spans, language, message_mods, forced = forced) /// Called when this movable hears a message from a source. diff --git a/code/game/turfs/closed/indestructible.dm b/code/game/turfs/closed/indestructible.dm index b89e1c627521..d7b726fde109 100644 --- a/code/game/turfs/closed/indestructible.dm +++ b/code/game/turfs/closed/indestructible.dm @@ -5,6 +5,8 @@ explosive_resistance = 50 rust_resistance = RUST_RESISTANCE_ABSOLUTE turf_flags = IS_SOLID | NO_RUST + baseturfs = /turf/open/indestructible/plating + /turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE) return diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index b16e8b45bdf4..4421661118a1 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -204,6 +204,17 @@ scan_state_overlay.pixel_y = 224 scan_overlay.add_overlay(scan_state_overlay) +/turf/closed/mineral/set_smoothed_icon_state(new_junction) + var/old_junction = smoothing_junction + . = ..() + if(new_junction != ALL_SMOOTHING_JUNCTIONS || (!isnull(old_junction) && old_junction != new_junction)) + update_appearance(UPDATE_OVERLAYS) + +/turf/closed/mineral/update_overlays() + . = ..() + if(smoothing_junction != ALL_SMOOTHING_JUNCTIONS) + . += fast_emissive_blocker(src) + /turf/closed/mineral/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) if(turf_type) underlay_appearance.icon = initial(turf_type.icon) diff --git a/code/game/turfs/closed/wall/concrete_walls.dm b/code/game/turfs/closed/wall/concrete_walls.dm new file mode 100644 index 000000000000..f191eed93ccd --- /dev/null +++ b/code/game/turfs/closed/wall/concrete_walls.dm @@ -0,0 +1,120 @@ +/turf/closed/wall/concrete + name = "concrete wall" + desc = "A thick wall made of concrete, poured around steel supports." + icon = 'icons/turf/walls/concrete.dmi' + icon_state = "concrete-0" + base_icon_state = "concrete" + explosive_resistance = 2 + rad_insulation = RAD_MEDIUM_INSULATION + rust_resistance = RUST_RESISTANCE_REINFORCED + + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CONCRETE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_CONCRETE_WALLS + hardness = 30 + + sheet_type = /obj/item/stack/ore/glass/concrete_powder + sheet_amount = 2 + girder_type = /obj/structure/grille + break_sound = 'sound/effects/break_stone.ogg' + + /// Time to fully cure + var/time_to_harden = 60 SECONDS + /// fraction ranging from 0 to 1 -- 0 is fully soft, 1 is fully hardened + /// don't change this in subtypes unless you want them to spawn in soft on maps + var/harden_lvl = 1 + +/turf/closed/wall/concrete/Initialize(mapload) + . = ..() + check_harden() + +/turf/closed/wall/concrete/deconstruction_hints(mob/user) + . = list() + . += span_notice("[p_They()] look[p_s()] like you could smash [p_them()] with [tool_behaviour_name(TOOL_MINING)].") + switch(harden_lvl) + if(0.8 to 0.99) + . += "[p_They()] look[p_s()] nearly dry." + if(0.4 to 0.8) + . += "[p_They()] look[p_s()] a little wet." + if(0 to 0.4) + . += "[p_They()] look[p_s()] freshly poured." + return + +/turf/closed/wall/concrete/try_clean(obj/item/W, mob/living/user) + // You can't weld dents on concrete wall + return FALSE + +/turf/closed/wall/concrete/try_decon(obj/item/I, mob/user) + if (I.tool_behaviour == TOOL_MINING) + to_chat(user, span_notice("You begin breaking the wall...")) + if(I.use_tool(src, user, slicing_duration, volume=100)) + // break_prob decreases linearly from 100 to hardness as it cures + var/break_prob = hardness + (1 - harden_lvl) * (100 - hardness) + if(prob(break_prob)) + to_chat(user, span_notice("You break the wall.")) + dismantle_wall() + new /obj/effect/decal/cleanable/concrete_dust(src) + else + add_dent(WALL_DENT_HIT) + playsound(src, break_sound, 50, TRUE) + + return TRUE + + return FALSE + +/turf/closed/wall/concrete/break_wall() + var/atom/girder = ..() + girder.atom_break() + return girder + +/turf/closed/wall/concrete/update_icon(updates=ALL) + . = ..() + if(!.) + return + remove_filter("harden") + if(harden_lvl == 1) + return + + var/offset = (1 - harden_lvl) * 0.4 + var/base = 1 - offset + var/col_filter = list(base, 0, 0, 0, base, 0, 0, 0, base, offset, offset, offset) + add_filter("harden", 1, color_matrix_filter(col_filter, FILTER_COLOR_RGB)) + return + +/turf/closed/wall/concrete/proc/check_harden() + harden_lvl = clamp(harden_lvl, 0, 1) + if(harden_lvl < 1) + START_PROCESSING(SSobj, src) + update_icon(UPDATE_ICON_STATE) + +/turf/closed/wall/concrete/process(seconds_per_tick) + var/time_per_tick = seconds_per_tick SECONDS + harden_lvl = min(harden_lvl + (time_per_tick / time_to_harden), 1) + if(harden_lvl == 1) + STOP_PROCESSING(SSobj, src) + update_icon(UPDATE_ICON_STATE) + +/turf/closed/wall/concrete/reinforced + name = "hexacrete wall" + desc = "A thick wall made of steel-reinforced hexacrete. Sturdier than a normal concrete wall." + icon = 'icons/turf/walls/hexacrete.dmi' + icon_state = "hexacrete-0" + base_icon_state = "hexacrete" + + hardness = 15 + explosive_resistance = 3 + rad_insulation = RAD_HEAVY_INSULATION + rust_resistance = RUST_RESISTANCE_REINFORCED + girder_type = /obj/structure/girder + + time_to_harden = 60 SECONDS + +/turf/closed/wall/concrete/reinforced/rcd_vals(...) + return call(src, /turf/closed/wall/r_wall::rcd_vals())(arglist(args)) + +/turf/closed/wall/concrete/reinforced/rcd_act(...) + return call(src, /turf/closed/wall/r_wall::rcd_act())(arglist(args)) + +/turf/closed/wall/concrete/reinforced/rust_turf(magic = FALSE) + return call(src, /turf/closed/wall/r_wall::rust_turf())(magic) + diff --git a/code/game/turfs/closed/wall/mineral_walls.dm b/code/game/turfs/closed/wall/mineral_walls.dm index 23700a6c9495..7d02bf2a7797 100644 --- a/code/game/turfs/closed/wall/mineral_walls.dm +++ b/code/game/turfs/closed/wall/mineral_walls.dm @@ -304,28 +304,6 @@ . = ..() copy_to_turf.transform = transform -/turf/closed/wall/mineral/titanium/survival - name = "pod wall" - desc = "An easily-compressible wall used for temporary shelter." - icon = 'icons/turf/walls/survival_pod_walls.dmi' - icon_state = "survival_pod_walls-0" - base_icon_state = "survival_pod_walls" - smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS - canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_WINDOW_FULLTILE + SMOOTH_GROUP_TITANIUM_WALLS - rust_resistance = RUST_RESISTANCE_TITANIUM - -/turf/closed/wall/mineral/titanium/survival/nodiagonal - icon = 'icons/turf/walls/survival_pod_walls.dmi' - icon_state = "survival_pod_walls-0" - base_icon_state = "survival_pod_walls" - smoothing_flags = SMOOTH_BITMASK - rust_resistance = RUST_RESISTANCE_TITANIUM - -/turf/closed/wall/mineral/titanium/survival/pod - smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS - canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD - rust_resistance = RUST_RESISTANCE_TITANIUM - /turf/closed/wall/mineral/titanium/rust_turf(magic = FALSE) if(HAS_TRAIT(src, TRAIT_RUSTY)) ChangeTurf(/turf/closed/wall/rust) @@ -349,13 +327,65 @@ custom_materials = list(/datum/material/alloy/plastitanium = SHEET_MATERIAL_AMOUNT*2) rust_resistance = RUST_RESISTANCE_TITANIUM +/turf/closed/wall/mineral/plastitanium/wall_fill + sheet_type = /obj/item/stack/wall_filling/plastitanium/basic + sheet_amount = 1 + +/turf/closed/wall/mineral/plastitanium/darkpod + name = "dark pod wall" + desc = "An easily-compressible wall used for temporary shelter." + sheet_type = /obj/item/stack/wall_filling/plastitanium/pod + sheet_amount = 1 + icon = 'icons/turf/walls/dark_pod.dmi' + icon_state = "dark_pod_walls-0" + base_icon_state = "dark_pod_walls" + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + rust_resistance = RUST_RESISTANCE_TITANIUM + +/turf/closed/wall/mineral/plastitanium/redpod + name = "red pod wall" + desc = "An easily-compressible wall used for temporary shelter." + sheet_type = /obj/item/stack/wall_filling/plastitanium/redpod + sheet_amount = 1 + icon = 'icons/turf/walls/red_pod.dmi' + icon_state = "red_pod_walls-0" + base_icon_state = "red_pod_walls" + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + rust_resistance = RUST_RESISTANCE_TITANIUM + +/turf/closed/wall/mineral/plastitanium/survival + name = "pod wall" + desc = "An easily-compressible wall used for temporary shelter." + icon = 'icons/turf/walls/survival_pod_walls.dmi' + icon_state = "survival_pod_walls-0" + base_icon_state = "survival_pod_walls" + sheet_type = /obj/item/stack/wall_filling/plastitanium/survivalpod + sheet_amount = 1 + smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS + smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_WINDOW_FULLTILE + SMOOTH_GROUP_TITANIUM_WALLS + rust_resistance = RUST_RESISTANCE_TITANIUM + +/turf/closed/wall/mineral/plastitanium/survival/nodiagonal + icon = 'icons/turf/walls/survival_pod_walls.dmi' + icon_state = "survival_pod_walls-0" + base_icon_state = "survival_pod_walls" + smoothing_flags = SMOOTH_BITMASK + rust_resistance = RUST_RESISTANCE_TITANIUM + +/turf/closed/wall/mineral/plastitanium/survival/pod + smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + rust_resistance = RUST_RESISTANCE_TITANIUM + /turf/closed/wall/mineral/plastitanium/rust_turf(magic = FALSE) if(HAS_TRAIT(src, TRAIT_RUSTY)) ChangeTurf(/turf/closed/wall/rust) return TRUE return ..() - /turf/closed/wall/mineral/plastitanium/nodiagonal icon = MAP_SWITCH('icons/turf/walls/plastitanium_wall.dmi', 'icons/turf/walls/misc_wall.dmi') icon_state = MAP_SWITCH("plastitanium_wall-0", "plastitanium_nd") diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm index 2501b3767b38..7d90ad3c4da1 100644 --- a/code/game/turfs/closed/walls.dm +++ b/code/game/turfs/closed/walls.dm @@ -40,6 +40,8 @@ var/girder_state = GIRDER_NORMAL /// How long this wall takes to make by using its sheet type on a girder. var/make_delay = 4 SECONDS + /// Sound played when wall is dismantled + var/break_sound = 'sound/items/tools/welder.ogg' /turf/closed/wall/Initialize(mapload) . = ..() @@ -64,6 +66,9 @@ if((initial(smoothing_flags) & SMOOTH_DIAGONAL_CORNERS) && held_item.tool_behaviour == TOOL_WRENCH) context[SCREENTIP_CONTEXT_LMB] = "Adjust Wall Corner" return CONTEXTUAL_SCREENTIP_SET + else if(istype(held_item, /obj/structure/wall_support::rods_type)) + context[SCREENTIP_CONTEXT_LMB] = "Build Wall Support" + return CONTEXTUAL_SCREENTIP_SET /turf/closed/wall/mouse_drop_receive(atom/dropping, mob/user, params) //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times @@ -94,7 +99,7 @@ if(devastated) devastate_wall() else - playsound(src, 'sound/items/tools/welder.ogg', 100, TRUE) + playsound(src, break_sound, 100, TRUE) var/newgirder = break_wall() if(newgirder) //maybe we don't /want/ a girder! transfer_fingerprints_to(newgirder) @@ -202,7 +207,7 @@ add_fingerprint(user) //the istype cascade has been spread among various procs for easy overriding - if(try_clean(tool, user) || try_decon(tool, user)) + if(try_clean(tool, user) || try_decon(tool, user) || try_build_support(tool, user)) return ITEM_INTERACT_SUCCESS return NONE @@ -239,6 +244,24 @@ return FALSE +///Helper for building wall_support on top of desired wall +/turf/closed/wall/proc/try_build_support(obj/item/I, mob/user) + if(!isstack(I) || !istype(I, /obj/structure/wall_support::rods_type)) + return FALSE + var/obj/item/stack/rods = I + var/amount_needed = /obj/structure/wall_support::rods_amount + if(rods.get_amount() < amount_needed) + to_chat(user, span_warning("You need at least [amount_needed] rods for that!")) + return FALSE + to_chat(user, span_notice("You start constructing wall support...")) + if(do_after(user, 2 SECONDS, target = src)) + var/obj/structure/wall_support/WS = new(src) + rods.use(amount_needed) + rods.transfer_fingerprints_to(WS) + to_chat(user, span_notice("You place [WS] on [src].")) + return TRUE + return FALSE + /turf/closed/wall/singularity_pull(atom/singularity, current_size) ..() wall_singularity_pull(current_size) diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm index 2dd2484e52e9..50b278662750 100644 --- a/code/game/turfs/open/floor.dm +++ b/code/game/turfs/open/floor.dm @@ -145,16 +145,16 @@ if(overfloor_placed && pry_tile(I, user)) return TRUE -/turf/open/floor/proc/try_replace_tile(obj/item/stack/tile/T, mob/user, list/modifiers) - if(T.turf_type == type && T.turf_dir == dir) +/turf/open/floor/proc/try_replace_tile(obj/item/stack/tile/tile, mob/user, list/modifiers) + if(tile.turf_type == type && tile.turf_dir == dir) return - var/obj/item/crowbar/CB = user.is_holding_tool_quality(TOOL_CROWBAR) - if(!CB) + var/obj/item/crowbar/crowbar = user.is_holding_tool_quality(TOOL_CROWBAR) + if(!crowbar) return - var/turf/open/floor/plating/P = pry_tile(CB, user, TRUE) - if(!istype(P)) + var/turf/open/floor/plating/plating = pry_tile(crowbar, user, TRUE) + if(!istype(plating)) return - P.attackby(T, user, modifiers) + plating.base_item_interaction(user, tile, modifiers) /turf/open/floor/proc/pry_tile(obj/item/I, mob/user, silent = FALSE) I.play_tool_sound(src, 80) diff --git a/code/game/turfs/open/floor/concrete_floor.dm b/code/game/turfs/open/floor/concrete_floor.dm new file mode 100644 index 000000000000..06d343c1747c --- /dev/null +++ b/code/game/turfs/open/floor/concrete_floor.dm @@ -0,0 +1,189 @@ +/turf/open/floor/concrete + name = "concrete floor" + desc = "Cold, bare concrete flooring." + icon = 'icons/turf/floors/concrete.dmi' + damaged_dmi = 'icons/turf/floors/concrete.dmi' + icon_state = "conc_smooth_1" + base_icon_state = "conc_smooth" + baseturfs = /turf/open/floor/plating + floor_tile = null + footstep = FOOTSTEP_CONCRETE + barefootstep = FOOTSTEP_HARD_BAREFOOT + clawfootstep = FOOTSTEP_HARD_CLAW + heavyfootstep = FOOTSTEP_GENERIC_HEAVY + leave_footprints = TRUE + + var/smash_time = 3 SECONDS + /// Time to fully cure + var/time_to_harden = 60 SECONDS + /// fraction ranging from 0 to 1 -- 0 is fully soft, 1 is fully hardened + /// don't change this in subtypes unless you want them to spawn in soft on maps + var/harden_lvl = 1 + var/has_variation = TRUE + + var/shape_types = list( + /turf/open/floor/concrete, + /turf/open/floor/concrete/slab_1, + /turf/open/floor/concrete/slab_2, + /turf/open/floor/concrete/slab_3, + /turf/open/floor/concrete/slab_4, + /turf/open/floor/concrete/tiles + ) + +/turf/open/floor/concrete/Initialize(mapload) + . = ..() + if(has_variation) + icon_state = "[base_icon_state]_[rand(1,4)]" + check_harden() + update_icon(UPDATE_ICON_STATE) + +/turf/open/floor/concrete/broken_states() + return list("concdam_1", "concdam_2", "concdam_3", "concdam_4") + +/turf/open/floor/concrete/examine(mob/user) + . = ..() + . += span_notice("[p_They()] look[p_s()] like you could smash [p_them()] with [tool_behaviour_name(TOOL_MINING)].") + switch(harden_lvl) + if(0.8 to 0.99) + . += "[p_They()] look[p_s()] nearly dry." + if(0.4 to 0.8) + . += "[p_They()] look[p_s()] a little wet." + if(0 to 0.4) + . += "[p_They()] look[p_s()] freshly poured." + return + +/turf/open/floor/concrete/add_footprint(mob/living/carbon/human/walker, movement_direction) + if (harden_lvl <= 0.8) + ..() + +/turf/open/floor/concrete/clear_footprints() + if (harden_lvl <= 0.8) + ..() + else + update_appearance() + +/turf/open/floor/concrete/attackby(obj/item/I, mob/living/user, list/modifiers) + . = ..() + if(.) + return + if(I.tool_behaviour == TOOL_MINING) + to_chat(user, span_notice("You start smashing [src]...")) + var/adj_time = (broken || burnt) ? smash_time/2 : smash_time + if(I.use_tool(src, user, adj_time, volume=30)) + new /obj/effect/decal/cleanable/concrete_dust(src) + to_chat(user, span_notice("You break [src].")) + playsound(src, 'sound/effects/break_stone.ogg', 30, TRUE) + remove_tile(silent=TRUE) + return TRUE + return FALSE + +/turf/open/floor/concrete/try_replace_tile(obj/item/stack/tile/T, mob/user, list/modifiers) + return FALSE + +/turf/open/floor/concrete/pry_tile(obj/item/I, mob/user, silent) + return FALSE + +/turf/open/floor/concrete/update_icon(updates=ALL) + . = ..() + if(!.) + return + remove_filter("harden") + if(harden_lvl == 1) + return + + var/offset = (1 - harden_lvl) * 0.3 + var/base = 1 - offset + var/col_filter = list(base,0,0, 0,base,0, 0,0,base, offset, offset, offset) + add_filter("harden", 1, color_matrix_filter(col_filter, FILTER_COLOR_RGB)) + return + +/turf/open/floor/concrete/proc/check_harden() + harden_lvl = clamp(harden_lvl, 0, 1) + if(harden_lvl < 1) + START_PROCESSING(SSobj, src) + update_icon(UPDATE_ICON_STATE) + +/turf/open/floor/concrete/process(seconds_per_tick) + var/time_per_tick = seconds_per_tick SECONDS + harden_lvl = min(harden_lvl + (time_per_tick / time_to_harden), 1) + if(harden_lvl == 1) + STOP_PROCESSING(SSobj, src) + update_icon(UPDATE_ICON_STATE) + +/turf/open/floor/concrete/proc/handle_shape(mob/user) + if(harden_lvl >= 0.8) + return FALSE + var/list/opts = list() + + for(var/turf/open/floor/concrete/conc as anything in shape_types) + opts[conc] = image(icon = initial(conc.icon), icon_state = initial(conc.icon_state)) + + var/choice = show_radial_menu( + user, + src, + opts, + uniqueid = "concmenu_[REF(user)]", + radius = 48, + custom_check = CALLBACK(src, PROC_REF(check_menu), user), + require_near = TRUE + ) + if(!choice) + return FALSE + to_chat(user, "You reshape [src].") + var/old_harden = harden_lvl + var/turf/open/floor/concrete/newconc = ChangeTurf(choice, flags = CHANGETURF_INHERIT_AIR) + newconc.harden_lvl = old_harden + newconc.check_harden() + newconc.update_appearance() + return TRUE + +/turf/open/floor/concrete/proc/check_menu(mob/living/user) + if(!istype(user)) + return FALSE + if(user.incapacitated || !user.Adjacent(src)) + return FALSE + return TRUE + +/turf/open/floor/concrete/slab_1 + icon_state = "conc_slab_1" + has_variation = FALSE + +/turf/open/floor/concrete/slab_2 + icon_state = "conc_slab_2" + has_variation = FALSE + +/turf/open/floor/concrete/slab_3 + icon_state = "conc_slab_3" + has_variation = FALSE + +/turf/open/floor/concrete/slab_4 + icon_state = "conc_slab_4" + has_variation = FALSE + +/turf/open/floor/concrete/tiles + icon_state = "conc_tiles" + has_variation = FALSE + +/turf/open/floor/concrete/reinforced + name = "hexacrete floor" + desc = "Reinforced hexacrete tiling." + icon = 'icons/turf/floors/hexacrete.dmi' + icon_state = "hexacrete-0" + base_icon_state = "hexacrete" + has_variation = FALSE + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_OPEN_FLOOR + SMOOTH_GROUP_FLOOR_HEXACRETE + canSmoothWith = SMOOTH_GROUP_FLOOR_HEXACRETE + + smash_time = 8 SECONDS + time_to_harden = 90 SECONDS + shape_types = list(/turf/open/floor/concrete/reinforced) + slowdown = -0.3 + +/turf/open/floor/concrete/pavement + name = "strip of pavement" + desc = "Hot, coarse pavement. Preferred for roadways, as vehicles are generally quieter driven on this than on traditional concrete." + icon_state = "pavement_1" + base_icon_state = "pavement" + shape_types = list(/turf/open/floor/concrete/pavement) + slowdown = -0.2 diff --git a/code/game/turfs/open/floor/fancy_floor.dm b/code/game/turfs/open/floor/fancy_floor.dm index 3be12306bab9..270cfef5db95 100644 --- a/code/game/turfs/open/floor/fancy_floor.dm +++ b/code/game/turfs/open/floor/fancy_floor.dm @@ -31,18 +31,18 @@ return TRUE return pry_tile(I, user) ? TRUE : FALSE -/turf/open/floor/wood/try_replace_tile(obj/item/stack/tile/T, mob/user, list/modifiers) - if(T.turf_type == type) +/turf/open/floor/wood/try_replace_tile(obj/item/stack/tile/newtile, mob/user, list/modifiers) + if(newtile.turf_type == type) return var/obj/item/tool = user.is_holding_tool_quality(TOOL_SCREWDRIVER) if(!tool) tool = user.is_holding_tool_quality(TOOL_CROWBAR) if(!tool) return - var/turf/open/floor/plating/P = pry_tile(tool, user, TRUE) - if(!istype(P)) + var/turf/open/floor/plating/bare_floor = pry_tile(tool, user, TRUE) + if(!istype(bare_floor)) return - P.attackby(T, user, modifiers) + bare_floor.base_item_interaction(user, newtile, modifiers) /turf/open/floor/wood/pry_tile(obj/item/C, mob/user, silent = FALSE) C.play_tool_sound(src, 80) @@ -166,6 +166,11 @@ /turf/open/floor/grass/proc/spawniconchange() icon_state = "grass[rand(0,3)]" +/turf/open/floor/grass/norandomicon + +/turf/open/floor/grass/norandomicon/spawniconchange() + return + /turf/open/floor/grass/lavaland name = "dead grass patch" desc = "It turns out grass doesn't grow very well in hell." diff --git a/code/game/turfs/open/floor/mineral_floor.dm b/code/game/turfs/open/floor/mineral_floor.dm index 8456afc084b7..e6a6e91b529b 100644 --- a/code/game/turfs/open/floor/mineral_floor.dm +++ b/code/game/turfs/open/floor/mineral_floor.dm @@ -155,6 +155,8 @@ /turf/open/floor/mineral/titanium/tiled/purple/airless initial_gas_mix = AIRLESS_ATMOS + + //PLASTITANIUM (syndieshuttle) /turf/open/floor/mineral/plastitanium name = "shuttle floor" @@ -176,6 +178,27 @@ /turf/open/floor/mineral/plastitanium/red/airless initial_gas_mix = AIRLESS_ATMOS +/turf/open/floor/mineral/plastitanium/pod + name = "pod floor" + icon_state = "podfloor" + floor_tile = /obj/item/stack/tile/mineral/plastitanium/pod + +/turf/open/floor/mineral/plastitanium/pod/light + icon_state = "podfloor_light" + floor_tile = /obj/item/stack/tile/mineral/plastitanium/pod/light + +/turf/open/floor/mineral/plastitanium/pod/dark + icon_state = "podfloor_dark" + floor_tile = /obj/item/stack/tile/mineral/plastitanium/pod/dark + +/turf/open/floor/mineral/plastitanium/pod/redlight + icon_state = "podfloor_red_light" + floor_tile = /obj/item/stack/tile/mineral/plastitanium/pod/redlight + +/turf/open/floor/mineral/plastitanium/pod/red + icon_state = "podfloor_red" + floor_tile = /obj/item/stack/tile/mineral/plastitanium/pod/red + //Used in SnowCabin.dm /turf/open/floor/mineral/plastitanium/red/snow_cabin temperature = ICEBOX_MIN_TEMPERATURE diff --git a/code/game/turfs/open/floor/misc_floor.dm b/code/game/turfs/open/floor/misc_floor.dm index 9ce464c68c73..ba1c0a6e0344 100644 --- a/code/game/turfs/open/floor/misc_floor.dm +++ b/code/game/turfs/open/floor/misc_floor.dm @@ -136,20 +136,6 @@ /turf/open/floor/circuit/red/telecomms initial_gas_mix = TCOMMS_ATMOS -/turf/open/floor/pod - name = "pod floor" - icon_state = "podfloor" - floor_tile = /obj/item/stack/tile/pod - -/turf/open/floor/pod/light - icon_state = "podfloor_light" - floor_tile = /obj/item/stack/tile/pod/light - -/turf/open/floor/pod/dark - icon_state = "podfloor_dark" - floor_tile = /obj/item/stack/tile/pod/dark - - /turf/open/floor/noslip name = "high-traction floor" icon_state = "noslip" diff --git a/code/game/turfs/open/ice.dm b/code/game/turfs/open/ice.dm index 1a227d2a0340..fbe5a2caf8da 100644 --- a/code/game/turfs/open/ice.dm +++ b/code/game/turfs/open/ice.dm @@ -49,7 +49,8 @@ /turf/open/misc/ice/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(tool.tool_behaviour != TOOL_SHOVEL && tool.tool_behaviour != TOOL_MINING) - return NONE + return ..() + return dig_hole(user) ? ITEM_INTERACT_SUCCESS : NONE /turf/open/misc/ice/proc/dig_hole(mob/living/user) diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index dc5dc15ee1a2..e9e3df973399 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -171,14 +171,19 @@ if(istype(tool, /obj/item/stack/rods)) build_with_rods(tool, user) - else if(ismetaltile(tool)) + return ITEM_INTERACT_SUCCESS + + if(ismetaltile(tool)) build_with_floor_tiles(tool, user) - else if(istype(tool, /obj/item/stack/thermoplastic)) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/stack/thermoplastic)) build_with_transport_tiles(tool, user) - else if(istype(tool, /obj/item/stack/sheet/mineral/titanium)) - build_with_titanium(tool, user) + return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_SUCCESS + if(istype(tool, /obj/item/stack/sheet/mineral/titanium)) + build_with_titanium(tool, user) + return ITEM_INTERACT_SUCCESS /turf/open/openspace/build_with_floor_tiles(obj/item/stack/tile/iron/used_tiles) if(!CanCoverUp()) diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm index 5045e6136ffe..b6cc5368aebd 100644 --- a/code/game/turfs/open/space/space.dm +++ b/code/game/turfs/open/space/space.dm @@ -137,9 +137,11 @@ GLOBAL_LIST_EMPTY(starlight) if(istype(tool, /obj/item/stack/rods)) build_with_rods(tool, user) - else if(ismetaltile(tool)) + return ITEM_INTERACT_SUCCESS + + if(ismetaltile(tool)) build_with_floor_tiles(tool, user) - return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_SUCCESS /turf/open/space/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent) return diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 21fe6b5b01b6..f7f863eecc06 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -254,6 +254,8 @@ . = ..() if(!(flags_1 & INITIALIZED_1)) return + if(!(arrived.flags_1 & INITIALIZED_1)) // If arrived hasn't finished its own Initialize() yet (e.g. during its own creation during mapload), on_atom_inited() will already call enter_hot_spring() for it once + return enter_hot_spring(arrived) /turf/open/water/hot_spring/on_atom_inited(datum/source, atom/movable/movable) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 4836e2370213..91d7c0d0a5c6 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -518,10 +518,9 @@ GLOBAL_LIST_EMPTY(station_turfs) if(L && (L.flags_1 & INITIALIZED_1)) qdel(L) -/turf/proc/Bless() - if(locate(/obj/effect/blessing) in src) - return - new /obj/effect/blessing(src) +/turf/proc/bless_turf(invisible = FALSE) + if(!HAS_TRAIT(src, TRAIT_TURF_BLESSED)) + AddElement(/datum/element/blessed_turf, invisible) ////////////////////////////// //Distance procs @@ -634,10 +633,12 @@ GLOBAL_LIST_EMPTY(station_turfs) /// Check if the heretic is strong enough to rust this turf, and if so, rusts the turf with an added visual effect. /turf/rust_heretic_act(rust_strength = RUST_RESISTANCE_BASIC) if((rust_strength < rust_resistance)) - return + return FALSE if (rust_turf(magic = TRUE)) new /obj/effect/glowing_rune(src) + return TRUE + return FALSE /// Override this to change behaviour when being rusted /turf/proc/rust_turf(magic = FALSE) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 1064e6902af9..31c437252282 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -496,7 +496,7 @@ ADMIN_VERB(give_disease, R_FUN, "Give Disease", ADMIN_VERB_NO_DESCRIPTION, ADMIN log_admin("[key_name(user)] gave [key_name(victim)] the disease [disease].") message_admins(span_adminnotice("[key_name_admin(user)] gave [key_name_admin(victim)] the disease [disease].")) -ADMIN_VERB_AND_CONTEXT_MENU(object_say, R_FUN, "OSay", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/speaker in world) +ADMIN_VERB_AND_CONTEXT_MENU(object_say, R_FUN, "OSay", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/speaker) var/message = tgui_input_text(user, "What do you want the message to be?", "Make Sound", encode = FALSE) if(!message) return @@ -770,109 +770,12 @@ ADMIN_VERB(clear_smart_asset_cache, R_DEBUG, "Clear Smart Asset Cache", "Clear t cleared++ to_chat(user, span_notice("Cleared [cleared] asset\s.")) -ADMIN_VERB(give_ai_speech, R_FUN, "Give Random AI Speech", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/my_guy) - if (isnull(my_guy.ai_controller)) - var/create_controller = tgui_alert(user, "Target has no AI controller, add one?", "Give AI?", list("Yes", "No")) == "Yes" - if (!create_controller) - return - var/run_with_mind = tgui_alert(user, "Run AI controller while the target has a client?", "Override Client?", list("Yes", "No")) - if (isnull(run_with_mind)) - return - if (QDELETED(my_guy)) - to_chat(user, span_warning("Target ceased to exist.")) - return - my_guy.ai_controller = new /datum/ai_controller/basic_controller/talk(my_guy) - if (run_with_mind == "Yes") - var/datum/ai_controller/guy_controller = my_guy.ai_controller - guy_controller.continue_processing_when_client = TRUE - guy_controller.reset_ai_status() - - var/speech_chance - var/list/spoken_lines - var/list/audible_emotes - var/list/visible_emotes - var/list/sounds - - speech_chance = tgui_input_number(user, "Enter chance per second to say something", "Speech Chance", default = 2, min_value = 0, max_value = 100, round_value = FALSE) - if (isnull(speech_chance)) - return - - var/add_another - var/next_line - - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] spoken line?", "Spoken Lines", list("Yes", "No")) - while (add_another == "Yes") - next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "a"] thing spoken out loud.", "Spoken Lines") - if (isnull(next_line)) - return - LAZYADD(spoken_lines, next_line) - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] spoken line?", "Spoken Lines", list("Yes", "No")) - if (isnull(add_another)) - return - - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can hear?", "Audible Emotes", list("Yes", "No")) - while (add_another == "Yes") - next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "an"] emote which people can hear.", "Audible Emotes") - if (isnull(next_line)) - return - LAZYADD(audible_emotes, next_line) - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can hear?", "Audible Emotes", list("Yes", "No")) - if (isnull(add_another)) - return - - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can see?", "Visible Emotes", list("Yes", "No")) - while (add_another == "Yes") - next_line = tgui_input_text(user, "Enter [length(spoken_lines) ? "another" : "an"] emote which people can see.", "Visible Emotes") - if (isnull(next_line)) - return - LAZYADD(visible_emotes, next_line) - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] emote which people can see?", "Visible Emotes", list("Yes", "No")) - if (isnull(add_another)) - return - - if (!length(spoken_lines) && !length(audible_emotes) && !length(visible_emotes)) - return // Well you didn't tell it to say anything... - - if (length(spoken_lines) || length(audible_emotes)) - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] sound to play when doing something audible?", "Sounds", list("Yes", "No")) - while (add_another == "Yes") - next_line = input("", "Select sound",) as null|sound - if (isnull(next_line)) - return - LAZYADD(sounds, next_line) - add_another = tgui_alert(user, "Add [length(spoken_lines) ? "another" : "a"] sound to play when doing something audible?", "Sounds", list("Yes", "No")) - if (isnull(add_another)) - return - - if (QDELETED(my_guy)) - to_chat(user, span_warning("Target stopped existing.")) - return - - var/datum/ai_controller/our_controller = my_guy.ai_controller - if (length(spoken_lines)) - spoken_lines = string_list(spoken_lines) - if (length(audible_emotes)) - audible_emotes = string_list(audible_emotes) - if (length(visible_emotes)) - visible_emotes = string_list(visible_emotes) - - var/list/emotes = list( - BB_EMOTE_SAY = spoken_lines, - BB_EMOTE_HEAR = audible_emotes, - BB_EMOTE_SEE = visible_emotes, - BB_EMOTE_SOUND = sounds, - BB_SPEAK_CHANCE = speech_chance, - ) - our_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, emotes) - - var/behaviour_exists = !!(locate(/datum/ai_planning_subtree/random_speech/blackboard) in our_controller.planning_subtrees) - if (behaviour_exists) - return - our_controller.planning_subtrees = list(GLOB.ai_subtrees[/datum/ai_planning_subtree/random_speech/blackboard]) + our_controller.planning_subtrees - ADMIN_VERB(open_event_logger, R_DEBUG, "Open Event Logger", "Open the event logger interface.", ADMIN_CATEGORY_DEBUG) GLOB.event_logger.ui_interact(user.mob) +ADMIN_VERB(view_behavior_tree, R_DEBUG, "View Behavior Tree", "Inspect the AI behavior tree of a mob.", ADMIN_CATEGORY_DEBUG) + GLOB.bt_viewer.ui_interact(user.mob) + ADMIN_VERB(new_blackmarket_item, R_BUILD, "Create Black Market Item", "Add an item to the black market for purchase.", ADMIN_CATEGORY_EVENTS, object as text) if(!object) to_chat(user, span_boldwarning("Failed! Provide a full or partial typepath!")) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index fdde0aa1cb23..9c14c974664f 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -92,7 +92,7 @@ /obj/effect/fun_balloon/sentience/effect() var/list/bodies = list() for(var/mob/living/possessable in range(effect_range, get_turf(src))) - if (!possessable.ckey && possessable.stat == CONSCIOUS) // Only assign ghosts to living, non-occupied mobs! + if (!possessable.ckey && !IS_UNCONSCIOUS_OR_CRIT(possessable)) // Only assign ghosts to living, non-occupied mobs! bodies += possessable var/list/candidates = SSpolling.poll_ghosts_for_targets( diff --git a/code/modules/admin/smites/knot_shoes.dm b/code/modules/admin/smites/knot_shoes.dm index 0092ec6b2014..82be6a92adc5 100644 --- a/code/modules/admin/smites/knot_shoes.dm +++ b/code/modules/admin/smites/knot_shoes.dm @@ -9,7 +9,7 @@ return var/mob/living/carbon/human/dude = target var/obj/item/clothing/shoes/sick_kicks = dude.shoes - if (!sick_kicks || sick_kicks.fastening_type == SHOES_SLIPON) + if (!istype(sick_kicks) || sick_kicks.fastening_type == SHOES_SLIPON) to_chat(user, span_warning("[dude] does not have knottable shoes!"), confidential = TRUE) return sick_kicks.adjust_laces(SHOES_KNOTTED) diff --git a/code/modules/admin/smites/supply_pod_quick.dm b/code/modules/admin/smites/supply_pod_quick.dm index f222857860e3..ee0e188185a1 100644 --- a/code/modules/admin/smites/supply_pod_quick.dm +++ b/code/modules/admin/smites/supply_pod_quick.dm @@ -13,7 +13,7 @@ user, "Enter typepath of an atom you'd like to send with the pod (type \"empty\" to send an empty pod):", "Typepath", - "/obj/item/food/grown/harebell", + "/obj/item/food/grown/flower/harebell", ) as null|text if (isnull(attempted_target_path)) //The user pressed "Cancel" diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index c46f6012e20f..795f379f47e1 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -703,14 +703,12 @@ var/mob/living/lifer = subject var/status switch (subject.stat) - if(CONSCIOUS) + if(STABLE) status = "Alive" if(SOFT_CRIT) - status = "Dying" - if(UNCONSCIOUS) - status = "Unconscious" + status = "Critical" if(HARD_CRIT) - status = "Unconscious and Dying" + status = "Unconscious" if(DEAD) status = "Dead" health_description = "Status: [status]" diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm index e0709b4a0fb4..eee0d0965325 100644 --- a/code/modules/admin/verbs/admin.dm +++ b/code/modules/admin/verbs/admin.dm @@ -27,7 +27,7 @@ ADMIN_VERB(announce, R_ADMIN, "Announce", "Announce your desires to the world.", log_admin("Announce: [key_name(user)] : [message]") BLACKBOX_LOG_ADMIN_VERB("Announce") -ADMIN_VERB(unprison, R_ADMIN, "UnPrison", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/prisoner in GLOB.mob_list) +ADMIN_VERB(unprison, R_ADMIN, "UnPrison", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/prisoner) if(!is_centcom_level(prisoner.z)) tgui_alert(user, "[prisoner.name] is not prisoned.") return @@ -149,7 +149,7 @@ ADMIN_VERB(cmd_admin_check_player_exp, R_ADMIN, "Player Playtime", "View player /////////////////////////////////////////////////////////////////////////////////////////////// -ADMIN_VERB(drop_everything, R_ADMIN, "Drop Everything", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/dropee in GLOB.mob_list) +ADMIN_VERB(drop_everything, R_ADMIN, "Drop Everything", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/dropee) var/confirm = tgui_alert(user, "Make [dropee] drop everything?", "Message", list("Yes", "No")) if(confirm != "Yes") return diff --git a/code/modules/admin/verbs/adminevents.dm b/code/modules/admin/verbs/adminevents.dm index fcd99fb0ecfc..eef7d16c24e6 100644 --- a/code/modules/admin/verbs/adminevents.dm +++ b/code/modules/admin/verbs/adminevents.dm @@ -1,6 +1,6 @@ // Admin Tab - Event Verbs -ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_subtle_message, R_ADMIN, "Subtle Message", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_subtle_message, R_ADMIN, "Subtle Message", mob/target) message_admins("[key_name_admin(user)] has started answering [ADMIN_LOOKUPFLW(target)]'s prayer.") var/msg = input(user, "Message:", "Subtle PM to [target.key]") as text|null @@ -19,7 +19,7 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_subtle_message, R_ADMIN, "Subtle Message", admin_ticket_log(target, msg) BLACKBOX_LOG_ADMIN_VERB("Subtle Message") -ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_headset_message, R_ADMIN, "Headset Message", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_headset_message, R_ADMIN, "Headset Message", mob/target) user.admin_headset_message(target) /client/proc/admin_headset_message(mob/target in GLOB.mob_list, sender = null) @@ -74,7 +74,7 @@ ADMIN_VERB(cmd_admin_world_narrate, R_ADMIN, "Global Narrate", "Send a direct na message_admins(span_adminnotice("[key_name_admin(user)] Sent a global narrate")) BLACKBOX_LOG_ADMIN_VERB("Global Narrate") -ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_local_narrate, R_ADMIN, "Local Narrate", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, atom/locale in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_local_narrate, R_ADMIN, "Local Narrate", atom/locale) var/range = input(user, "Range:", "Narrate to mobs within how many tiles:", 7) as num|null if(!range) return @@ -89,7 +89,7 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_local_narrate, R_ADMIN, "Local Narrate", A message_admins(span_adminnotice(" LocalNarrate: [key_name_admin(user)] at [ADMIN_VERBOSEJMP(locale)]: [msg]
    ")) BLACKBOX_LOG_ADMIN_VERB("Local Narrate") -ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_direct_narrate, R_ADMIN, "Direct Narrate", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/target) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_direct_narrate, R_ADMIN, "Direct Narrate", mob/target) var/msg = input(user, "Message:", "Enter the text you wish to appear to your target:") as text|null if( !msg ) @@ -115,9 +115,14 @@ ADMIN_VERB(cmd_admin_add_freeform_ai_law, R_ADMIN, "Add Custom AI Law", "Add a c var/show_log = tgui_alert(user, "Show ion message?", "Message", list("Yes", "No")) var/announce_ion_laws = (show_log == "Yes" ? 100 : 0) - var/datum/round_event/ion_storm/add_law_only/ion = new + var/datum/round_event/ion_storm/ion = new ion.announce_chance = announce_ion_laws ion.ionMessage = input + ion.replaceLawsetChance = 0 + ion.removeRandomLawChance = 0 + ion.removeDontImproveChance = 0 + ion.shuffleLawsChance = 0 + ion.botEmagChance = 0 BLACKBOX_LOG_ADMIN_VERB("Add Custom AI Law") diff --git a/code/modules/admin/verbs/adminfun.dm b/code/modules/admin/verbs/adminfun.dm index 883eb6949ef9..0fa1efc54e52 100644 --- a/code/modules/admin/verbs/adminfun.dm +++ b/code/modules/admin/verbs/adminfun.dm @@ -39,7 +39,7 @@ ADMIN_VERB(admin_emp, R_ADMIN|R_FUN, "EM Pulse", ADMIN_VERB_NO_DESCRIPTION, ADMI message_admins("[key_name_admin(user)] created an EM Pulse ([heavy],[light]) at [AREACOORD(orignator)]") BLACKBOX_LOG_ADMIN_VERB("EM Pulse") -ADMIN_VERB(gib_them, R_ADMIN, "Gib", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/victim in GLOB.mob_list) +ADMIN_VERB(gib_them, R_ADMIN, "Gib", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/victim) var/confirm = tgui_alert(user, "Drop a brain?", "Confirm", list("Yes", "No","Cancel")) || "Cancel" if(confirm == "Cancel") return @@ -255,7 +255,7 @@ ADMIN_VERB(mass_modify_traits, R_FUN, "Mass Modify Traits", "Adds or removes a t out += GLOB.admin_visible_traits[key] return out -ADMIN_VERB_AND_CONTEXT_MENU(admin_smite, R_ADMIN|R_FUN, "Smite", "Smite a player with divine power.", ADMIN_CATEGORY_FUN, mob/living/target in world) +ADMIN_VERB_AND_CONTEXT_MENU(admin_smite, R_ADMIN|R_FUN, "Smite", "Smite a player with divine power.", ADMIN_CATEGORY_FUN, mob/living/target) var/punishment = tgui_input_list(user, "Choose a punishment", "DIVINE SMITING", GLOB.smites) if(QDELETED(target) || !punishment) diff --git a/code/modules/admin/verbs/admingame.dm b/code/modules/admin/verbs/admingame.dm index 6413f2fa3def..e7b261a21c53 100644 --- a/code/modules/admin/verbs/admingame.dm +++ b/code/modules/admin/verbs/admingame.dm @@ -1,7 +1,7 @@ ADMIN_VERB(cmd_player_panel, R_ADMIN, "Player Panel", "See all players and their Player Panel.", ADMIN_CATEGORY_GAME) user.holder.player_panel_new() -ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mob/player in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mob/player) log_admin("[key_name(user)] checked the individual player panel for [key_name(player)][isobserver(user.mob)?"":" while in game"].") if(!player) @@ -155,7 +155,7 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mo user << browse(body, "window=adminplayeropts-[REF(player)];size=550x540") BLACKBOX_LOG_ADMIN_VERB("Player Panel") -ADMIN_VERB_ONLY_CONTEXT_MENU(show_occupants_player_panel, R_ADMIN, "Show Occupants PP", obj/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(show_occupants_player_panel, R_ADMIN, "Show Occupants PP", obj/target) var/list/options = list() // Vehicles @@ -225,7 +225,7 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_occupants_player_panel, R_ADMIN, "Show Occupan SSadmin_verbs.dynamic_invoke_verb(user, /datum/admin_verb/show_player_panel, selected_mob) return -GAME_VERB_PROC(/client, cmd_admin_godmode, "Godmode", "Admin.Game", mob/mob in GLOB.mob_list) +GAME_VERB_PROC(/client, cmd_admin_godmode, "Godmode", "Admin.Game", mob/mob) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 00a64df4a6b2..c20e5905d7c3 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -17,13 +17,13 @@ ADMIN_VERB(jump_to_area, R_ADMIN, "Jump To Area", "Jumps to the specified area." message_admins("[key_name_admin(user)] jumped to [AREACOORD(drop_location)]") BLACKBOX_LOG_ADMIN_VERB("Jump To Area") -ADMIN_VERB_AND_CONTEXT_MENU(jump_to_turf, R_ADMIN, "Jump To Turf", "Jump to any turf in the game. This will lag your client.", ADMIN_CATEGORY_GAME, turf/locale in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(jump_to_turf, R_ADMIN, "Jump To Turf", turf/locale) log_admin("[key_name(user)] jumped to [AREACOORD(locale)]") message_admins("[key_name_admin(user)] jumped to [AREACOORD(locale)]") user.mob.abstract_move(locale) BLACKBOX_LOG_ADMIN_VERB("Jump To Turf") -ADMIN_VERB_AND_CONTEXT_MENU(jump_to_mob, R_ADMIN, "Jump To Mob", "Jump to any mob in the game.", ADMIN_CATEGORY_GAME, mob/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(jump_to_mob, R_ADMIN, "Jump To Mob", mob/target) user.mob.abstract_move(target.loc) log_admin("[key_name(user)] jumped to [key_name(target)]") message_admins("[key_name_admin(user)] jumped to [ADMIN_LOOKUPFLW(target)] at [AREACOORD(target)]") @@ -71,7 +71,7 @@ ADMIN_VERB(jump_to_ghost, R_ADMIN, "Jump To Ghost", "Jump your body to your Agho SSadmin_verbs.dynamic_invoke_verb(user, /datum/admin_verb/admin_ghost) BLACKBOX_LOG_ADMIN_VERB("Jump To Ghost") -ADMIN_VERB_AND_CONTEXT_MENU(get_mob, R_ADMIN, "Get Mob", "Teleport a mob to your location.", ADMIN_CATEGORY_GAME, mob/target in world) +ADMIN_VERB_AND_CONTEXT_MENU(get_mob, R_ADMIN, "Get Mob", "Teleport a mob to your location.", ADMIN_CATEGORY_GAME, mob/target) var/atom/loc = get_turf(user.mob) target.admin_teleport(loc) BLACKBOX_LOG_ADMIN_VERB("Get Mob") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index e96fba64e853..dca693429c87 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -13,7 +13,7 @@ // We also make SURE to fail loud, IE: if something stops the message from reaching the recipient, the sender HAS to know // If you "refactor" this to make it "cleaner" I will send you to hell -ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_pm_context, R_NONE, "Admin PM Mob", mob/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_pm_context, R_NONE, "Admin PM Mob", mob/target) if(!ismob(target)) to_chat( src, diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index ce68cf4be717..926ae7df0bb8 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -114,7 +114,7 @@ ADMIN_VERB(cmd_debug_make_powernets, R_DEBUG|R_SERVER, "Make Powernets", "Regene BLACKBOX_LOG_ADMIN_VERB("Make Powernets") ADMIN_VERB_VISIBILITY(cmd_admin_grantfullaccess, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG) -ADMIN_VERB(cmd_admin_grantfullaccess, R_DEBUG, "Grant Full Access", "Grant full access to a mob.", ADMIN_CATEGORY_DEBUG, mob/M in world) +ADMIN_VERB(cmd_admin_grantfullaccess, R_DEBUG, "Grant Full Access", "Grant full access to a mob.", ADMIN_CATEGORY_DEBUG, mob/M) if(!SSticker.HasRoundStarted()) tgui_alert(user, "Wait until the game starts") return @@ -455,7 +455,7 @@ ADMIN_VERB(cmd_admin_areatest_all, R_DEBUG, "Test Areas (ALL)", "Tests the areas return dresscode -ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_rejuvenate, R_ADMIN, "Rejuvenate", mob/living/M in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_rejuvenate, R_ADMIN, "Rejuvenate", mob/living/M) if(!istype(M)) tgui_alert(user,"Cannot revive a ghost") return @@ -467,10 +467,10 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_rejuvenate, R_ADMIN, "Rejuvenate", mob/li admin_ticket_log(M, msg) BLACKBOX_LOG_ADMIN_VERB("Rejuvenate") -ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_delete, R_DEBUG|R_SPAWN, "Delete", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, atom/target as obj|mob|turf in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_delete, R_DEBUG|R_SPAWN, "Delete", atom/target as obj|mob|turf) user.admin_delete(target) -ADMIN_VERB_AND_CONTEXT_MENU(cmd_check_contents, R_ADMIN, "Check Contents", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, mob/living/mob) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_check_contents, R_ADMIN, "Check Contents", mob/living/mob) var/list/mob_contents = mob.get_contents() for(var/content in mob_contents) to_chat(user, "[content] [ADMIN_VV(content)] [ADMIN_TAG(content)]", confidential = TRUE) @@ -629,7 +629,7 @@ ADMIN_VERB(place_ruin, R_DEBUG, "Spawn Ruin", "Attempt to randomly place a speci return var/len = GLOB.ruin_landmarks.len - seedRuins(SSmapping.levels_by_trait(data[2]), max(1, template.cost), data[3], list(ruinname = template)) + seedRuins(SSmapping.levels_by_trait(data[2]), max(1, template.cost), data[3], list(ruinname = template), immediate_load = TRUE) if (GLOB.ruin_landmarks.len > len) var/obj/effect/landmark/ruin/landmark = GLOB.ruin_landmarks[GLOB.ruin_landmarks.len] log_admin("[key_name(user)] randomly spawned ruin [ruinname] at [COORD(landmark)].") diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index c8797a23a829..5dafce71c193 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -1,5 +1,5 @@ ADMIN_VERB_VISIBILITY(debug_air_status, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG) -ADMIN_VERB(debug_air_status, R_DEBUG, "Debug Air Status" , ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, turf/target in world) +ADMIN_VERB(debug_air_status, R_DEBUG, "Debug Air Status" , ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, turf/target) atmos_scan(user.mob, target, silent = TRUE) BLACKBOX_LOG_ADMIN_VERB("Show Air Status") diff --git a/code/modules/admin/verbs/fix_air.dm b/code/modules/admin/verbs/fix_air.dm index 4da06921b929..5ca7aeadcc3a 100644 --- a/code/modules/admin/verbs/fix_air.dm +++ b/code/modules/admin/verbs/fix_air.dm @@ -1,5 +1,5 @@ // Proc taken from yogstation, credit to nichlas0010 for the original -ADMIN_VERB_AND_CONTEXT_MENU(fix_air, R_ADMIN, "Fix Air", "Fixes air in a specified radius.", ADMIN_CATEGORY_GAME, turf/open/locale in world, range = 2 as num) +ADMIN_VERB_AND_CONTEXT_MENU(fix_air, R_ADMIN, "Fix Air", "Fixes air in a specified radius.", ADMIN_CATEGORY_GAME, turf/open/locale, range = 2 as num) message_admins("[key_name_admin(user)] fixed air with range [range] in area [locale.loc.name]") user.mob.log_message("fixed air with range [range] in area [locale.loc.name]", LOG_ADMIN) diff --git a/code/modules/admin/verbs/grant_dna_infusion.dm b/code/modules/admin/verbs/grant_dna_infusion.dm index 28b62662d6be..e39b7efc1b23 100644 --- a/code/modules/admin/verbs/grant_dna_infusion.dm +++ b/code/modules/admin/verbs/grant_dna_infusion.dm @@ -3,7 +3,7 @@ * Returns the entry if all organs were successfully replaced. * If no infusion was picked, the infusion had no organs, or if one or more organs could not be granted, returns FALSE */ -GAME_VERB_PROC(/client, grant_dna_infusion, "Apply DNA Infusion", "Debug", mob/living/carbon/human/target in world) +GAME_VERB_PROC(/client, grant_dna_infusion, "Apply DNA Infusion", "Debug", mob/living/carbon/human/target) var/list/infusions = list() for(var/datum/infuser_entry/path as anything in sort_list(subtypesof(/datum/infuser_entry), GLOBAL_PROC_REF(cmp_typepaths_asc))) diff --git a/code/modules/admin/verbs/jukebox_moderation.dm b/code/modules/admin/verbs/jukebox_moderation.dm index 4b7bf35594c5..23fd8118d67a 100644 --- a/code/modules/admin/verbs/jukebox_moderation.dm +++ b/code/modules/admin/verbs/jukebox_moderation.dm @@ -25,6 +25,7 @@ ADMIN_VERB(upload_jukebox_music, R_SERVER, "Jukebox Upload Music", "Upload a val message_admins("[key_name_admin(user)] uploaded [clean_name] to the jukebox!") to_chat(user, span_notice("Successfully uploaded [clean_name]!")) + refresh_jukebox_songs() ADMIN_VERB(browse_jukebox_music, R_SERVER, "Jukebox Browse Music", "Browse music files for moderation.", ADMIN_CATEGORY_SERVER) var/list/files = flist(CONFIG_JUKEBOX_SOUNDS) @@ -51,7 +52,18 @@ ADMIN_VERB(browse_jukebox_music, R_SERVER, "Jukebox Browse Music", "Browse music message_admins(msg) log_admin(msg) SSblackbox.record_feedback("associative", "jukebox_deletion", 1, list("round_id" = "[GLOB.round_id]", "deletor" = "[key_name_admin(user)]", "deleted" = "[choice]")) + refresh_jukebox_songs() if ("Download") user << ftp(file(path)) else return + +/// Recache the songs from config then force replace all the songs. +/proc/refresh_jukebox_songs() + var/refreshed = FALSE + for(var/obj/machinery/jukebox/jukebox as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/jukebox)) + if(!refreshed) + jukebox.music_player.load_songs_from_config(TRUE) + refreshed = TRUE + + jukebox.music_player.songs = jukebox.music_player.init_songs() diff --git a/code/modules/admin/verbs/lawpanel.dm b/code/modules/admin/verbs/lawpanel.dm index 68a691111be5..833f96309a59 100644 --- a/code/modules/admin/verbs/lawpanel.dm +++ b/code/modules/admin/verbs/lawpanel.dm @@ -20,7 +20,7 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ qdel(src) /datum/law_panel/proc/add_law_helper(mob/living/user, mob/living/silicon/borgo) - var/list/lawtypes = list(LAW_ZEROTH, LAW_HACKED, LAW_ION, LAW_INHERENT, LAW_SUPPLIED) // in order of priority + var/list/lawtypes = list(LAW_ZEROTH, LAW_HACKED, LAW_INHERENT, LAW_SUPPLIED) // in order of priority var/lawtype = tgui_input_list(user, "Select law type", "Law type", lawtypes) if(isnull(lawtype)) return FALSE @@ -40,14 +40,14 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ borgo.laws.set_zeroth_law(lawtext) borgo.laws.protected_zeroth = TRUE - if(LAW_ION) - borgo.laws.add_ion_law(lawtext) if(LAW_HACKED) borgo.laws.add_hacked_law(lawtext) if(LAW_INHERENT) borgo.laws.add_inherent_law(lawtext) if(LAW_SUPPLIED) - borgo.laws.add_supplied_law(length(borgo.laws.supplied), lawtext) // Just goes to the end of the list + borgo.laws.add_supplied_law(lawtext) // Just goes to the end of the list + + log_law_change(user, "added law to [key_name(borgo)] (type: [lawtype], text: [lawtext])") log_admin("[key_name(user)] has UPLOADED a [lawtype] law to [key_name(borgo)] stating: [lawtext]") message_admins("[key_name(user)] has UPLOADED a [lawtype] law to [key_name(borgo)] stating: [lawtext]") return TRUE @@ -74,6 +74,10 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ relevant_laws.Swap(lawindex - 1, lawindex) else CRASH("Invalid direction ([direction]) passed to move_law_helper.") + + log_law_change(user, "moved law for [key_name(borgo)] (type: [LAW_INHERENT], law: [law], direction: [direction])") + log_admin("[key_name(user)] has MOVED a [LAW_INHERENT] law for [key_name(borgo)] [direction]. LAW: [law]") + message_admins("[key_name(user)] has MOVED a [LAW_INHERENT] law for [key_name(borgo)] [direction]. LAW: [law]") return TRUE /datum/law_panel/proc/edit_law_text_helper(mob/living/user, mob/living/silicon/borgo, lawtype, oldlaw) @@ -89,110 +93,41 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ relevant_laws = borgo.laws.supplied if(LAW_HACKED) relevant_laws = borgo.laws.hacked - if(LAW_ION) - relevant_laws = borgo.laws.ion if(LAW_ZEROTH) - borgo.set_zeroth_law(newlaw, announce = FALSE) + borgo.laws.set_zeroth_law(newlaw) borgo.laws.protected_zeroth = TRUE - return TRUE else return FALSE - var/lawindex = relevant_laws.Find(oldlaw) - if(!lawindex) - to_chat(user, span_danger("Something went wrong, we couldn't edit that law.")) - return FALSE + if(lawtype != LAW_ZEROTH) + var/lawindex = relevant_laws.Find(oldlaw) + if(!lawindex) + to_chat(user, span_danger("Something went wrong, we couldn't edit that law.")) + return FALSE + + relevant_laws[lawindex] = newlaw - relevant_laws[lawindex] = newlaw + log_law_change(user, "edited law for [key_name(borgo)] (type: [lawtype], old: [oldlaw], new: [newlaw])") log_admin("[key_name(user)] has EDITED [key_name(borgo)] [lawtype] law. OLD LAW: [oldlaw] \ NEW LAW: [newlaw]") message_admins("[key_name(user)] has EDITED a [lawtype] law on [key_name(borgo)]") return TRUE -/datum/law_panel/proc/edit_law_priority_helper(mob/living/user, mob/living/silicon/borgo, law) - var/old_prio = borgo.laws.supplied.Find(law) - if(!old_prio) - to_chat(user, span_danger("Something went wrong, we couldn't edit that law.")) - return FALSE - - var/new_prio = tgui_input_number(user, "Enter a new priority.", "Edit priority", old_prio, 50, 0) - if(!new_prio || QDELETED(src) || QDELETED(borgo)) - return FALSE - - // Sanity - if(old_prio != borgo.laws.supplied.Find(law)) - to_chat(user, span_danger("[borgo]'s laws may have changed since you have edited priority, please re-try.")) - return FALSE - - // If it's far beyond any existing values, just re-add it normally - if(new_prio > length(borgo.laws.supplied)) - borgo.laws.remove_supplied_law_by_num(old_prio) - borgo.laws.add_supplied_law(new_prio, law) - return TRUE - - // Handle collisions - var/existing_law = borgo.laws.supplied[new_prio] - if(existing_law) - var/list/options = list("Swap", "Move up", "Move down", "Replace", "Cancel") - if(new_prio == 1) - // Nowhere to go from here - options -= "Move down" - - var/swap_or_remove = tgui_alert(user, "There's already a law at that priority level. What should be done to it?", "Existing law", options) - if(swap_or_remove == "Cancel" || !swap_or_remove || QDELETED(src) || QDELETED(borgo)) - return FALSE - // Sanity - if(law != borgo.laws.supplied[old_prio] || existing_law != borgo.laws.supplied[new_prio]) - to_chat(user, span_danger("[borgo]'s laws have changed since you have edited priority, please re-try.")) - return FALSE - - if(swap_or_remove == "Swap") - borgo.laws.supplied.Swap(old_prio, new_prio) - log_admin("[key_name(user)] has SWAPPED [key_name(borgo)] law [old_prio] and [new_prio]") - return TRUE - if(swap_or_remove == "Replace") - borgo.laws.remove_supplied_law_by_num(new_prio, law) - borgo.laws.add_supplied_law(new_prio, law) - log_admin("[key_name(user)] has REPLACED [key_name(borgo)] law: [law] with priority [new_prio]") - return TRUE - - var/new_prio_for_old_law = new_prio + (swap_or_remove == "Move up" ? 1 : -1) - - borgo.laws.remove_supplied_law_by_num(old_prio) - borgo.laws.remove_supplied_law_by_num(new_prio) - borgo.laws.add_supplied_law(new_prio, law) - borgo.laws.add_supplied_law(new_prio_for_old_law, existing_law) - log_admin("[key_name(user)] has changed the priority of an existing law on [key_name(borgo)]. LAW: [law] PRIORITY: [new_prio]") - return TRUE - - // Sanity - if(old_prio != borgo.laws.supplied.Find(law)) - to_chat(user, span_danger("[borgo]'s may laws have changed since you have edited priority, please re-try.")) - return FALSE - - // At this point the slot is free, insert it as normal - borgo.laws.remove_supplied_law_by_num(old_prio) - borgo.laws.add_supplied_law(new_prio, law) - log_admin("[key_name(user)] has UPLOADED a supplied law to [key_name(borgo)] stating: [law]") // Normal insertion, I.E upload - message_admins("[key_name(user)] has UPLOADED a supplied law to [key_name(borgo)] stating: [law]") - return TRUE - /datum/law_panel/proc/remove_law_helper(mob/living/user, mob/living/silicon/borgo, lawtype, law) switch(lawtype) if(LAW_INHERENT) borgo.laws.remove_inherent_law(law) if(LAW_SUPPLIED) - borgo.laws.remove_supplied_law_by_law(law) + borgo.laws.remove_supplied_law(law) if(LAW_HACKED) borgo.laws.remove_hacked_law(law) - if(LAW_ION) - borgo.laws.remove_ion_law(law) if(LAW_ZEROTH) borgo.laws.clear_zeroth_law(force = TRUE) - borgo.laws.protected_zeroth = FALSE else return FALSE + + log_law_change(user, "removed law from [key_name(borgo)] (type: [lawtype], text: [law])") log_admin("[key_name(user)] has REMOVED a law from [key_name(borgo)]. LAW: [law]") message_admins("[key_name(user)] has REMOVED a law from [key_name(borgo)]. LAW: [law]") return TRUE @@ -227,7 +162,7 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ return FALSE if("laws_updated_alert") - borgo.post_lawchange() + borgo.announce_law_change() return FALSE if("give_law_datum") @@ -246,9 +181,6 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ if("edit_law_text") . = edit_law_text_helper(usr, borgo, params["lawtype"], params["law"]) - if("edit_law_prio") - . = edit_law_priority_helper(usr, borgo, params["law"]) - if(. && !QDELETED(borgo)) // One of our functions successfully changed a law // If it was an AI with connected borgs, we should sync @@ -298,11 +230,9 @@ ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_ // zeroth law on top if(lawset.zeroth || lawset.zeroth_borg) UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_ZEROTH, "law" = lawset.zeroth || lawset.zeroth_borg, "num" = 0)) - // then goes ion / hacked + // then goes hacked for(var/law in lawset.hacked) UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_HACKED, "law" = law, "num" = -1)) - for(var/law in lawset.ion) - UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_ION, "law" = law, "num" = -1)) // normie laws var/lawnum = 1 for(var/law in lawset.inherent) diff --git a/code/modules/admin/verbs/machine_upgrade.dm b/code/modules/admin/verbs/machine_upgrade.dm index 7495b383d0e0..2cac1be623e9 100644 --- a/code/modules/admin/verbs/machine_upgrade.dm +++ b/code/modules/admin/verbs/machine_upgrade.dm @@ -1,4 +1,6 @@ -ADMIN_VERB_AND_CONTEXT_MENU(machine_upgrade, R_DEBUG, "Tweak Component Ratings", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/machinery/machine in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(machine_upgrade, R_DEBUG, "Tweak Component Ratings", obj/machinery/machine) + if(!ismachinery(machine)) + return var/new_rating = tgui_input_number(user, "", "Enter new rating:") if(new_rating && machine.component_parts) for(var/obj/item/stock_parts/P in machine.component_parts) diff --git a/code/modules/admin/verbs/manipulate_organs.dm b/code/modules/admin/verbs/manipulate_organs.dm index 59c6e4983d79..ecf63cc5e6f8 100644 --- a/code/modules/admin/verbs/manipulate_organs.dm +++ b/code/modules/admin/verbs/manipulate_organs.dm @@ -1,5 +1,5 @@ ADMIN_VERB_VISIBILITY(manipulate_organs, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG) -ADMIN_VERB(manipulate_organs, R_DEBUG, "Manipulate Organs", "Manipulate the organs of a living carbon.", ADMIN_CATEGORY_DEBUG, mob/living/carbon/carbon_victim in world) +ADMIN_VERB(manipulate_organs, R_DEBUG, "Manipulate Organs", "Manipulate the organs of a living carbon.", ADMIN_CATEGORY_DEBUG, mob/living/carbon/carbon_victim) var/operation = tgui_input_list(user, "Select organ operation", "Organ Manipulation", list("Add organ", "Add implant", "Drop organ/implant", "Remove organ/implant")) if (isnull(operation)) return diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index f445916fed53..c30c3e6d4550 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -46,7 +46,7 @@ ADMIN_VERB(play_local_sound, R_SOUND, "Play Local Sound", "Plays a sound only yo playsound(get_turf(user.mob), sound, volume || 50, FALSE) BLACKBOX_LOG_ADMIN_VERB("Play Local Sound") -ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a sound directly to a mob.", ADMIN_CATEGORY_FUN, sound as sound, mob/target in world) +ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a sound directly to a mob.", ADMIN_CATEGORY_FUN, sound as sound, mob/target) if(!target) target = input(user, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound") as null|anything in sort_names(GLOB.player_list) if(QDELETED(target)) diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm index 47dd6fdc4c3e..4f4fd3d9649a 100644 --- a/code/modules/admin/verbs/possess.dm +++ b/code/modules/admin/verbs/possess.dm @@ -1,5 +1,8 @@ -ADMIN_VERB_AND_CONTEXT_MENU(possess, R_POSSESS, "Possess Obj", "Possess an object.", ADMIN_CATEGORY_FUN, obj/target in world) +ADMIN_VERB_AND_CONTEXT_MENU(possess, R_POSSESS, "Possess Obj", "Possess an object.", ADMIN_CATEGORY_FUN, obj/target) + if(isnull(target.loc)) + return + var/result = user.mob.AddComponent(/datum/component/object_possession, target) if(isnull(result)) // trigger a safety movement just in case we yonk diff --git a/code/modules/admin/verbs/selectequipment.dm b/code/modules/admin/verbs/selectequipment.dm index 5731702e6caa..f67ef904ffc3 100644 --- a/code/modules/admin/verbs/selectequipment.dm +++ b/code/modules/admin/verbs/selectequipment.dm @@ -1,4 +1,4 @@ -ADMIN_VERB_ONLY_CONTEXT_MENU(select_equipment, R_FUN, "Select Equipment", mob/target in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(select_equipment, R_FUN, "Select Equipment", mob/target) var/datum/select_equipment/ui = new(user, target) ui.ui_interact(user.mob) @@ -28,6 +28,10 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(select_equipment, R_FUN, "Select Equipment", mob/ta var/datum/outfit/selected_outfit = /datum/outfit //serializable string for the UI to keep track of which outfit is selected var/selected_identifier = "/datum/outfit" + /// Cached flat icon of the dummy wearing selected_outfit, so ui_data() doesn't redraw the sprite on every refresh when it doesn't need to + var/icon/cached_dummy_icon + /// The selected_identifier cached_dummy_icon was last rendered for. + var/cached_for_identifier /datum/select_equipment/New(_user, mob/target) user = CLIENT_FROM_VAR(_user) @@ -105,12 +109,16 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(select_equipment, R_FUN, "Select Equipment", mob/ta if(!dummy_key) init_dummy() - var/icon/dummysprite = get_flat_human_icon(null, - dummy_key = dummy_key, - outfit_override = selected_outfit, - no_anim = TRUE, - ) - data["icon64"] = icon2base64(dummysprite) + if(isnull(cached_dummy_icon) || cached_for_identifier != selected_identifier) + cached_dummy_icon = get_flat_human_icon(null, + dummy_key = dummy_key, + showDirs = list(SOUTH), + outfit_override = selected_outfit, + no_anim = TRUE, + ) + cached_for_identifier = selected_identifier + + data["icon64"] = icon2base64(cached_dummy_icon) data["name"] = target_mob var/datum/preferences/prefs = user?.client?.prefs diff --git a/code/modules/admin/view_variables/mark_datum.dm b/code/modules/admin/view_variables/mark_datum.dm index f8f9977e6353..a71e5909db41 100644 --- a/code/modules/admin/view_variables/mark_datum.dm +++ b/code/modules/admin/view_variables/mark_datum.dm @@ -10,7 +10,7 @@ holder.RegisterSignal(holder.marked_datum, COMSIG_QDELETING, TYPE_PROC_REF(/datum/admins, handle_marked_del)) vv_update_display(D, "marked", VV_MSG_MARKED) -ADMIN_VERB_ONLY_CONTEXT_MENU(mark_datum, R_NONE, "Mark Object", datum/target as mob|obj|turf|area in view()) +ADMIN_VERB_ONLY_CONTEXT_MENU(mark_datum, R_NONE, "Mark Object", datum/target as anything) user.mark_datum(target) /datum/admins/proc/handle_marked_del(datum/source) diff --git a/code/modules/admin/view_variables/tag_datum.dm b/code/modules/admin/view_variables/tag_datum.dm index b4ca42860c34..030f245bbe5a 100644 --- a/code/modules/admin/view_variables/tag_datum.dm +++ b/code/modules/admin/view_variables/tag_datum.dm @@ -12,5 +12,5 @@ else holder.add_tagged_datum(target_datum) -ADMIN_VERB_ONLY_CONTEXT_MENU(tag_datum, R_NONE, "Tag Datum", datum/target_datum as mob|obj|turf|area in view()) +ADMIN_VERB_ONLY_CONTEXT_MENU(tag_datum, R_NONE, "Tag Datum", datum/target_datum as anything) user.tag_datum(target_datum) diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 18237e09e19c..3259ccf4c221 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -1,7 +1,7 @@ #define ICON_STATE_CHECKED 1 /// this dmi is checked. We don't check this one anymore. #define ICON_STATE_NULL 2 /// this dmi has null-named icon_state, allowing it to show a sprite on vv editor. -ADMIN_VERB_ONLY_CONTEXT_MENU(debug_variables, R_NONE, "View Variables", datum/thing in world) +ADMIN_VERB_ONLY_CONTEXT_MENU(debug_variables, R_NONE, "View Variables", datum/thing as anything) user.debug_variables(thing) // This is kept as a separate proc because admins are able to show VV to non-admins diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 24d04bb65be2..f6cf16e7f908 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -79,7 +79,7 @@ if(marked == target) to_chat(user, span_warning("This specimen is already marked!")) return - if(isabductor(target) || iscow(target)) + if(HAS_TRAIT(target, TRAIT_ABDUCTOR_QUICK_SCAN)) marked_target_weakref = WEAKREF(target) to_chat(user, span_notice("You mark [target] for future retrieval.")) else @@ -241,7 +241,7 @@ fail_message = span_abductor("Firing error, please contact Command.") /obj/item/firing_pin/abductor/pin_auth(mob/living/user) - . = isabductor(user) + return HAS_MIND_TRAIT(user, TRAIT_ABDUCTOR_KNOWLEDGE) /obj/item/gun/energy/alien name = "alien pistol" diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm index 96ba94170c89..7360e32e7b3a 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm @@ -12,15 +12,15 @@ poster_item_icon_state = "rolled_abductor" /obj/structure/sign/poster/abductor/tear_poster(mob/user) - if(!isabductor(user)) + if(!HAS_MIND_TRAIT(user, TRAIT_ABDUCTOR_KNOWLEDGE)) balloon_alert(user, "it won't budge!") return return ..() -/obj/structure/sign/poster/abductor/attackby(obj/item/tool, mob/user, list/modifiers, list/attack_modifiers) +/obj/structure/sign/poster/abductor/wirecutter_act(mob/living/user, obj/item/tool) if(tool.toolspeed >= 0.2) balloon_alert(user, "tool too weak!") - return FALSE + return ITEM_INTERACT_BLOCKING return ..() /obj/structure/sign/poster/abductor/random diff --git a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm index d312a4409d06..79267a87b781 100644 --- a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm +++ b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm @@ -38,7 +38,7 @@ if(!ishuman(target_mob)) continue var/mob/living/carbon/human/target_human = target_mob - if(target_human.stat) + if(IS_UNCONSCIOUS_OR_CRIT(target_human)) continue if(HAS_MIND_TRAIT(target_human, TRAIT_UNCONVERTABLE)) diff --git a/code/modules/antagonists/abductor/equipment/glands/quantum.dm b/code/modules/antagonists/abductor/equipment/glands/quantum.dm index 1991287981c0..bbabfcfc214a 100644 --- a/code/modules/antagonists/abductor/equipment/glands/quantum.dm +++ b/code/modules/antagonists/abductor/equipment/glands/quantum.dm @@ -32,7 +32,7 @@ /obj/item/organ/heart/gland/quantum/mind_control(command, mob/living/user) if(..()) - if(entangled_mob && ishuman(entangled_mob) && (entangled_mob.stat < DEAD)) + if(entangled_mob && ishuman(entangled_mob) && (entangled_mob.stat != DEAD)) to_chat(entangled_mob, span_userdanger("You suddenly feel an irresistible compulsion to follow an order...")) to_chat(entangled_mob, span_mind_control("[command]")) var/atom/movable/screen/alert/mind_control/mind_alert = entangled_mob.throw_alert(ALERT_MIND_CONTROL, /atom/movable/screen/alert/mind_control) diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm index dd3abe6994ac..7f9fb9ca15f2 100644 --- a/code/modules/antagonists/abductor/machinery/console.dm +++ b/code/modules/antagonists/abductor/machinery/console.dm @@ -72,7 +72,7 @@ TeleporterSend() /obj/machinery/abductor/console/ui_status(mob/user, datum/ui_state/state) - if(!isabductor(user) && !isobserver(user)) + if(!HAS_MIND_TRAIT(user, TRAIT_ABDUCTOR_KNOWLEDGE) && !isobserver(user)) return UI_CLOSE return ..() @@ -266,13 +266,16 @@ vest = V return TRUE -/obj/machinery/abductor/console/attackby(obj/O, mob/user, list/modifiers, list/attack_modifiers) - if(istype(O, /obj/item/abductor/gizmo) && AddGizmo(O)) +/obj/machinery/abductor/console/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/abductor/gizmo) && AddGizmo(tool)) to_chat(user, span_notice("You link the tool to the console.")) - else if(istype(O, /obj/item/clothing/suit/armor/abductor/vest) && AddVest(O)) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/clothing/suit/armor/abductor/vest) && AddVest(tool)) to_chat(user, span_notice("You link the vest to the console.")) - else - return ..() + return ITEM_INTERACT_SUCCESS + + return NONE /obj/machinery/abductor/console/proc/Dispense(items_list, cost=1) if(experiment && experiment.credits >= cost) diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm index bd6cc36e5ba3..a8e66ace494e 100644 --- a/code/modules/antagonists/abductor/machinery/dispenser.dm +++ b/code/modules/antagonists/abductor/machinery/dispenser.dm @@ -23,7 +23,7 @@ amounts[i] = rand(1,5) /obj/machinery/abductor/gland_dispenser/ui_status(mob/user, datum/ui_state/state) - if(!isabductor(user) && !isobserver(user)) + if(!HAS_MIND_TRAIT(user, TRAIT_ABDUCTOR_KNOWLEDGE) && !isobserver(user)) return UI_CLOSE return ..() @@ -61,15 +61,17 @@ Dispense(gland_id) return TRUE -/obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - if(istype(W, /obj/item/organ/heart/gland)) - if(!user.transferItemToLoc(W, src)) - return - for(var/i in 1 to gland_colors.len) - if(gland_types[i] == W.type) - amounts[i]++ - else - return ..() +/obj/machinery/abductor/gland_dispenser/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/organ/heart/gland)) + return NONE + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + + for(var/slot in 1 to gland_colors.len) + if(gland_types[slot] == tool.type) + amounts[slot]++ + return ITEM_INTERACT_SUCCESS /obj/machinery/abductor/gland_dispenser/proc/Dispense(count) if(amounts[count]>0) diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm index 324ec4381e17..2cc52bed914c 100644 --- a/code/modules/antagonists/abductor/machinery/experiment.dm +++ b/code/modules/antagonists/abductor/machinery/experiment.dm @@ -24,7 +24,7 @@ return ..() /obj/machinery/abductor/experiment/mouse_drop_receive(mob/target, mob/user, params) - if(!ishuman(target) || isabductor(target)) + if(!ishuman(target) || HAS_MIND_TRAIT(target, TRAIT_ABDUCTOR_KNOWLEDGE)) return close_machine(target) @@ -33,14 +33,14 @@ ..() /obj/machinery/abductor/experiment/close_machine(mob/target, density_to_set = TRUE) - for(var/A in loc) - if(isabductor(A)) + for(var/mob/living/mob in loc) + if(HAS_MIND_TRAIT(mob, TRAIT_ABDUCTOR_KNOWLEDGE)) return if(state_open && !panel_open) ..(target) /obj/machinery/abductor/experiment/relaymove(mob/living/user, direction) - if(user.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return if(message_cooldown <= world.time) message_cooldown = world.time + 50 @@ -53,7 +53,7 @@ span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \ span_hear("You hear a metallic creaking from [src].")) if(do_after(user,(breakout_time), target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || state_open) return user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ span_notice("You successfully break out of [src]!")) diff --git a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm index c69931f91439..e85d9828c2b3 100644 --- a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm +++ b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm @@ -27,7 +27,7 @@ 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(ishuman(exposed_mob)) - if(exposed_mob.stat == UNCONSCIOUS || exposed_mob.stat == HARD_CRIT) + if(IS_UNCONSCIOUS_AND_ALIVE(exposed_mob)) 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(5)) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index a5a9ff6a694c..29cd71db1af2 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -174,6 +174,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) if(announcement_time && (world.time >= announcement_time || blobs_legit.len >= announcement_size) && !has_announced) priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", ANNOUNCER_OUTBREAK5) + SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_TOMBSTONE] = TRUE // Set status displays to biohazard alert send_status_display_biohazard_alert() diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index b2cf3808eff1..6939c4d27c4c 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -353,7 +353,7 @@ for(var/mob/living/basic/blob_mob as anything in blob_mobs) if(!isturf(blob_mob.loc) || get_dist(blob_mob, tile) > 35 || blob_mob.key) continue - blob_mob.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + blob_mob.ai_controller.clear_blackboard_key(BB_CURRENT_TARGET) blob_mob.ai_controller.set_blackboard_key(BB_TRAVEL_DESTINATION, pick(surrounding_turfs)) /** Opens the reroll menu to change strains */ diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index 8bde78251ff5..340754275506 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -257,19 +257,17 @@ /obj/structure/blob/hulk_damage() return 15 -/obj/structure/blob/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(I.tool_behaviour == TOOL_ANALYZER) - user.changeNext_move(CLICK_CD_MELEE) - to_chat(user, "The analyzer beeps once, then reports:
    ") - SEND_SOUND(user, sound('sound/machines/ping.ogg')) - if(overmind) - to_chat(user, "Progress to Critical Mass: [span_notice("[overmind.blobs_legit.len]/[overmind.blobwincount].")]") - to_chat(user, chemeffectreport(user).Join("\n")) - else - to_chat(user, "Blob core neutralized. Critical mass no longer attainable.") - to_chat(user, typereport(user).Join("\n")) +/obj/structure/blob/analyzer_act(mob/living/user, obj/item/analyzer/tool) + user.changeNext_move(CLICK_CD_MELEE) + to_chat(user, "The analyzer beeps once, then reports:
    ") + SEND_SOUND(user, sound('sound/machines/ping.ogg')) + if(overmind) + to_chat(user, "Progress to Critical Mass: [span_notice("[overmind.blobs_legit.len]/[overmind.blobwincount].")]") + to_chat(user, chemeffectreport(user).Join("\n")) else - return ..() + to_chat(user, "Blob core neutralized. Critical mass no longer attainable.") + to_chat(user, typereport(user).Join("\n")) + return ITEM_INTERACT_SUCCESS /obj/structure/blob/proc/chemeffectreport(mob/user) RETURN_TYPE(/list) diff --git a/code/modules/antagonists/blood_worm/actions/spit_blood.dm b/code/modules/antagonists/blood_worm/actions/spit_blood.dm index fc765c009063..06f80268d8b6 100644 --- a/code/modules/antagonists/blood_worm/actions/spit_blood.dm +++ b/code/modules/antagonists/blood_worm/actions/spit_blood.dm @@ -150,7 +150,7 @@ if (host.wear_suit?.breakouttime) something_to_melt = TRUE melted_something |= melt_restraints_in_slot(host, ITEM_SLOT_OCLOTHING) - if (host.shoes?.tied == SHOES_KNOTTED) + if (astype(host.get_item_by_slot(ITEM_SLOT_FEET), /obj/item/clothing/shoes)?.tied == SHOES_KNOTTED) something_to_melt = TRUE melted_something |= melt_restraints_in_slot(host, ITEM_SLOT_FEET) if (istype(host.loc, /obj/structure/closet)) diff --git a/code/modules/antagonists/blood_worm/blood_worm_mob.dm b/code/modules/antagonists/blood_worm/blood_worm_mob.dm index 4d647d7740ad..fa9a3d2df608 100644 --- a/code/modules/antagonists/blood_worm/blood_worm_mob.dm +++ b/code/modules/antagonists/blood_worm/blood_worm_mob.dm @@ -206,7 +206,7 @@ /mob/living/basic/blood_worm/set_stat(new_stat) . = ..() - if (host && stat != CONSCIOUS) + if (host && IS_UNCONSCIOUS_OR_CRIT(src)) leave_host() /mob/living/basic/blood_worm/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index 298a51ef40f5..7f3cbe1225fb 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -70,7 +70,11 @@ if (flashed.stat == DEAD || issilicon(flashed) || isdrone(flashed)) return - if (flashed.stat != CONSCIOUS) + if (flashed.stat != STABLE) + flashed.balloon_alert(source, "in critical!") + return + + if (IS_UNCONSCIOUS(flashed)) flashed.balloon_alert(source, "unconscious!") return @@ -94,7 +98,7 @@ return if (HAS_MIND_TRAIT(flashed, TRAIT_UNCONVERTABLE)) - flashed.balloon_alert(source, "[flashed.p_they()] resist!") + flashed.balloon_alert(source, "[flashed.p_they()] resist[flashed.p_s()]!") return if (!team.add_brother(flashed, key_name(source))) // Shouldn't happen given the former, more specific checks but just in case diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 4d5099f40a3e..279bc3032c69 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -304,7 +304,7 @@ // nothing to handle if(!chosen_sting) return - if(!isliving(ling) || clicked == ling || ling.stat != CONSCIOUS) + if(!isliving(ling) || clicked == ling || IS_UNCONSCIOUS_OR_CRIT(ling)) return // sort-of hack done here: we use in_given_range here because it's quicker. // actual ling stings do pathfinding to determine whether the target's "in range". diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm index 9cee60fbfe32..9ceef3b07cd2 100644 --- a/code/modules/antagonists/changeling/changeling_power.dm +++ b/code/modules/antagonists/changeling/changeling_power.dm @@ -26,9 +26,9 @@ /// Similar to req_dna, but only gained from absorbing, not DNA sting var/req_absorbs = 0 /// Maximum stat before the ability is blocked. - /// For example, `UNCONSCIOUS` prevents it from being used when in hard crit or dead, - /// while `DEAD` allows the ability to be used on any stat values. - var/req_stat = CONSCIOUS + /// For example, `STABLE` can only be used while not in crit, and + /// `DEAD` allows the ability to be used on any stat values. + var/req_stat = STABLE /// usable when the changeling is in death coma var/ignores_fakedeath = FALSE /// used by a few powers that toggle @@ -102,7 +102,7 @@ the same goes for Remove(). if you override Remove(), call parent or else your p if(changeling.true_absorbs < req_absorbs) user.balloon_alert(user, "needs [req_absorbs] absorption\s!") return FALSE - if(req_stat < user.stat) + if(req_stat < IS_UNCONSCIOUS_OR_CRIT(user) || (req_stat == STABLE && IS_UNCONSCIOUS(user))) user.balloon_alert(user, "incapacitated!") return FALSE if(HAS_TRAIT(user, TRAIT_DEATHCOMA) && !ignores_fakedeath) diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm index ec28fc2866f1..f92a795e045d 100644 --- a/code/modules/antagonists/changeling/powers/absorb.dm +++ b/code/modules/antagonists/changeling/powers/absorb.dm @@ -162,7 +162,7 @@ target.take_overall_damage(40) SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("Absorb DNA", "[absorbing_iteration]")) - if(!do_after(owner, 15 SECONDS, target, hidden = TRUE)) + if(!do_after(owner, 15 SECONDS, target, cog_icon = null)) owner.balloon_alert(owner, "interrupted!") qdel(absorbing_loop) is_absorbing = FALSE diff --git a/code/modules/antagonists/changeling/powers/adrenaline.dm b/code/modules/antagonists/changeling/powers/adrenaline.dm index a88195849d95..d47caa7ca316 100644 --- a/code/modules/antagonists/changeling/powers/adrenaline.dm +++ b/code/modules/antagonists/changeling/powers/adrenaline.dm @@ -7,7 +7,7 @@ chemical_cost = 25 // similar cost to biodegrade, as they serve similar purposes dna_cost = 2 req_human = FALSE - req_stat = CONSCIOUS + req_stat = STABLE disabled_by_fire = TRUE //Recover from stuns. diff --git a/code/modules/antagonists/changeling/powers/mmi_talk.dm b/code/modules/antagonists/changeling/powers/mmi_talk.dm index c70896c9797b..483072265432 100644 --- a/code/modules/antagonists/changeling/powers/mmi_talk.dm +++ b/code/modules/antagonists/changeling/powers/mmi_talk.dm @@ -114,7 +114,7 @@ return FALSE var/obj/item/mmi/mmi = brain_ref.loc - if(mmi.brainmob.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(mmi.brainmob)) to_chat(usr, span_warning("Our decoy brain is too damaged to speak.")) else // Say will perform input sanitization and such for us diff --git a/code/modules/antagonists/changeling/powers/strained_muscles.dm b/code/modules/antagonists/changeling/powers/strained_muscles.dm index 7972c2473b76..40d8208e77f0 100644 --- a/code/modules/antagonists/changeling/powers/strained_muscles.dm +++ b/code/modules/antagonists/changeling/powers/strained_muscles.dm @@ -41,7 +41,7 @@ return user.add_movespeed_modifier(/datum/movespeed_modifier/strained_muscles) - if(user.stat != CONSCIOUS || user.staminaloss >= 90) + if(IS_UNCONSCIOUS_OR_CRIT(user) || user.staminaloss >= 90) active = !active to_chat(user, span_notice("Our muscles relax without the energy to strengthen them.")) user.Paralyze(40) diff --git a/code/modules/antagonists/clown_ops/clown_weapons.dm b/code/modules/antagonists/clown_ops/clown_weapons.dm index 73c639c16a13..5a5baa90c851 100644 --- a/code/modules/antagonists/clown_ops/clown_weapons.dm +++ b/code/modules/antagonists/clown_ops/clown_weapons.dm @@ -62,13 +62,17 @@ var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery) slipper.Slip(src, hit_atom) -/obj/item/melee/energy/sword/bananium/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers) - if(COOLDOWN_FINISHED(src, next_trombone_allowed) && istype(weapon, /obj/item/melee/energy/sword/bananium)) - COOLDOWN_START(src, next_trombone_allowed, 5 SECONDS) - to_chat(user, span_warning("You slap the two swords together. Sadly, they do not seem to fit!")) - playsound(src, 'sound/misc/sadtrombone.ogg', 50) - return TRUE - return ..() +/obj/item/melee/energy/sword/bananium/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/melee/energy/sword/bananium)) + return ..() + + if(!COOLDOWN_FINISHED(src, next_trombone_allowed)) + return ITEM_INTERACT_BLOCKING + + COOLDOWN_START(src, next_trombone_allowed, 5 SECONDS) + to_chat(user, span_warning("You slap the two swords together. Sadly, they do not seem to fit!")) + playsound(src, 'sound/misc/sadtrombone.ogg', 50) + return ITEM_INTERACT_SUCCESS /obj/item/melee/energy/sword/bananium/suicide_act(mob/living/user) if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 0be336ff584d..b5b7b6966a30 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -194,7 +194,7 @@ Striking a noncultist, however, will tear their flesh."} // Void PATH_VOID = list( WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/void_phase), - SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/void_prison), + SWORD_SPELLS = list(/datum/action/cooldown/spell/aoe/void_pull), SWORD_PREFIX = "tenebrous", ), // Blade @@ -377,8 +377,6 @@ Striking a noncultist, however, will tear their flesh."} /obj/item/melee/cultblade/haunted/Initialize(mapload, mob/soul_to_bind, mob/awakener, do_bind = TRUE) . = ..() - - AddElement(/datum/element/heretic_focus) add_traits(list(TRAIT_CASTABLE_LOC, TRAIT_SPELLS_TRANSFER_TO_LOC), INNATE_TRAIT) if(do_bind && !mapload) bind_soul(soul_to_bind, awakener) @@ -425,10 +423,6 @@ Striking a noncultist, however, will tear their flesh."} bozo.objectives |= copied_objectives trapped_entity.mind.add_antag_datum(bozo) - // Assigning the spells to give to the wielder and spirit. - // Let them cast the given spell. - ADD_TRAIT(trapped_entity, TRAIT_ALLOW_HERETIC_CASTING, INNATE_TRAIT) - var/list/path_spells = heretic_paths_to_haunted_sword_abilities[heretic_path] var/list/wielder_spells = path_spells[WIELDER_SPELLS] @@ -1048,33 +1042,41 @@ Striking a noncultist, however, will tear their flesh."} playsound(src, 'sound/effects/magic/exit_blood.ogg', 75, TRUE) new /obj/effect/temp_visual/dir_setting/cult/phase(user.loc, user.dir) var/turf/temp_target = get_turf_in_angle(set_angle, targets_from, 40) - for(var/turf/T in get_line(targets_from,temp_target)) - if (locate(/obj/effect/blessing, T)) - temp_target = T - playsound(T, 'sound/effects/parry.ogg', 50, TRUE) - new /obj/effect/temp_visual/at_shield(T, T) + for(var/turf/target_turf in get_line(targets_from,temp_target)) + if (HAS_TRAIT(target_turf, TRAIT_TURF_BLESSED)) + temp_target = target_turf + playsound(target_turf, 'sound/effects/parry.ogg', 50, TRUE) + new /obj/effect/temp_visual/at_shield(target_turf, target_turf) break - T.narsie_act(TRUE, TRUE) - for(var/mob/living/target in T.contents) - if(IS_CULTIST(target)) - new /obj/effect/temp_visual/cult/sparks(T) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(H.stat != DEAD) - H.reagents.add_reagent(/datum/reagent/fuel/unholywater, 7) - if(isshade(target) || isconstruct(target)) - var/mob/living/basic/construct/healed_guy = target - if(healed_guy.health + 15 < healed_guy.maxHealth) - healed_guy.adjust_health(-15) - else - healed_guy.health = healed_guy.maxHealth + + target_turf.narsie_act(TRUE, TRUE) + for(var/mob/living/target in target_turf) + if(!IS_CULTIST(target)) + var/mob/living/victim = target + if(!victim.density) + continue + victim.Paralyze(20) + victim.adjust_brute_loss(45) + playsound(victim, 'sound/effects/hallucinations/wail.ogg', 50, TRUE) + victim.emote("scream") + continue + + new /obj/effect/temp_visual/cult/sparks(target_turf) + if(ishuman(target)) + var/mob/living/carbon/human/cultie = target + if(cultie.stat != DEAD) + cultie.reagents.add_reagent(/datum/reagent/fuel/unholywater, 7) + continue + + if(!isshade(target) && !isconstruct(target)) + continue + + var/mob/living/basic/construct/healed_guy = target + if(healed_guy.health + 15 < healed_guy.maxHealth) + healed_guy.adjust_health(-15) else - var/mob/living/L = target - if(L.density) - L.Paralyze(20) - L.adjust_brute_loss(45) - playsound(L, 'sound/effects/hallucinations/wail.ogg', 50, TRUE) - L.emote("scream") + healed_guy.health = healed_guy.maxHealth + user.Beam(temp_target, icon_state="blood_beam", time = 7, beam_type = /obj/effect/ebeam/blood) diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm index cb853d1b358d..7b293830bb84 100644 --- a/code/modules/antagonists/cult/datums/cultist.dm +++ b/code/modules/antagonists/cult/datums/cultist.dm @@ -14,6 +14,8 @@ var/give_equipment = FALSE ///Reference to the Blood cult team they are part of. var/datum/team/cult/cult_team + ///List of cult recipe typepaths learned (Autogenerated based on CAT_CULT recipes) + var/static/list/cult_recipes /datum/antagonist/cult/can_be_owned(datum/mind/new_owner) if(!is_convertable_to_cult(new_owner.current, cult_team)) @@ -39,7 +41,19 @@ current.log_message("has been converted to the cult of Nar'Sie!", LOG_ATTACK, color=COLOR_CULT_RED) + if(isnull(cult_recipes)) + cult_recipes = list() + for(var/datum/crafting_recipe/recipe_type as anything in typesof(/datum/crafting_recipe)) + if(recipe_type::category == CAT_CULT) + cult_recipes += recipe_type + + for(var/recipe_type in cult_recipes) + owner.teach_crafting_recipe(recipe_type) + /datum/antagonist/cult/on_removal() + for(var/recipe_type in cult_recipes) + owner.forget_crafting_recipe(recipe_type) + if (!owner.current) return ..() diff --git a/code/modules/antagonists/cult/rune_spawn_action.dm b/code/modules/antagonists/cult/rune_spawn_action.dm index be0e1e88ff8e..481add6ebaf0 100644 --- a/code/modules/antagonists/cult/rune_spawn_action.dm +++ b/code/modules/antagonists/cult/rune_spawn_action.dm @@ -38,48 +38,58 @@ /datum/action/innate/cult/create_rune/Activate() var/turf/T = get_turf(owner) - if(turf_check(T)) - var/chosen_keyword - if(initial(rune_type.req_keyword)) - chosen_keyword = tgui_input_text(owner, "Enter a keyword for the new rune.", "Words of Power", max_length = MAX_NAME_LEN) - if(!chosen_keyword || !turf_check(T)) - return - //the outer ring is always the same across all runes - var/obj/effect/temp_visual/cult/rune_spawn/R1 = new(T, scribe_time, rune_color) - //the rest are not always the same, so we need types for em - var/obj/effect/temp_visual/cult/rune_spawn/R2 - if(ispath(rune_word_type, /obj/effect/temp_visual/cult/rune_spawn)) - R2 = new rune_word_type(T, scribe_time, rune_color) - var/obj/effect/temp_visual/cult/rune_spawn/R3 - if(ispath(rune_innerring_type, /obj/effect/temp_visual/cult/rune_spawn)) - R3 = new rune_innerring_type(T, scribe_time, rune_color) - var/obj/effect/temp_visual/cult/rune_spawn/R4 - if(ispath(rune_center_type, /obj/effect/temp_visual/cult/rune_spawn)) - R4 = new rune_center_type(T, scribe_time, rune_color) + if(!turf_check(T)) + return + var/chosen_keyword + if(initial(rune_type.req_keyword)) + chosen_keyword = tgui_input_text(owner, "Enter a keyword for the new rune.", "Words of Power", max_length = MAX_NAME_LEN) + if(!chosen_keyword || !turf_check(T)) + return +//the outer ring is always the same across all runes + var/obj/effect/temp_visual/cult/rune_spawn/R1 = new(T, scribe_time, rune_color) +//the rest are not always the same, so we need types for em + var/obj/effect/temp_visual/cult/rune_spawn/R2 + if(ispath(rune_word_type, /obj/effect/temp_visual/cult/rune_spawn)) + R2 = new rune_word_type(T, scribe_time, rune_color) + var/obj/effect/temp_visual/cult/rune_spawn/R3 + if(ispath(rune_innerring_type, /obj/effect/temp_visual/cult/rune_spawn)) + R3 = new rune_innerring_type(T, scribe_time, rune_color) + var/obj/effect/temp_visual/cult/rune_spawn/R4 + if(ispath(rune_center_type, /obj/effect/temp_visual/cult/rune_spawn)) + R4 = new rune_center_type(T, scribe_time, rune_color) - cooldown = base_cooldown + world.time - owner.update_mob_action_buttons() - addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob, update_mob_action_buttons)), base_cooldown + 1) - var/list/health - if(damage_interrupt && isliving(owner)) - var/mob/living/L = owner - health = list("health" = L.health) - var/scribe_mod = scribe_time - if(istype(T, /turf/open/floor/engine/cult)) - scribe_mod *= 0.5 - playsound(T, 'sound/effects/magic/enter_blood.ogg', 100, FALSE) - if(do_after(owner, scribe_mod, target = owner, extra_checks = CALLBACK(owner, TYPE_PROC_REF(/mob, break_do_after_checks), health, action_interrupt))) - new rune_type(owner.loc, chosen_keyword) - else - qdel(R1) - if(R2) - qdel(R2) - if(R3) - qdel(R3) - if(R4) - qdel(R4) - cooldown = 0 - owner.update_mob_action_buttons() + cooldown = base_cooldown + world.time + owner.update_mob_action_buttons() + addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob, update_mob_action_buttons)), base_cooldown + 1) + var/list/mob_health = null + if(damage_interrupt && isliving(owner)) + var/mob/living/as_living = owner + mob_health = list(as_living.health) + var/scribe_mod = scribe_time + if(istype(T, /turf/open/floor/engine/cult)) + scribe_mod *= 0.5 + playsound(T, 'sound/effects/magic/enter_blood.ogg', 100, FALSE) + + if(do_after(owner, scribe_mod, owner, action_interrupt ? DO_AFTER_CHECK_NEXT_MOVE : NONE, extra_checks = !isnull(mob_health) ? CALLBACK(src, PROC_REF(check_health), owner, mob_health) : null)) + new rune_type(owner.loc, chosen_keyword) + return + + qdel(R1) + if(R2) + qdel(R2) + if(R3) + qdel(R3) + if(R4) + qdel(R4) + cooldown = 0 + owner.update_mob_action_buttons() + +/datum/action/innate/cult/create_rune/proc/check_health(mob/living/user, old_health) + if(user.health < old_health[1]) + return FALSE + + old_health[1] = user.health + return TRUE //teleport rune /datum/action/innate/cult/create_rune/tele diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 33353150a85d..986ca91c6da2 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -158,7 +158,7 @@ structure_check() searches for nearby cultist structures required for the invoca continue if(!cultist.can_speak(allow_mimes = TRUE)) continue - if(cultist.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(cultist)) continue invokers += cultist @@ -1023,7 +1023,7 @@ GLOBAL_VAR_INIT(narsie_summon_count, 0) to_chat(new_human, span_cult_italic("You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar'Sie, and you are to serve them at all costs.")) while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T)) - if(user.stat != CONSCIOUS || HAS_TRAIT(new_human, TRAIT_CRITICAL_CONDITION)) + if(IS_UNCONSCIOUS_OR_CRIT(user)) break user.apply_damage(0.1, BRUTE) sleep(0.1 SECONDS) diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm index 56fbb918952c..4e9ef0b4f81e 100644 --- a/code/modules/antagonists/heretic/heretic_antag.dm +++ b/code/modules/antagonists/heretic/heretic_antag.dm @@ -159,12 +159,22 @@ var/list/knowledge_info = researched_knowledge[knowledge] if(islist(knowledge_info)) var/datum/heretic_knowledge/knowledge_instance = knowledge_info[HKT_INSTANCE] - knowledge_data["desc"] = knowledge_instance.desc + knowledge_data["info"] = knowledge_instance.transmute_text + knowledge_data["notice"] = knowledge_instance.notice + else knowledge_data["desc"] = initial(knowledge.desc) - return knowledge_data + knowledge_data["info"] = initial(knowledge.transmute_text) + knowledge_data["notice"] = initial(knowledge.notice) + + if(ispath(knowledge, /datum/heretic_knowledge/ultimate)) + var/ascension_check = can_ascend() + if(ascension_check != HERETIC_CAN_ASCEND) + knowledge_data["disabled"] = TRUE + knowledge_data["notice"] += "
    [ascension_check]" + return knowledge_data /datum/antagonist/heretic/ui_interact(mob/user, datum/tgui/ui) . = ..() @@ -215,15 +225,6 @@ if(!(knowledge_info[HKT_ID] in researchable_knowledges)) continue var/list/knowledge_data = get_knowledge_data(knowledge_path, heretic_tree, FALSE) - - // Final knowledge can't be learned until all objectives are complete. - if(ispath(knowledge_path, /datum/heretic_knowledge/ultimate)) - var/ascension_check = can_ascend() - if(ascension_check != HERETIC_CAN_ASCEND) - knowledge_data["disabled"] = TRUE - knowledge_data["tooltip"] = ascension_check - - var/depth = knowledge_data[HKT_DEPTH] while(depth > length(tree_data)) @@ -385,7 +386,6 @@ ADD_TRAIT(our_mob, TRAIT_MANSUS_TOUCHED, REF(src)) RegisterSignal(our_mob, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed)) - RegisterSignals(our_mob, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED), PROC_REF(on_spell_cast)) RegisterSignal(our_mob, COMSIG_USER_ITEM_INTERACTION, PROC_REF(on_item_use)) RegisterSignal(our_mob, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(after_fully_healed)) RegisterSignal(our_mob, COMSIG_ATOM_EXAMINE, PROC_REF(on_heretic_examine)) @@ -411,8 +411,6 @@ UnregisterSignal( our_mob, list( - COMSIG_MOB_BEFORE_SPELL_CAST, - COMSIG_MOB_SPELL_ACTIVATED, COMSIG_USER_ITEM_INTERACTION, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_LIVING_CULT_SACRIFICED, @@ -480,31 +478,6 @@ knowledge.on_lose(old_body, src) knowledge.on_gain(new_body, src) -/* - * Signal proc for [COMSIG_MOB_BEFORE_SPELL_CAST] and [COMSIG_MOB_SPELL_ACTIVATED]. - * - * Checks if our heretic has [TRAIT_ALLOW_HERETIC_CASTING] or is ascended. - * If so, allow them to cast like normal. - * If not, cancel the cast, and returns [SPELL_CANCEL_CAST]. - */ -/datum/antagonist/heretic/proc/on_spell_cast(mob/living/source, datum/action/cooldown/spell/spell) - SIGNAL_HANDLER - - // Heretic spells are of the forbidden school, otherwise we don't care - if(spell.school != SCHOOL_FORBIDDEN) - return - - // If we've got the trait, we don't care - if(HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING)) - return - // All powerful, don't care - if(ascended) - return - - // We shouldn't be able to cast this! Cancel it. - source.balloon_alert(source, "you need a focus!") - return SPELL_CANCEL_CAST - /* * Signal proc for [COMSIG_USER_ITEM_INTERACTION]. * @@ -571,7 +544,7 @@ else drawing_effect = new(target_turf, rune_colour) - if(!do_after(user, drawing_time, target_turf, extra_checks = additional_checks, hidden = TRUE)) + if(!do_after(user, drawing_time, target_turf, extra_checks = additional_checks, cog_icon = null)) target_turf.balloon_alert(user, "interrupted!") new /obj/effect/temp_visual/drawing_heretic_rune/fail(target_turf, rune_colour) qdel(drawing_effect) @@ -784,8 +757,8 @@ */ /datum/antagonist/heretic/proc/passive_influence_gain() adjust_knowledge_points(1) - if(owner?.current?.stat <= SOFT_CRIT) - to_chat(owner.current, "[span_hear("You hear a whisper...")] [span_hypnophrase(pick_list(HERETIC_INFLUENCE_FILE, "drain_message"))]") + if(!IS_UNCONSCIOUS(owner?.current)) + to_chat(owner.current, "[span_hear("You hear a whisper...")] [span_mansus(pick_list(HERETIC_INFLUENCE_FILE, "drain_message"))]") addtimer(CALLBACK(src, PROC_REF(passive_influence_gain)), passive_gain_timer) /datum/antagonist/heretic/proc/adjust_knowledge_points(amount, update = TRUE) @@ -932,7 +905,7 @@ var/mob/living/pawn = owner.current pawn.equip_to_slot_if_possible(new /obj/item/clothing/neck/heretic_focus(get_turf(pawn)), ITEM_SLOT_NECK, TRUE, TRUE) - to_chat(pawn, span_hypnophrase("The Mansus has manifested you a focus.")) + to_chat(pawn, span_mansus("The Mansus has manifested you a focus.")) /datum/antagonist/heretic/antag_panel_data() var/list/string_of_knowledge = list() @@ -1028,6 +1001,26 @@ researchable_knowledge -= banned_knowledge return researchable_knowledge +/** + * Get a list of all knowledge datums that we've researched. + */ +/datum/antagonist/heretic/proc/get_researched_knowledge() + var/list/knowledge_list = list() + for(var/knowledge_type in researched_knowledge) + knowledge_list += researched_knowledge[knowledge_type][HKT_INSTANCE] + return knowledge_list + +/** + * Get a list of all knowledge datums that we've researched in a specific category. + */ +/datum/antagonist/heretic/proc/get_researched_knowledge_by_category(category_type) + var/list/knowledge_list = list() + for(var/knowledge_type in researched_knowledge) + if(researched_knowledge[knowledge_type][HKT_CATEGORY] == category_type) + knowledge_list += researched_knowledge[knowledge_type][HKT_INSTANCE] + return knowledge_list + + /** * Check if the wanted type-path is in the list of research knowledge. */ @@ -1058,9 +1051,9 @@ var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_path][HKT_INSTANCE] if(!knowledge.can_be_invoked(src)) continue - rituals[knowledge.name] = knowledge + rituals += knowledge - return sortTim(rituals, GLOBAL_PROC_REF(cmp_heretic_knowledge), associative = TRUE) + return sortTim(rituals, GLOBAL_PROC_REF(cmp_heretic_knowledge)) /** * Checks to see if our heretic can ccurrently ascend. @@ -1071,7 +1064,7 @@ if(feast_of_owls) return "The owls have taken your right of ascension (denied ascension)." // We sold our ambition for immediate power :/ if(!can_assign_self_objectives) - return "The mansus has spurned you (denied ascension)." + return "The Mansus has spurned you (denied ascension)." for(var/datum/objective/must_be_done as anything in objectives) if(!must_be_done.check_completion()) return "Must complete all objectives before ascending." diff --git a/code/modules/antagonists/heretic/heretic_curses.dm b/code/modules/antagonists/heretic/heretic_curses.dm index 1893bc725587..3d05ff097f46 100644 --- a/code/modules/antagonists/heretic/heretic_curses.dm +++ b/code/modules/antagonists/heretic/heretic_curses.dm @@ -80,7 +80,7 @@ log_combat(user, to_curse, "cursed via heretic ritual", addition = "([name])") var/obj/item/codex_cicatrix/morbus/cursed_book = locate() in selected_atoms curse(to_curse, cursed_book) - to_chat(user, span_hierophant("You cast a [name] upon [to_curse.real_name].")) + to_chat(user, span_mansus("You cast a [name] upon [to_curse.real_name].")) fingerprints = null blood_samples = null @@ -136,9 +136,10 @@ /datum/heretic_knowledge/curse/paralysis abstract_type = /datum/heretic_knowledge/curse/paralysis name = "Curse of Paralysis" - desc = "Allows you to transmute a hatchet and both a left and right leg to cast a curse of immobility on a crew member. \ + desc = "Casts a curse of immobility on a crew member.
    \ While cursed, the victim will be unable to walk. You can additionally supply an item that a victim has touched \ or is covered in the victim's blood to make the curse last longer." + transmute_text = "Transmute a hatchet, and both a left and right leg." gain_text = "The flesh of humanity is weak. Make them bleed. Show them their fragility." duration = 5 MINUTES @@ -171,9 +172,10 @@ /datum/heretic_knowledge/curse/corrosion abstract_type = /datum/heretic_knowledge/curse/corrosion name = "Curse of Corrosion" - desc = "Allows you to transmute wirecutters, a pool of vomit, and a heart to cast a curse of sickness on a crew member. \ + desc = "Cast a curse of sickness on a crew member.
    \ While cursed, the victim will repeatedly vomit while their organs will take constant damage. You can additionally supply an item \ that a victim has touched or is covered in the victim's blood to make the curse last longer." + transmute_text = "Transmute a pair of wirecutters, a pool of vomit, and a heart." gain_text = "The body of humanity is temporary. Their weaknesses cannot be stopped, like iron falling to rust. Show them all." duration = 3 MINUTES diff --git a/code/modules/antagonists/heretic/heretic_focus.dm b/code/modules/antagonists/heretic/heretic_focus.dm deleted file mode 100644 index 45bbf743b8cd..000000000000 --- a/code/modules/antagonists/heretic/heretic_focus.dm +++ /dev/null @@ -1,61 +0,0 @@ -/// Heretic focus element, simple element for making an item a heretic focus, -/// allowing heretics to cast advanced spells (examine message included). -/datum/element/heretic_focus - -/datum/element/heretic_focus/Attach(datum/target) - . = ..() - if(!isitem(target)) - return ELEMENT_INCOMPATIBLE - - RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(target, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip)) - RegisterSignal(target, COMSIG_ITEM_DROPPED, PROC_REF(on_drop)) - - var/obj/item/item_target = target - // If our loc is a mob, it's possible we already have it equippied - if(ismob(item_target.loc)) - var/mob/wearer = item_target.loc - if(!item_target.slot_flags || wearer.get_item_by_slot(item_target.slot_flags) == item_target) - ADD_TRAIT(wearer, TRAIT_ALLOW_HERETIC_CASTING, ELEMENT_TRAIT(target)) - -/datum/element/heretic_focus/Detach(obj/item/source) - . = ..() - UnregisterSignal(source, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED)) - if(isliving(source.loc)) - REMOVE_TRAIT(source.loc, TRAIT_ALLOW_HERETIC_CASTING, ELEMENT_TRAIT(source)) - -/** - * Signal proc for [COMSIG_ATOM_EXAMINE]. - * Let's the examiner see that this item is a heretic focus - */ -/datum/element/heretic_focus/proc/on_examine(obj/item/source, mob/user, list/examine_list) - SIGNAL_HANDLER - - if(!IS_HERETIC(user)) - return - - examine_list += span_notice("Allows you to cast advanced heretic spells when worn.") - -/** - * Signal proc for [COMSIG_ITEM_EQUIPPED]. - * When equipped in a right slot, give user our trait - */ -/datum/element/heretic_focus/proc/on_equip(obj/item/source, mob/user, slot) - SIGNAL_HANDLER - - if(!IS_HERETIC(user)) - return - - if(source.slot_flags && !(source.slot_flags & slot)) - return - - ADD_TRAIT(user, TRAIT_ALLOW_HERETIC_CASTING, ELEMENT_TRAIT(source)) - -/** - * Signal proc for [COMSIG_ITEM_DROPPED]. - * Remove our trait when we drop (unequip) our item - */ -/datum/element/heretic_focus/proc/on_drop(obj/item/source, mob/user) - SIGNAL_HANDLER - - REMOVE_TRAIT(user, TRAIT_ALLOW_HERETIC_CASTING, ELEMENT_TRAIT(source)) diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index 0c5efc1a9595..8c0f55b6b568 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -15,6 +15,10 @@ var/name = "Basic knowledge" /// Description of the knowledge, shown to the heretic. Describes what it unlocks / does. var/desc = "Basic knowledge of forbidden arts." + /// Text describing how you create the thing + var/transmute_text = "" + /// Big red notices about the knowledge + var/notice = "" /// What's shown to the heretic when the knowledge is acquired var/gain_text /// Assoc list of [typepaths we need] to [amount needed]. @@ -75,6 +79,7 @@ on_gain(user, our_heretic) if(is_final_knowledge && !our_heretic.unlimited_blades) our_heretic.disable_blade_breaking() + SEND_SIGNAL(our_heretic, COMSIG_HERETIC_RESEARCHED_KNOWLEDGE, src) /** * Called when the knowledge is applied to a mob. @@ -128,6 +133,12 @@ /datum/heretic_knowledge/proc/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) return TRUE +/// Used in unit testing to prepare the components for the test +/datum/heretic_knowledge/proc/prepare_atom_for_ritual_test(atom/what) + if(isitem(what)) + var/obj/item/item = what + item.item_flags &= ~ABSTRACT + /** * Parses specific items into a more readble form. * Can be overriden by knoweldge subtypes. @@ -135,10 +146,11 @@ /datum/heretic_knowledge/proc/parse_required_item(atom/item_path, number_of_things) // If we need a human, there is a high likelihood we actually need a (dead) body if(ispath(item_path, /mob/living/carbon/human)) - return "bod[number_of_things > 1 ? "ies" : "y"]" + return "[number_of_things] bod[number_of_things > 1 ? "ies" : "y"]" if(ispath(item_path, /mob/living)) - return "carcass[number_of_things > 1 ? "es" : ""] of any kind" - return "[initial(item_path.name)]\s" + return "[number_of_things] carcass[number_of_things > 1 ? "es" : ""] of any kind" + return "[number_of_things] [initial(item_path.name)]\s" + /** * Called whenever the knowledge's associated ritual is completed successfully. * @@ -193,9 +205,31 @@ /datum/heretic_knowledge/spell abstract_type = /datum/heretic_knowledge/spell /// Spell path we add to the heretic. Type-path. - var/datum/action/action_to_add + var/datum/action/cooldown/spell/action_to_add /// The spell we actually created. - var/datum/weakref/created_action_ref + VAR_FINAL/datum/action/cooldown/spell/created_action_ref + /// Used to display charges on the spell's action button, if applicable. + VAR_FINAL/mutable_appearance/charge_maptext + /// Charge count + VAR_FINAL/charges = 1 + /// Max amount of charges before a heretic needs to recharge by doing the ritual again + var/max_charges = 1 + /// Percent of max charges restored on a successful ritual + var/recharge_amount = 1.0 + /// Percent of max charges restored on completing the path passive + var/path_recharge_amount = 0.5 + /// What percent of the max charges the spell regains periodically while wearing a focus + var/focus_recharge_amount = 0.0 + /// What percent of the max charges the spell loses periodically from consuming holy water + var/holywater_drain_amount = 0.0 + /// When recharged via path passive, if TRUE, we bypass the max_charges cap. + var/path_recharge_can_surpass_cap = FALSE + +/datum/heretic_knowledge/spell/New() + . = ..() + charges = max_charges + if(max_charges != INFINITY) + desc += "
    Has [max_charges] charge\s[transmute_text ? ", after which you must recharge the spell" : ""]." /datum/heretic_knowledge/spell/Destroy() QDEL_NULL(created_action_ref) @@ -205,14 +239,144 @@ // Added spells are tracked on the body, and not the mind, // because we handle heretic mind transfers // via the antag datum (on_gain and on_lose). - var/datum/action/created_action = created_action_ref?.resolve() || new action_to_add(user) - created_action.Grant(user) - created_action_ref = WEAKREF(created_action) + created_action_ref ||= new action_to_add(src) + created_action_ref.Grant(user) + + RegisterSignal(created_action_ref, COMSIG_ACTION_STATUS_UPDATE, PROC_REF(action_update)) + RegisterSignal(created_action_ref, COMSIG_QDELETING, PROC_REF(action_delete)) + RegisterSignal(created_action_ref, COMSIG_SPELL_CAN_CAST_CHECK, PROC_REF(spell_check)) + + RegisterSignals(user, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED), PROC_REF(check_charges)) + if(istype(created_action_ref, /datum/action/cooldown/spell/pointed/projectile)) + RegisterSignal(user, COMSIG_MOB_SPELL_PROJECTILE, PROC_REF(deduct_charge)) + else if(istype(created_action_ref, /datum/action/cooldown/spell/touch)) + RegisterSignal(user, COMSIG_SPELL_TOUCH_SPELL_ACTUALLY_CAST, PROC_REF(deduct_charge)) + else + RegisterSignal(user, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(deduct_charge)) + + update_charge_counter() /datum/heretic_knowledge/spell/on_lose(mob/user, datum/antagonist/heretic/our_heretic) - var/datum/action/cooldown/spell/created_action = created_action_ref?.resolve() - if(created_action?.owner == user) - created_action.Remove(user) + if(created_action_ref?.owner == user) + created_action_ref.Remove(user) + + UnregisterSignal(user, list( + COMSIG_SPELL_CAN_CAST_CHECK, + COMSIG_MOB_SPELL_ACTIVATED, + COMSIG_MOB_AFTER_SPELL_CAST, + COMSIG_MOB_BEFORE_SPELL_CAST, + COMSIG_MOB_SPELL_PROJECTILE, + COMSIG_SPELL_TOUCH_SPELL_ACTUALLY_CAST, + )) + +/datum/heretic_knowledge/spell/can_be_invoked(datum/antagonist/heretic/invoker) + if(!LAZYLEN(required_atoms)) + return FALSE + if(created_action_ref?.owner != invoker.owner?.current) + return FALSE + if(charges >= max_charges) + return FALSE + return TRUE + +/datum/heretic_knowledge/spell/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) + add_charges(ceil(max_charges * recharge_amount)) + return TRUE + +/datum/heretic_knowledge/spell/proc/action_update(datum/action/source, atom/movable/screen/movable/action_button/button, ...) + SIGNAL_HANDLER + + if(charge_maptext) + button.cut_overlay(charge_maptext) + + if(charges >= 100) + return + if(source.owner) + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(source.owner) + if(our_heretic?.ascended) + return + + charge_maptext ||= new() + charge_maptext.maptext_x = 4 + charge_maptext.maptext_y = 20 + charge_maptext.maptext = MAPTEXT("[charges]") + button.add_overlay(charge_maptext) + +/datum/heretic_knowledge/spell/proc/action_delete(datum/action/source) + SIGNAL_HANDLER + created_action_ref = null // shouldn't happen... + +/datum/heretic_knowledge/spell/proc/spell_check(datum/action/the_spell, feedback) + SIGNAL_HANDLER + + if(the_spell != created_action_ref || isnull(the_spell.owner)) + return NONE + if(charges > 0) + return NONE + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(the_spell.owner) + if(our_heretic?.ascended) + return NONE + + if(feedback) + to_chat(the_spell.owner, span_mansus("You don't have enough charges to cast this spell!")) + return SPELL_CANCEL_CAST + +/datum/heretic_knowledge/spell/proc/check_charges(mob/living/source, datum/action/cooldown/the_spell) + SIGNAL_HANDLER + + if(the_spell != created_action_ref) + return NONE + if(charges > 0) + return NONE + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(source) + if(our_heretic?.ascended) + return NONE + + to_chat(source, span_mansus("You don't have enough charges to cast this spell! [transmute_text]")) + return SPELL_CANCEL_CAST + +/datum/heretic_knowledge/spell/proc/deduct_charge(mob/living/source, datum/action/cooldown/the_spell) + SIGNAL_HANDLER + + if(the_spell != created_action_ref) + return + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(source) + if(our_heretic?.ascended) + return + + remove_charges(1) + +/// Add a number of charges, optionally bypassing the cap +/datum/heretic_knowledge/spell/proc/add_charges(num, uncapped = FALSE) + if(num <= 0) + return FALSE + + var/pre_charge_value = charges + if(uncapped) + charges += num + else + charges = min(charges + num, max_charges) + update_charge_counter() + return charges != pre_charge_value + +/// Remove a number of charges (down to 0) +/datum/heretic_knowledge/spell/proc/remove_charges(num) + if(num <= 0) + return FALSE + + var/pre_charge_value = charges + charges = max(charges - num, 0) + update_charge_counter() + if(charges <= 0 && created_action_ref.owner?.click_intercept == created_action_ref) + created_action_ref.unset_click_ability(created_action_ref.owner, refund_cooldown = FALSE) + return charges != pre_charge_value + +/datum/heretic_knowledge/spell/proc/update_charge_counter() + created_action_ref?.build_all_button_icons(UPDATE_BUTTON_STATUS) + +/datum/heretic_knowledge/spell/vv_edit_var(var_name, var_value) + . = ..() + if(var_name == NAMEOF(src, charges) || var_name == NAMEOF(src, max_charges)) + update_charge_counter() /** * A knowledge subtype for knowledge that can only @@ -406,7 +570,7 @@ * Overridable proc that invokes special effects * whenever the heretic clicks on someone at range with their heretic blade. */ -/datum/heretic_knowledge/blade_upgrade/proc/do_ranged_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade) +/datum/heretic_knowledge/blade_upgrade/proc/do_ranged_effects(mob/living/source, atom/target, obj/item/melee/sickly_blade/blade) return /** @@ -433,7 +597,7 @@ summoned = mob_to_summon else summoned = new mob_to_summon(loc) - summoned.ai_controller?.set_ai_status(AI_STATUS_OFF) + summoned.ai_controller?.force_ai_off() // Fade in the summon while the ghost poll is ongoing. // Also don't let them mess with the summon while waiting summoned.alpha = 0 @@ -456,10 +620,13 @@ summoned.ghostize(FALSE) summoned.PossessByPlayer(chosen_one.key) + summoned.ai_controller?.clear_forced_off() //the client keeps the AI off from here; if they disconnect the AI may take back over user.log_message("created a [summoned.name], controlled by [key_name(chosen_one)].", LOG_GAME) message_admins("[ADMIN_LOOKUPFLW(user)] created a [summoned.name], [ADMIN_LOOKUPFLW(summoned)].") + SEND_SIGNAL(user, COMSIG_HERETIC_SUMMONED_MOB, summoned) + var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster) heretic_monster.set_owner(user.mind) @@ -477,6 +644,7 @@ /datum/heretic_knowledge/knowledge_ritual name = "Ritual of Knowledge" desc = "A randomly generated transmutation ritual that rewards knowledge points and can only be completed once." + notice = "This can only be completed once." gain_text = "Everything can be a key to unlocking the secrets behind the Gates. I must be wary and wise." abstract_type = /datum/heretic_knowledge/knowledge_ritual cost = 1 @@ -531,15 +699,16 @@ var/list/requirements_string = list() - to_chat(user, span_hierophant("The [name] requires the following:")) + to_chat(user, span_mansus("The [name] requires the following:")) for(var/obj/item/path as anything in required_atoms) var/amount_needed = required_atoms[path] to_chat(user, span_hypnophrase("[amount_needed] [initial(path.name)]\s...")) requirements_string += "[amount_needed == 1 ? "":"[amount_needed] "][initial(path.name)]\s" - to_chat(user, span_hierophant("Completing it will reward you [KNOWLEDGE_RITUAL_POINTS] knowledge points. You can check the knowledge in your Researched Knowledge to be reminded.")) + to_chat(user, span_mansus("Completing it will reward you [KNOWLEDGE_RITUAL_POINTS] knowledge points. You can check the knowledge in your Researched Knowledge to be reminded.")) - desc = "Allows you to transmute [english_list(requirements_string)] for [KNOWLEDGE_RITUAL_POINTS] bonus knowledge points. This can only be completed once." + transmute_text = "Transmute [english_list(requirements_string)]." + desc = "Rewards you with [KNOWLEDGE_RITUAL_POINTS] bonus knowledge points." /datum/heretic_knowledge/knowledge_ritual/can_be_invoked(datum/antagonist/heretic/invoker) return !was_completed diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm index 78256b12a635..3a8caa822b9c 100644 --- a/code/modules/antagonists/heretic/heretic_living_heart.dm +++ b/code/modules/antagonists/heretic/heretic_living_heart.dm @@ -174,7 +174,7 @@ if(ismob(tracked_thing)) var/mob/tracked_mob = tracked_thing if(tracked_mob.stat == DEAD) - to_chat(owner, span_hierophant("[tracked_mob] is dead. Bring them to a transmutation rune \ + to_chat(owner, span_mansus("[tracked_mob] is dead. Bring them to a transmutation rune \ and invoke \"[sac_knowledge.name]\" to sacrifice them!")) StartCooldown() diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index b74d120d86b9..1292f8fc3eeb 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -205,14 +205,12 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/effect/heretic_influence/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(.) - return - +/obj/effect/heretic_influence/item_interaction(mob/living/user, obj/item/tool, list/modifiers) // Using a codex will give you two knowledge points for draining. - if(drain_influence_with_codex(user, weapon)) - return TRUE + if(drain_influence_with_codex(user, tool)) + return ITEM_INTERACT_SUCCESS + + return ..() /obj/effect/heretic_influence/proc/drain_influence_with_codex(mob/user, obj/item/codex_cicatrix/codex) if(!istype(codex) || being_drained) @@ -239,7 +237,7 @@ draining_overlay.pixel_y = 16 user.add_overlay(draining_overlay) - if(!do_after(user, drain_speed, src, hidden = TRUE)) + if(!do_after(user, drain_speed, src, cog_icon = null)) being_drained = FALSE loc.balloon_alert(user, "interrupted!") user.cut_overlay(draining_overlay) @@ -251,6 +249,7 @@ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user) heretic_datum.adjust_knowledge_points(knowledge_to_gain) + SEND_SIGNAL(heretic_datum, COMSIG_HERETIC_INFLUENCE_DRAINED) // Aaand now we delete it after_drain(user) diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm index 0c96652436c2..bea4e1ebafb6 100644 --- a/code/modules/antagonists/heretic/items/corrupted_organs.dm +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -281,7 +281,7 @@ /obj/item/organ/appendix/corrupt/on_life(seconds_per_tick) . = ..() - if (owner.stat != CONSCIOUS || owner.has_reagent(/datum/reagent/water/holywater) || IS_IN_MANSUS(owner) || !SPT_PROB(worm_chance, seconds_per_tick)) + if (IS_UNCONSCIOUS_OR_CRIT(owner) || owner.has_reagent(/datum/reagent/water/holywater) || IS_IN_MANSUS(owner) || !SPT_PROB(worm_chance, seconds_per_tick)) return owner.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM, vomit_type = /obj/effect/decal/cleanable/vomit/nebula/worms, distance = 0) owner.Knockdown(0.5 SECONDS) diff --git a/code/modules/antagonists/heretic/items/eldritch_painting.dm b/code/modules/antagonists/heretic/items/eldritch_painting.dm index 54bc953ee1d9..211306dcfe8a 100644 --- a/code/modules/antagonists/heretic/items/eldritch_painting.dm +++ b/code/modules/antagonists/heretic/items/eldritch_painting.dm @@ -38,7 +38,7 @@ SIGNAL_HANDLER if(!isliving(viewer) || !can_see(viewer, src, range)) return - if(isnull(viewer.mind) || isnull(viewer.mob_mood) || viewer.stat != CONSCIOUS || viewer.is_blind()) + if(isnull(viewer.mind) || isnull(viewer.mob_mood) || IS_UNCONSCIOUS_OR_CRIT(viewer) || viewer.is_blind()) return if(viewer.has_status_effect(applied_status_effect)) return @@ -173,8 +173,8 @@ ) // Poppy and harebell are used in heretic rituals var/list/items_to_spawn = list( - /obj/item/food/grown/poppy, - /obj/item/food/grown/harebell, + /obj/item/food/grown/flower/poppy, + /obj/item/food/grown/flower/harebell, ) /obj/structure/sign/painting/eldritch/vines/Initialize(mapload) diff --git a/code/modules/antagonists/heretic/items/forbidden_book.dm b/code/modules/antagonists/heretic/items/forbidden_book.dm index 35039d3e88f4..3a7fafba81ba 100644 --- a/code/modules/antagonists/heretic/items/forbidden_book.dm +++ b/code/modules/antagonists/heretic/items/forbidden_book.dm @@ -2,7 +2,7 @@ /obj/item/codex_cicatrix name = "Codex Cicatrix" desc = "This heavy tome is full of cryptic scribbles and impossible diagrams. \ - According to legend, it can be deciphered to reveal the secrets of the veil between worlds." + According to legend, it can be deciphered to reveal the secrets of the veil between worlds." icon = 'icons/obj/antags/eldritch.dmi' base_icon_state = "book" icon_state = "book" @@ -11,9 +11,10 @@ /// Helps determine the icon state of this item when it's used on self. var/book_open = FALSE /// How fast we can drain influences - var/drain_speed = 5 SECONDS + var/drain_speed = 7.5 SECONDS /// How fast we can draw runes - var/draw_speed = 8 SECONDS + var/draw_speed = 15 SECONDS + /obj/item/codex_cicatrix/Initialize(mapload) . = ..() @@ -34,7 +35,6 @@ . += span_notice("Can be used to tap influences for additional knowledge points.") . += span_notice("Can also be used to draw or remove transmutation runes with ease.") - . += span_notice("Additionally, it can work as a focus for your spells when held.") /obj/item/codex_cicatrix/attack_self(mob/user, modifiers) . = ..() @@ -42,13 +42,19 @@ return if(book_open) - close_animation() - RemoveElement(/datum/element/heretic_focus) - update_weight_class(WEIGHT_CLASS_SMALL) - else - open_animation() - AddElement(/datum/element/heretic_focus) - update_weight_class(WEIGHT_CLASS_NORMAL) + return + + open_animation() + update_weight_class(WEIGHT_CLASS_NORMAL) + addtimer(CALLBACK(src, PROC_REF(close_book)), 30 SECONDS) + + var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user) + var/datum/heretic_knowledge/spell/basic/grasp_knowledge = heretic_datum.get_knowledge(__IMPLIED_TYPE__) + grasp_knowledge?.add_charges(ceil(grasp_knowledge.max_charges * 0.5)) + +/obj/item/codex_cicatrix/proc/close_book() + close_animation() + update_weight_class(WEIGHT_CLASS_SMALL) /obj/item/codex_cicatrix/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user) @@ -79,8 +85,8 @@ desc = "A hideous, ragged book covered in separately-blinking eyes, all of them staring at you. You have no idea how to hold this thing, and to be honest you're not sure if you want to." base_icon_state = "book_morbus" icon_state = "book_morbus" - drain_speed = 2.5 SECONDS - draw_speed = 5 SECONDS + drain_speed = 5 SECONDS + draw_speed = 10 SECONDS /// List of mobs we've cursed with transmutation. When the codex is destroyed all those curses become undone var/list/transmuted_victims = list() diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm index c94d7bf94069..a91199fdae78 100644 --- a/code/modules/antagonists/heretic/items/heretic_armor.dm +++ b/code/modules/antagonists/heretic/items/heretic_armor.dm @@ -15,7 +15,7 @@ transparent_protection = HIDEGLOVES | HIDESUITSTORAGE | HIDEJUMPSUIT | HIDESHOES | HIDENECK cold_protection = FULL_BODY min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT - allowed = list(/obj/item/melee/sickly_blade, /obj/item/gun/ballistic/rifle/lionhunter) + allowed = list(/obj/item/melee/sickly_blade, /obj/item/gun/ballistic/rifle/lionhunter, /obj/item/flashlight/lantern/heretic) hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch armor_type = /datum/armor/eldritch_armor clothing_traits = list(TRAIT_HERETIC_AURA_HIDDEN) @@ -57,16 +57,6 @@ /obj/item/clothing/suit/hooded/cultrobes/eldritch/on_hood_down(obj/item/clothing/head/hooded/hood) hood_up = FALSE -/obj/item/clothing/suit/hooded/cultrobes/eldritch/examine(mob/user) - . = ..() - if(!IS_HERETIC(user)) - return - if(hood_up) - return - - // Our hood gains the heretic_focus element. - . += span_notice("Allows you to cast heretic spells while the hood is up.") - /obj/item/clothing/head/hooded/cult_hoodie/eldritch name = "ominous hood" icon = 'icons/obj/clothing/head/helmet.dmi' @@ -80,10 +70,6 @@ clothing_flags = THICKMATERIAL | PLASMAMAN_PREVENT_IGNITION | SNUG_FIT armor_type = /datum/armor/eldritch_armor -/obj/item/clothing/head/hooded/cult_hoodie/eldritch/Initialize(mapload) - . = ..() - AddElement(/datum/element/heretic_focus) - /datum/armor/eldritch_armor melee = 50 bullet = 50 @@ -1220,7 +1206,6 @@ /// Makes our cloak "invisible". Not the wearer, the cloak itself. /obj/item/clothing/suit/hooded/cultrobes/void/proc/make_invisible() add_traits(list(TRAIT_NO_STRIP, TRAIT_EXAMINE_SKIP), REF(src)) - RemoveElement(/datum/element/heretic_focus) flags_cover |= ALLOW_SURGERY_THROUGH if(isliving(loc)) @@ -1232,7 +1217,6 @@ /// Makes our cloak "visible" again. /obj/item/clothing/suit/hooded/cultrobes/void/proc/make_visible() remove_traits(list(TRAIT_NO_STRIP, TRAIT_EXAMINE_SKIP), REF(src)) - AddElement(/datum/element/heretic_focus) flags_cover &= ~ALLOW_SURGERY_THROUGH if(isliving(loc)) diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm index 30433933ec84..614a91ed0228 100644 --- a/code/modules/antagonists/heretic/items/heretic_blades.dm +++ b/code/modules/antagonists/heretic/items/heretic_blades.dm @@ -50,6 +50,8 @@ var/mob/living/carbon/human/human_user = user human_user.AdjustParalyzed(5 SECONDS) return TRUE + if(SEND_SIGNAL(user, COMSIG_HERETIC_BLADE_PREATTACK, target, src) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE return . diff --git a/code/modules/antagonists/heretic/items/heretic_grenade.dm b/code/modules/antagonists/heretic/items/heretic_grenade.dm index 4cae667299da..aa62156aa3c6 100644 --- a/code/modules/antagonists/heretic/items/heretic_grenade.dm +++ b/code/modules/antagonists/heretic/items/heretic_grenade.dm @@ -4,8 +4,8 @@ */ /obj/item/grenade/chem_grenade/rust_sower - name = "\improper Rust sower" - desc = "A nifty little thing that explodes into rust. Causes borgs and mechs to get utterly obliterated" + name = "rust sower" + desc = "A nifty little thing that explodes into rust. Causes borgs and mechs to get utterly obliterated." possible_fuse_time = list("5") stage = GRENADE_READY base_icon_state = "rustgrenade" diff --git a/code/modules/antagonists/heretic/items/heretic_lantern.dm b/code/modules/antagonists/heretic/items/heretic_lantern.dm new file mode 100644 index 000000000000..780bc7757266 --- /dev/null +++ b/code/modules/antagonists/heretic/items/heretic_lantern.dm @@ -0,0 +1,185 @@ +/obj/item/flashlight/lantern/heretic + name = "burning lantern" + desc = "A strange lantern that hurts your eyes to look at, even when it's quelled." + light_power = 3 + light_color = "#e8ff66" + heat = 2400 + w_class = WEIGHT_CLASS_NORMAL + ignore_base_color = TRUE + color = list( + 0, 1, 0, 0, + 0, 1, 1, 0, + 0, 1.5, 0, 0, + 0, 0, 0, 0.5, + 0, -0.25, -0.25, 0, + ) + /// Lazylist of world.time values for when the next pulse can affect a given mob. Indexed by REF(mob). + var/list/effect_cds + /// Lazylist of how many active pulses are currently affecting a given mob. Indexed by REF(mob). + var/list/tick_counts + /// Lighting middleman, lets us do a flicker effect + var/datum/light_middleman/middleman + +/obj/item/flashlight/lantern/heretic/Initialize(mapload) + . = ..() + if(IS_OVERLAY_LIGHT_SYSTEM(light_system)) + middleman = new(src, "lantern") + RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated)) + middleman.being_overriding_light() + +/obj/item/flashlight/lantern/heretic/init_slapcrafting() + return + +/obj/item/flashlight/lantern/heretic/set_light_on(new_value) + . = ..() + if(isnull(.)) + return + if(new_value) + START_PROCESSING(SSobj, src) + add_filter("lantern_pulse", 1, outline_filter(color = "#0b8000", size = 1)) + apply_wibbly_filters(src) + update_weight_class(WEIGHT_CLASS_BULKY) + var/atom/message_loc = isturf(loc) ? src : loc + message_loc.visible_message( + span_notice("[isturf(loc) ? src : "[loc]'s [src]"] flickers to life, casting eerie shadows around it.") + ) + else + STOP_PROCESSING(SSobj, src) + remove_filter("lantern_pulse", 1) + remove_wibbly_filters(src) + update_weight_class(WEIGHT_CLASS_NORMAL) + var/atom/message_loc = isturf(loc) ? src : loc + message_loc.visible_message( + span_notice("[isturf(loc) ? src : "[loc]'s [src]"] flickers out, the shadows retreating.") + ) + +/obj/item/flashlight/lantern/heretic/equipped(mob/user, slot, initial) + . = ..() + if(!light_on) + return + remove_wibbly_filters(src) + apply_wibbly_filters(src) + +/obj/item/flashlight/lantern/heretic/dropped(mob/user, silent) + . = ..() + if(!light_on) + return + remove_wibbly_filters(src) + apply_wibbly_filters(src) + +/obj/item/flashlight/lantern/heretic/Destroy() + STOP_PROCESSING(SSobj, src) + QDEL_NULL(middleman) + return ..() + +/obj/item/flashlight/lantern/heretic/proc/light_updated(datum/source) + SIGNAL_HANDLER + fire_flicker_middleman(middleman) + +/obj/item/flashlight/lantern/heretic/process(seconds_per_tick) + if(!isturf(loc) && !ismob(loc)) + return + + open_flame(heat, slot_flags|ITEM_SLOT_HANDS|ITEM_SLOT_SUITSTORE) + effect_pulse(isturf(loc) ? src : loc, seconds_per_tick) + +/obj/item/flashlight/lantern/heretic/proc/effect_pulse(atom/center = src, seconds_per_tick) + var/list/affected_refs = list() + for(var/mob/living/seer in viewers(light_range - 1, center)) + try_effect_pulse(seer, center) + affected_refs += REF(seer) + // if you are unaffected, tick count goes down by 1 + for(var/tick_ref in SANITIZE_LIST(tick_counts) - affected_refs) + LAZYSET(tick_counts, tick_ref, LAZYACCESS(tick_counts, tick_ref) - 1) + if(LAZYACCESS(tick_counts, tick_ref) <= 0) + LAZYREMOVE(tick_counts, tick_ref) + +/obj/item/flashlight/lantern/heretic/proc/try_effect_pulse(mob/living/seer, atom/center) + if(prob(33)) + return // tiny bit of variance + if(LAZYACCESS(effect_cds, REF(seer)) >= world.time) + return + + var/tick_count = LAZYACCESS(tick_counts, REF(seer)) + var/seer_eye_prot = seer.get_eye_protection() + if(seer_eye_prot >= FLASH_PROTECTION_WELDER_HYPER_SENSITIVE || IS_HERETIC_OR_MONSTER(seer)) + if(tick_count - 1 <= 0) + LAZYREMOVE(tick_counts, REF(seer)) + else + LAZYSET(tick_counts, REF(seer), tick_count - 1) + return + + + var/turf/seer_turf = get_turf(seer) + if(!isnull(seer_turf)) + var/list/datum/light_source/other_lights = list() + for(var/datum/lighting_corner/corner in list(seer_turf.lighting_corner_NE, seer_turf.lighting_corner_SE, seer_turf.lighting_corner_SW, seer_turf.lighting_corner_NW)) + other_lights |= SANITIZE_LIST(corner.affecting) + var/total_power = 0 + for(var/datum/light_source/light as anything in other_lights) + // mob lights are excluded, otherwise ash heretics are punished for lighting people on fire + if(istype(light.source_atom, /obj/effect/dummy/lighting_obj/moblight)) + continue + total_power += light.light_power + // if the power of nearby lights is high, the effect is diminished a bit (giving free levels of eye protection) + seer_eye_prot += max(0, round(total_power / 3)) + + LAZYSET(effect_cds, REF(seer), world.time + 5 SECONDS) + LAZYSET(tick_counts, REF(seer), tick_count + 1) + var/seer_stam = seer.get_stamina_loss() + switch(seer_eye_prot) + if(FLASH_PROTECTION_WELDER_SENSITIVE to INFINITY) + to_chat(seer, span_notice("[(src == center) ? src : "[center]'s [src]"] emits a bright flash of light.")) + return + + if(FLASH_PROTECTION_WELDER) + to_chat(seer, span_danger("[(src == center) ? src : "[center]'s [src]"] emits a bright flash of light, causing you to flinch.")) + seer.adjust_organ_loss(ORGAN_SLOT_EYES, 3, maximum = 30) + + if(FLASH_PROTECTION_FLASH) + to_chat(seer, span_danger("[(src == center) ? src : "[center]'s [src]"] emits a bright flash of light, causing you to flinch.")) + seer.adjust_organ_loss(ORGAN_SLOT_EYES, 4, maximum = 40) + seer.adjust_eye_blur(2 SECONDS) + if(tick_count > 2 && (seer_stam < 60 || (tick_count > 8 && seer_stam < 100))) + seer.adjust_stamina_loss(10) + if(tick_count > 3) + seer.adjust_confusion(2 SECONDS) + + if(FLASH_PROTECTION_NONE) + to_chat(seer, span_danger("A bright flash of light from [(src == center) ? src : "[center]'s [src]"] blinds you for a moment!")) + seer.adjust_organ_loss(ORGAN_SLOT_EYES, 5) + seer.adjust_temp_blindness(1 SECONDS) + seer.adjust_eye_blur(4 SECONDS) + if(tick_count > 2 && (seer_stam < 60 || (tick_count > 8 && seer_stam < 100))) + seer.adjust_stamina_loss(10) + if(tick_count > 3) + seer.adjust_confusion(4 SECONDS) + + if(FLASH_PROTECTION_SENSITIVE) + to_chat(seer, span_danger("A bright flash of light from [(src == center) ? src : "[center]'s [src]"] blinds you!")) + seer.adjust_organ_loss(ORGAN_SLOT_EYES, 8) + seer.adjust_temp_blindness(2 SECONDS) + seer.adjust_eye_blur(8 SECONDS) + if(tick_count > 1 && (seer_stam < 80 || (tick_count > 6 && seer_stam < 120))) + seer.adjust_stamina_loss(10) + if(tick_count > 2) + seer.adjust_confusion(tick_count > 3 ? 8 SECONDS : 4 SECONDS) + + if(-INFINITY to FLASH_PROTECTION_HYPER_SENSITIVE) + to_chat(seer, span_danger("A bright flash of light from [(src == center) ? src : "[center]'s [src]"] blinds you!")) + seer.adjust_organ_loss(ORGAN_SLOT_EYES, 15) + seer.adjust_temp_blindness(4 SECONDS) + seer.adjust_eye_blur(12 SECONDS) + if(seer_stam < 80 || (tick_count > 4 && seer_stam < 120)) + seer.adjust_stamina_loss(30) + if(tick_count > 1) + seer.adjust_confusion(tick_count > 2 ? 12 SECONDS : 6 SECONDS) + + if(!iscarbon(seer)) + return + + var/mob/living/carbon/carbon_seer = seer + // persistent exposure, to the point of causing full blindness, may put us in a trance. + // more sensitive mobs, funnily enough, will be blinded before the trance can set it (this is intentional). + if(tick_count > 6 && prob(66) && seer_eye_prot <= FLASH_PROTECTION_NONE && seer.is_blind_from(EYE_DAMAGE) && !carbon_seer.has_trauma_type(/datum/brain_trauma/hypnosis)) + carbon_seer.gain_trauma(new /datum/brain_trauma/hypnosis(pick_list(HERETIC_INFLUENCE_FILE, "hypnosis")), TRAUMA_RESILIENCE_BASIC) diff --git a/code/modules/antagonists/heretic/items/heretic_necks.dm b/code/modules/antagonists/heretic/items/heretic_necks.dm index ad5ab5f99425..2e6116c92ddf 100644 --- a/code/modules/antagonists/heretic/items/heretic_necks.dm +++ b/code/modules/antagonists/heretic/items/heretic_necks.dm @@ -1,18 +1,45 @@ /obj/item/clothing/neck/heretic_focus name = "amber focus" - desc = "An amber focusing glass that provides a link to the world beyond. The necklace seems to twitch, but only when you look at it from the corner of your eye." + desc = "An amber focusing glass that provides a link to the world beyond. \ + The necklace seems to twitch, but only when you look at it from the corner of your eye." icon_state = "eldritch_necklace" w_class = WEIGHT_CLASS_SMALL resistance_flags = FIRE_PROOF + /// Cooldown between attempts to recharge spells + var/cooldown_period = 3 MINUTES + COOLDOWN_DECLARE(spell_recharge_cd) -/obj/item/clothing/neck/heretic_focus/Initialize(mapload) +/obj/item/clothing/neck/heretic_focus/equipped(mob/living/user, slot) . = ..() - AddElement(/datum/element/heretic_focus) + if(!(slot & ITEM_SLOT_NECK) || !IS_HERETIC(user)) + return + START_PROCESSING(SSobj, src) + COOLDOWN_START(src, spell_recharge_cd, cooldown_period) + +/obj/item/clothing/neck/heretic_focus/dropped(mob/living/user) + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/item/clothing/neck/heretic_focus/process(seconds_per_tick) + if(!COOLDOWN_FINISHED(src, spell_recharge_cd)) + return + + var/mob/living/wearer = loc + if(!istype(wearer) || !IS_HERETIC(wearer)) + return PROCESS_KILL + + var/datum/antagonist/heretic/our_heretic = GET_HERETIC(wearer) + for(var/datum/heretic_knowledge/spell/spell in our_heretic?.get_researched_knowledge()) + spell.add_charges(ceil(spell.max_charges * spell.focus_recharge_amount)) + + COOLDOWN_START(src, spell_recharge_cd, cooldown_period) /obj/item/clothing/neck/heretic_focus/crimson_medallion name = "crimson medallion" - desc = "A blood-red focusing glass that provides a link to the world beyond, and worse. Its eye is constantly twitching and gazing in all directions. It almost seems to be silently screaming..." + desc = "A blood-red focusing glass that provides a link to the world beyond, and worse. \ + Its eye is constantly twitching and gazing in all directions. It almost seems to be silently screaming..." icon_state = "crimson_medallion" + cooldown_period = 1 MINUTES /// The aura healing component. Used to delete it when taken off. var/datum/component/component /// If active or not, used to add and remove its cult and heretic buffs. @@ -98,7 +125,6 @@ . += span_cult_bold("This focus will allow you to store one extra spell and halve the empowering time, alongside providing a small regenerative effect.") magic_dude = TRUE if(IS_HERETIC_OR_MONSTER(user)) - . += span_notice("This focus will halve your spell cooldowns, alongside granting a small regenerative effect to any nearby heretics or monsters, including you.") magic_dude = TRUE if(magic_dude) @@ -106,7 +132,8 @@ /obj/item/clothing/neck/eldritch_amulet name = "warm eldritch medallion" - desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulsing of a thousand others." + desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. \ + You see your own beating heart, and the pulsing of a thousand others." icon = 'icons/obj/antags/eldritch.dmi' icon_state = "eye_medalion" w_class = WEIGHT_CLASS_SMALL @@ -114,10 +141,6 @@ /// A secondary clothing trait only applied to heretics. var/heretic_only_trait = TRAIT_THERMAL_VISION -/obj/item/clothing/neck/eldritch_amulet/Initialize(mapload) - . = ..() - AddElement(/datum/element/heretic_focus) - /obj/item/clothing/neck/eldritch_amulet/equipped(mob/user, slot) . = ..() if(!(slot & ITEM_SLOT_NECK)) @@ -147,7 +170,7 @@ w_class = WEIGHT_CLASS_SMALL // The amulet conversion tool used by moon heretics -/obj/item/clothing/neck/heretic_focus/moon_amulet +/obj/item/clothing/neck/moon_amulet name = "moonlight amulet" desc = "A piece of the mind, the soul and the moon. Gazing into it makes your head spin and hear whispers of laughter and joy." icon = 'icons/obj/antags/eldritch.dmi' @@ -163,12 +186,12 @@ var/valid_weapon_type = /obj/item/melee/sickly_blade var/sanity_threshold = SANITY_LEVEL_INSANE -/obj/item/clothing/neck/heretic_focus/moon_amulet/examine(mob/user) +/obj/item/clothing/neck/moon_amulet/examine(mob/user) . = ..() if(IS_HERETIC(user)) - . += span_notice("Wearing this amulet increases your healing speed by 50%") + . += span_notice("Wearing this amulet increases your healing speed by 50%.") -/obj/item/clothing/neck/heretic_focus/moon_amulet/equipped(mob/living/user, slot) +/obj/item/clothing/neck/moon_amulet/equipped(mob/living/user, slot) . = ..() if(!IS_HERETIC(user) && (slot_flags & slot)) channel_amulet(user) @@ -179,7 +202,7 @@ on_amulet_activate(user) /// Modifies any blades you hold/pickup/drop when the amulet is enabled -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/on_amulet_activate(mob/living/user) +/obj/item/clothing/neck/moon_amulet/proc/on_amulet_activate(mob/living/user) RegisterSignal(user, COMSIG_HERETIC_BLADE_ATTACK, PROC_REF(blade_channel)) RegisterSignal(user, COMSIG_MOB_EQUIPPED_ITEM, PROC_REF(on_equip_item)) RegisterSignal(user, COMSIG_MOB_DROPPED_ITEM, PROC_REF(on_dropped_item)) @@ -187,37 +210,35 @@ on_equip_item(user, user.get_active_held_item(), ITEM_SLOT_HANDS) on_equip_item(user, user.get_inactive_held_item(), ITEM_SLOT_HANDS) ADD_TRAIT(user, TRAIT_THERMAL_VISION, REF(src)) - user.update_sight() // If the equipper is a moon heretic, we buff their passive var/datum/status_effect/heretic_passive/moon/moon_passive = user.has_status_effect(/datum/status_effect/heretic_passive/moon) moon_passive?.amulet_equipped = TRUE /// Modifies any blades you hold/pickup/drop when the amulet is disabled -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/on_amulet_deactivate(mob/living/user) +/obj/item/clothing/neck/moon_amulet/proc/on_amulet_deactivate(mob/living/user) // Make sure to restore the values of any blades we might be holding when our amulet is deactivated on_dropped_item(user, user.get_active_held_item()) on_dropped_item(user, user.get_inactive_held_item()) UnregisterSignal(user, list(COMSIG_HERETIC_BLADE_ATTACK, COMSIG_MOB_EQUIPPED_ITEM, COMSIG_MOB_DROPPED_ITEM)) REMOVE_TRAIT(user, TRAIT_THERMAL_VISION, REF(src)) - user.update_sight() var/datum/status_effect/heretic_passive/moon/moon_passive = user.has_status_effect(/datum/status_effect/heretic_passive/moon) moon_passive?.amulet_equipped = FALSE -/obj/item/clothing/neck/heretic_focus/moon_amulet/dropped(mob/living/user) +/obj/item/clothing/neck/moon_amulet/dropped(mob/living/user) on_amulet_deactivate(user) return ..() -/obj/item/clothing/neck/heretic_focus/moon_amulet/attack(mob/living/target, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/item/clothing/neck/moon_amulet/attack(mob/living/target, mob/living/user, list/modifiers, list/attack_modifiers) if(channel_amulet(user, target)) return return ..() -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/blade_channel(mob/living/attacker, mob/living/victim) +/obj/item/clothing/neck/moon_amulet/proc/blade_channel(mob/living/attacker, mob/living/victim) SIGNAL_HANDLER channel_amulet(attacker, victim) /// Makes whoever the target is a bit more insane. If they are insane enough, they will be zombified into a moon zombie -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/channel_amulet(mob/user, atom/target) +/obj/item/clothing/neck/moon_amulet/proc/channel_amulet(mob/user, atom/target) if(!isliving(user)) return FALSE @@ -255,6 +276,7 @@ human_target.balloon_alert(living_user, "their mind almost bends but something protects it!") human_target.apply_status_effect(/datum/status_effect/moon_slept) return TRUE + SEND_SIGNAL(user, COMSIG_MOB_APPLIED_MOONLIGHT_AMULET, target) human_target.balloon_alert(living_user, "their mind bends to see the truth!") human_target.apply_status_effect(/datum/status_effect/moon_converted) living_user.log_message("made [human_target] insane.", LOG_GAME) @@ -262,7 +284,7 @@ return TRUE /// Modifies any blades that we equip while wearing the amulet -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/on_equip_item(mob/user, obj/item/blade, slot) +/obj/item/clothing/neck/moon_amulet/proc/on_equip_item(mob/user, obj/item/blade, slot) SIGNAL_HANDLER if(!istype(blade, valid_weapon_type)) return // We only care about modifying blades @@ -279,7 +301,7 @@ blade.armour_penetration = initial(blade.armour_penetration) UnregisterSignal(blade, COMSIG_SEND_ITEM_ATTACK_MESSAGE_OBJECT) -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/modify_attack_message(obj/item/weapon, mob/living/victim, mob/living/attacker) +/obj/item/clothing/neck/moon_amulet/proc/modify_attack_message(obj/item/weapon, mob/living/victim, mob/living/attacker) SIGNAL_HANDLER var/list/attack_list = list( @@ -299,7 +321,7 @@ return SIGNAL_MESSAGE_MODIFIED /// Modifies any blades that we drop while wearing the amulet -/obj/item/clothing/neck/heretic_focus/moon_amulet/proc/on_dropped_item(mob/user, obj/item/dropped_item) +/obj/item/clothing/neck/moon_amulet/proc/on_dropped_item(mob/user, obj/item/dropped_item) SIGNAL_HANDLER if(!istype(dropped_item, valid_weapon_type)) return // We only care about modifying blades diff --git a/code/modules/antagonists/heretic/items/madness_mask.dm b/code/modules/antagonists/heretic/items/madness_mask.dm index b9721f0501b7..ccb1a486e02f 100644 --- a/code/modules/antagonists/heretic/items/madness_mask.dm +++ b/code/modules/antagonists/heretic/items/madness_mask.dm @@ -54,7 +54,7 @@ REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT) for(var/mob/living/carbon/human/human_in_range in view(local_user)) - if(IS_HERETIC_OR_MONSTER(human_in_range) || human_in_range.stat > SOFT_CRIT || human_in_range.is_blind()) + if(IS_HERETIC_OR_MONSTER(human_in_range) || IS_UNCONSCIOUS(human_in_range) || human_in_range.is_blind()) continue if(human_in_range.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND)) diff --git a/code/modules/antagonists/heretic/items/voidballd.dm b/code/modules/antagonists/heretic/items/voidballd.dm new file mode 100644 index 000000000000..f88b3e6288e1 --- /dev/null +++ b/code/modules/antagonists/heretic/items/voidballd.dm @@ -0,0 +1,121 @@ +/obj/item/void_prison + name = "void prison" + desc = "A small glass orb of swirling darkness. It feels cold to the touch, and consumes all light around it." + icon = 'icons/mob/actions/actions_ecult.dmi' + icon_state = "voidball" + pickup_sound = 'sound/items/handling/materials/glass_pick_up.ogg' + drop_sound = 'sound/items/handling/materials/glass_drop.ogg' + +/obj/item/void_prison/Initialize(mapload) + . = ..() + transform = transform.Scale(0.5) + +/obj/item/void_prison/attack_self(mob/living/user, modifiers) + . = ..() + if(.) + return + + playsound(src, SFX_SHATTER, 50, TRUE) + playsound(src, 'sound/effects/magic/voidblink.ogg', 50, FALSE) + if(IS_HERETIC_OR_MONSTER(user)) + to_chat(user, span_mansus("You smash [src], releasing its power around you!")) + for(var/mob/living/nearby_mob in view(3, user)) + if(IS_HERETIC_OR_MONSTER(nearby_mob)) + continue + if(nearby_mob.has_status_effect(/datum/status_effect/eldritch)) + continue + if(nearby_mob.can_block_magic(MAGIC_RESISTANCE)) + nearby_mob.visible_message( + span_danger("A swirling, cold void wraps around [nearby_mob], but they burst free in a wave of heat!"), + span_userdanger("A yawning void begins to open before you, but a great wave of heat bursts it apart! You are protected!!") + ) + continue + nearby_mob.visible_message( + span_danger("A swirling, cold void wraps around [nearby_mob]!"), + span_userdanger("A yawning void opens before you! You are swallowed by the darkness, and find yourself in complete nothingness..."), + ) + nearby_mob.apply_status_effect(/datum/status_effect/void_prison) + + else if(user.can_block_magic(MAGIC_RESISTANCE)) + to_chat(user, span_hypnophrase("You smash [src], but its power begins to encompass you!")) + user.visible_message( + span_danger("A swirling, cold void wraps around [user], but they burst free in a wave of heat!"), + span_userdanger("A yawning void begins to open before you, but a great wave of heat bursts it apart! You are protected!!") + ) + + else + to_chat(user, span_hypnophrase("You smash [src], but its power begins to encompass you!")) + user.visible_message( + span_danger("A swirling, cold void wraps around [user]!"), + span_userdanger("A yawning void opens before you! You are swallowed by the darkness, and find yourself in complete nothingness..."), + ) + user.apply_status_effect(/datum/status_effect/void_prison) + + qdel(src) + return TRUE + +/datum/status_effect/void_prison + id = "void_prison" + duration = 10 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/void_prison + ///The overlay that gets applied to whoever has this status active + var/obj/effect/abstract/voidball/stasis_overlay + +/datum/status_effect/void_prison/on_creation(mob/living/new_owner) + . = ..() + stasis_overlay = new /obj/effect/abstract/voidball(new_owner) + RegisterSignal(stasis_overlay, COMSIG_QDELETING, PROC_REF(clear_overlay)) + new_owner.vis_contents += stasis_overlay + stasis_overlay.animate_opening() + addtimer(CALLBACK(src, PROC_REF(enter_prison), new_owner), 1 SECONDS) + +/datum/status_effect/void_prison/on_remove() + if(!IS_HERETIC(owner)) + owner.apply_status_effect(/datum/status_effect/void_chill, 1) + if(stasis_overlay) + //Free our prisoner + owner.remove_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM, TRAIT_FORCE_WHISPER), TRAIT_STATUS_EFFECT(id)) + owner.forceMove(get_turf(stasis_overlay)) + stasis_overlay.forceMove(owner) + owner.vis_contents += stasis_overlay + //Animate closing the ball + stasis_overlay.animate_closing() + stasis_overlay.icon_state = "voidball_closed" + QDEL_IN(stasis_overlay, 1.1 SECONDS) + stasis_overlay = null + return ..() + +///Freezes our prisoner in place +/datum/status_effect/void_prison/proc/enter_prison(mob/living/prisoner) + stasis_overlay.forceMove(prisoner.loc) + prisoner.forceMove(stasis_overlay) + prisoner.add_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM, TRAIT_FORCE_WHISPER), TRAIT_STATUS_EFFECT(id)) + +///Makes sure to clear the ref in case the voidball ever suddenly disappears +/datum/status_effect/void_prison/proc/clear_overlay() + SIGNAL_HANDLER + stasis_overlay = null + +//----Voidball effect +/obj/effect/abstract/voidball + icon = 'icons/mob/actions/actions_ecult.dmi' + icon_state = "voidball_effect" + layer = ABOVE_ALL_MOB_LAYER + vis_flags = VIS_INHERIT_ID + +///Plays a opening animation +/obj/effect/abstract/voidball/proc/animate_opening() + flick("voidball_opening", src) + +///Plays a closing animation +/obj/effect/abstract/voidball/proc/animate_closing() + flick("voidball_closing", src) + +//---- Screen alert +/atom/movable/screen/alert/status_effect/void_prison + name = "Void Prison" + desc = "A Yawning void encases your mortal coil." //Go straight to jail, do not pass GO, do not collect 200$ + use_user_hud_icon = USER_HUD_STYLE_INHERIT + icon_state = "heretic_template" + overlay_icon = 'icons/mob/actions/actions_ecult.dmi' + overlay_state = "voidball_effect" diff --git a/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm index ed4c7b97dd35..243e1d579469 100644 --- a/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm +++ b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm @@ -90,6 +90,7 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) var/datum/status_effect/heretic_passive/passive = new start.eldritch_passive() data["passive"] = list( "name" = initial(passive.name), + "recharge" = initial(passive.recharge_description), "description" = passive.passive_descriptions.Copy(), ) qdel(passive) @@ -294,6 +295,7 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) list( "parent_knowledge" = knowledge_tier1, "guaranteed_knowledge" = guaranteed_draft_t1, + "supplementary_knowledge" = list(/datum/heretic_knowledge/spell/cloak_of_shadows), "probabilities" = list("1" = 50, "2" = 50, "3" = 0, "4" = 0, "5" = 0), HKT_DEPTH = HKT_DEPTH_DRAFT_1, ), @@ -316,13 +318,20 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) ) ) /// generate 3 drafts for each draft tier, while banning you from picking multiple drafts - for(var/draft in drafts) + for(var/list/draft as anything in drafts) var/parent_knowledge_path = draft["parent_knowledge"] var/datum/heretic_knowledge/guaranteed_draft = draft["guaranteed_knowledge"] var/list/probabilities = draft["probabilities"] var/depth = draft[HKT_DEPTH] var/list/draft_blacklist = list() + for(var/datum/heretic_knowledge/supplementary as anything in draft["supplementary_knowledge"]) + final_draft[supplementary] = make_knowledge_entry(supplementary, null, HERETIC_KNOWLEDGE_DRAFT, depth, 0) + final_draft[supplementary][HKT_PURCHASED_DEPTH] = supplementary::drafting_tier + var/supplementary_id = final_draft[supplementary][HKT_ID] + draft_blacklist[supplementary] = supplementary_id + heretic_research_tree[parent_knowledge_path][HKT_NEXT] |= supplementary_id + for(var/cycle in 1 to 3) var/datum/heretic_knowledge/selected_knowledge if(guaranteed_draft && cycle == 1) @@ -343,21 +352,14 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) stack_trace("Failed to select a knowledge for heretic path [heretic_path] at depth [depth]. This should never happen.") continue - final_draft[selected_knowledge] = make_knowledge_entry( - selected_knowledge, - null, - HERETIC_KNOWLEDGE_DRAFT, - depth, - 0, - ) + final_draft[selected_knowledge] = make_knowledge_entry(selected_knowledge, null, HERETIC_KNOWLEDGE_DRAFT, depth, 0) final_draft[selected_knowledge][HKT_PURCHASED_DEPTH] = selected_knowledge::drafting_tier var/draft_id = final_draft[selected_knowledge][HKT_ID] draft_blacklist[selected_knowledge] = draft_id heretic_research_tree[parent_knowledge_path][HKT_NEXT] |= draft_id var/list/blacklist_ids = assoc_to_values(draft_blacklist) - for(var/blacklist_path in draft_blacklist) - var/id = draft_blacklist[blacklist_path] + for(var/blacklist_path, id in draft_blacklist) final_draft[blacklist_path][HKT_BAN] += (blacklist_ids - id) // all possible drafts are added to the shop, this time with costs diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm index 6579c857b8db..7fe8e33bb9dc 100644 --- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm @@ -27,9 +27,7 @@ tips = list( "Your Mansus Grasp applies a short blind and a mark that puts your opponent into stamina crit when triggered by your blade. The mark can spread to nearby opponents.", "Selecting this path makes you immune to high temperature damage. Remember, however, that your clothes can still burn! If you want to protect yourself from your own fire, wear a Scorched Mantle.", - "Your Scorched Mantle will cause you to generate firestacks on your own body (Make sure you toggle the effect!). Upon reaching 5 fire stacks, your ashen spells will be empowered (indicated by your spells being highlighted in green).", - "Your Ashen passage is a short cooldown jaunt capable of removing restraints. If empowered, it gains a longer jaunt time, and also will remove stuns and stamina crit.", - "Volcano blast can make short work of your enemies, should they be foolish enough to stick close to each other. If empowered, it will have no cast time and generate twice the amount of firestacks. Burn the heathens to ashes!", + "Your Scorched Mantle will keep you on fire while protecting you from the ill effects of it. Use this to your advantage by running into groups of enemies, spreading fire far and wide.", "Do not neglect the Mask of Madness. It will slowly sap the stamina of your enemies and make them hallucinate.", "Make sure to set as many enemies on fire as you possibly can! Nightwatcher's Rebirth will heal you and have its cooldown reduced based on how many mobs you siphon.", "Your ascension grants you complete immunity to environmental hazards, including bombs! But you are still vulnerable to more conventional weaponry. Do not become overconfident.", @@ -41,7 +39,7 @@ knowledge_tier2 = /datum/heretic_knowledge/spell/fire_blast guaranteed_side_tier2 = /datum/heretic_knowledge/rifle robes = /datum/heretic_knowledge/armor/ash - knowledge_tier3 = /datum/heretic_knowledge/mad_mask + knowledge_tier3 = /datum/heretic_knowledge/nightwatchers_lantern guaranteed_side_tier3 = /datum/heretic_knowledge/summon/ashy blade = /datum/heretic_knowledge/blade_upgrade/ash knowledge_tier4 = /datum/heretic_knowledge/spell/flame_birth @@ -49,9 +47,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_ash name = "Nightwatcher's Secret" - desc = "Opens up the Path of Ash to you. \ - Allows you to transmute a match and a knife into an Ashen Blade. \ + desc = "Opens up the Path of Ash to you.
    \ + Allows you to create a Ashen Blades. \ You can only create two at a time." + transmute_text = "Transmute a match and a knife." gain_text = "The City Guard know their watch. If you ask them at night, they may tell you about the ashy lantern." required_atoms = list( /obj/item/knife = 1, @@ -89,32 +88,35 @@ /datum/heretic_knowledge/spell/ash_passage name = "Ashen Passage" - desc = "Grants you Ashen Passage, a spell that lets you phase out of reality, allowing you to traverse a short distance, passing though any walls. \ - When empowered, it will break you out of any stuns and restraints, and will have a longer range." + desc = "Grants you Ashen Passage, a spell that lets you phase out of reality, \ + allowing you to traverse a short distance, passing though any walls." gain_text = "He knew how to walk between the planes." - action_to_add = /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash cost = 2 drafting_tier = 5 + max_charges = 6 + focus_recharge_amount = 0.15 + holywater_drain_amount = 0.15 /datum/heretic_knowledge/spell/fire_blast name = "Volcano Blast" desc = "Grants you Volcano Blast, a spell that - after a short charge - fires off a beam of energy \ at a nearby enemy, setting them on fire and burning them. If they do not extinguish themselves, \ - the beam will continue to another target. \ - When empowered, has instant cast time and blasts enemies with more flames." + the beam will continue to another target." gain_text = "No fire was hot enough to rekindle them. No fire was bright enough to save them. No fire is eternal." action_to_add = /datum/action/cooldown/spell/charged/beam/fire_blast cost = 2 research_tree_icon_frame = 7 + max_charges = 3 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.16 /datum/heretic_knowledge/armor/ash - desc = "Allows you to transmute a table (or a suit), a mask and a match to create a scorched mantle. \ - It provides completes protection from fire, and is able to produce more flames passively. \ - When you have enough fire, you may cast empowered versions of your ashen spells. \ - Acts as a focus while hooded." + desc = "Create a Scorched Mantle.
    \ + It provides completes protection from fire, and is able to produce more flames passively." + transmute_text = "Transmute a table (or a suit), a mask and a match." gain_text = "The Watch remain as they fell, crumbling away from sight. \ - Yet the winds blowing through the city call them back to service, dust kicked into the air, a drifting silhouette of the fallen." + Yet the winds blowing through the city call them back to service, dust kicked into the air, a drifting silhouette of the fallen." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/ash) research_tree_icon_state = "ash_armor" required_atoms = list( @@ -123,22 +125,35 @@ /obj/item/match = 1, ) -/datum/heretic_knowledge/mad_mask - name = "Mask of Madness" - desc = "Allows you to transmute any mask, four candles, a stun baton, and a liver to create a Mask of Madness. \ - The mask instills fear into heathens who witness it, causing stamina damage, hallucinations, and insanity. \ - It can also be forced onto a heathen, to make them unable to take it off..." - gain_text = "The Nightwatcher was lost. That's what the Watch believed. Yet he walked the world, unnoticed by the masses." +/datum/heretic_knowledge/nightwatchers_lantern + name = "Nightwatcher's Lantern" + desc = "Create a burning lantern.
    \ + A burning lantern is a bright light that damages the eyes and eventually confuses those who witness it for too long. \ + The effect is reduced for those with protective eyewear, and strengthened if the burning lantern is the only nearby source of light." + transmute_text = "Transmute a lamp, lantern, or seclight, a pair of eyes, a flash, and four lit candles." + gain_text = "The Nightwatcher did not venture out in the dark. That was foolish, even the Watch knew that. \ + Their lantern burned with a light that could burn the sun." + cost = 2 + result_atoms = list(/obj/item/flashlight/lantern/heretic) required_atoms = list( - /obj/item/organ/liver = 1, - /obj/item/melee/baton/security = 1, // Technically means a cattleprod is valid - /obj/item/clothing/mask = 1, + list(/obj/item/flashlight/lamp, /obj/item/flashlight/lantern, /obj/item/flashlight/seclite) = 1, + /obj/item/organ/eyes = 1, + /obj/item/assembly/flash = 1, /obj/item/flashlight/flare/candle = 4, ) - result_atoms = list(/obj/item/clothing/mask/madness_mask) - cost = 2 - research_tree_icon_path = 'icons/obj/clothing/masks.dmi' - research_tree_icon_state = "mad_mask" + research_tree_icon_path = 'icons/obj/lighting.dmi' + research_tree_icon_state = "lantern" + +/datum/heretic_knowledge/nightwatchers_lantern/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + . = ..() + for(var/obj/item/flashlight/flare/candle/candle in atoms) + if(!candle.light_on) + atoms -= candle + +/datum/heretic_knowledge/nightwatchers_lantern/prepare_atom_for_ritual_test(atom/what) + . = ..() + if(istype(what, /obj/item/flashlight/flare/candle)) + what.set_light_on(TRUE) /datum/heretic_knowledge/blade_upgrade/ash name = "Fiery Blade" @@ -146,7 +161,6 @@ gain_text = "He returned, blade in hand, he swung and swung as the ash fell from the skies. \ His city, the people he swore to watch... and watch he did, as they all burnt to cinders." - research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_ash" @@ -160,7 +174,7 @@ /datum/heretic_knowledge/spell/flame_birth name = "Nightwatcher's Rebirth" desc = "Grants you Nightwatcher's Rebirth, a spell that extinguishes you and \ - burns all nearby heathens who are currently on fire, healing you for every victim afflicted. \ + burns all nearby heathens who are currently on fire, healing you for every victim afflicted.
    \ If any victims afflicted are in critical condition, they will also instantly die." gain_text = "The fire was inescapable, and yet, life remained in his charred body. \ The Nightwatcher was a particular man, always watching." @@ -168,16 +182,18 @@ cost = 2 research_tree_icon_frame = 5 is_final_knowledge = TRUE + max_charges = 3 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.16 /datum/heretic_knowledge/ultimate/ash_final name = "Ashlord's Rite" - desc = "The ascension ritual of the Path of Ash. \ - Bring 3 burning or husked corpses to a transmutation rune to complete the ritual. \ - When completed, you become a harbinger of flames, gaining two abilites. \ + desc = "The ascension ritual of the Path of Ash.
    \ + When completed, you become a harbinger of flames, gaining two abilites.
    \ Cascade, which causes a massive, growing ring of fire around you, \ - and Oath of Flame, causing you to passively create a ring of flames as you walk. \ - Some ashen spells you already knew will be empowered as well. \ + and Oath of Flame, causing you to passively create a ring of flames as you walk.
    \ You will also become immune to flames, space, and similar environmental hazards." + transmute_text = "Transmute 3 burning or husked corpses." gain_text = "The Watch is dead, the Nightwatcher burned with it. Yet his fire burns evermore, \ for the Nightwatcher brought forth the rite to mankind! His gaze continues, as now I am one with the flames, \ WITNESS MY ASCENSION, THE ASHY LANTERN BLAZES ONCE MORE!" diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm index 495d994f1372..8d97e5d3d1e0 100644 --- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm @@ -52,9 +52,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_blade name = "The Cutting Edge" - desc = "Opens up the Path of Blades to you. \ - Allows you to transmute a knife with one bar of silver or titanium to create a Sundered Blade. \ + desc = "Opens up the Path of Blades to you.
    \ + Allows you to create Sundered Blades. \ You can create up to four at a time." + transmute_text = "Transmute a knife with one bar of silver or titanium." gain_text = "Our great ancestors forged swords and practiced sparring on the eve of great battles." required_atoms = list( /obj/item/knife = 1, @@ -95,19 +96,22 @@ /datum/heretic_knowledge/spell/realignment name = "Realignment" - desc = "Grants you Realignment a spell that wil realign your body rapidly for a short period. \ - During this process, you will rapidly regenerate stamina and quickly recover from stuns, however, you will be unable to attack. \ + desc = "Grants you Realignment, a spell that wil realign your body rapidly for a short period.
    \ + During this process, you will rapidly regenerate stamina and quickly recover from stuns, however, you will be unable to attack.
    \ This spell can be cast in rapid succession, but doing so will increase the cooldown." gain_text = "In the flurry of death, he found peace within himself. Despite insurmountable odds, he forged on." action_to_add = /datum/action/cooldown/spell/realignment cost = 2 + max_charges = 3 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.16 /// The amount of blood flow reduced per level of severity of gained bleeding wounds for Stance of the Torn Champion. #define BLOOD_FLOW_PER_SEVEIRTY -1 /datum/heretic_knowledge/duel_stance name = "Stance of the Torn Champion" - desc = "Grants resilience to blood loss from wounds and immunity to having your limbs dismembered. \ + desc = "Grants resilience to blood loss from wounds and immunity to having your limbs dismembered.
    \ Additionally, when damaged below 50% of your maximum health, \ you gain increased resistance to gaining wounds and resistance to slowdown." gain_text = "In time, it was he who stood alone among the bodies of his former comrades, awash in blood, none of it his own. \ @@ -173,11 +177,11 @@ #undef BLOOD_FLOW_PER_SEVEIRTY /datum/heretic_knowledge/armor/blade - desc = "Allows you to transmute a table (or a suit), a mask and a sheet of titanium or silver to create a Shattered Panoply. \ - Provides baton resistance and shock insulation while worn. \ - Acts as a focus while hooded." + desc = "Create a Shattered Panoply.
    \ + Provides baton resistance and shock insulation while worn." + transmute_text = "Transmute a table (or a suit), a mask and a sheet of titanium or silver." gain_text = "The echoing, directionless cacophony of violence reverberates about me. \ - Even as the Champion's steel panoply was torn from their form, each piece craves purpose still, seeking to intercept unseen or imagined attackers." + Even as the Champion's steel panoply was torn from their form, each piece craves purpose still, seeking to intercept unseen or imagined attackers." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/blade) research_tree_icon_state = "blade_armor" required_atoms = list( @@ -188,10 +192,10 @@ /datum/heretic_knowledge/spell/wolves_among_sheep name = "Wolves Among Sheep" - desc = "Alters the fabric of reality, conjuring a magical arena unpassable to outsiders, \ - all participants are trapped and immune to any form of crowd control or enviromental hazards; \ + desc = "Alters the fabric of reality, conjuring a magical arena impassable to outsiders, \ + all participants are trapped and immune to any form of crowd control or environmental hazards; \ trapped participants are granted a Blade and are unable to leave or jaunt until they score a critical hit. \ - Critical hits partially restore the Heretic's health." + Knocking combatant into critical condition partially restore the Heretic's health." gain_text = "Shadows crawl across the room, casting every chair, table \ and console into the looming shape of another traitorous hand. \ I have made an enemy of all, and peace will never be known to me \ @@ -200,12 +204,34 @@ cost = 2 action_to_add = /datum/action/cooldown/spell/wolves_among_sheep is_final_knowledge = TRUE + max_charges = 4 + path_recharge_amount = 0.0 + holywater_drain_amount = 0.25 + transmute_text = "You are rewarded with one charge for every sacrifice you complete. \ + You will also be rewarded with a charge if you knock at least three combatants into critical condition while the spell is active. \ + You may only have up to four charges at once." + +/datum/heretic_knowledge/spell/wolves_among_sheep/New() + . = ..() + charges = 1 // start with one, can go up to four + +/datum/heretic_knowledge/spell/wolves_among_sheep/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + RegisterSignal(our_heretic, COMSIG_HERETIC_SACRIFICE, PROC_REF(on_sacrifice)) + +/datum/heretic_knowledge/spell/wolves_among_sheep/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + UnregisterSignal(our_heretic, COMSIG_HERETIC_SACRIFICE) + +/datum/heretic_knowledge/spell/wolves_among_sheep/proc/on_sacrifice(datum/source, mob/living/sacrifice, high_value) + SIGNAL_HANDLER + add_charges(1) /datum/heretic_knowledge/blade_upgrade/blade name = "Empowered Blades" desc = "Attacking someone with a Sundered Blade in both hands \ - will now deliver a blow with both at once, dealing two attacks in rapid succession. \ - The second blow will be slightly weaker. \ + will now deliver a blow with both at once, dealing two attacks in rapid succession.
    \ + The second blow will be slightly weaker.
    \ You are able to infuse your mansus grasp directly into your blades, and your blades are more effective against structures." gain_text = "I found him cleaved in twain, halves locked in a duel without end; \ a flurry of blades, neither hitting their mark, for the Champion was indomitable." @@ -299,23 +325,44 @@ /datum/heretic_knowledge/spell/furious_steel name = "Furious Steel" desc = "Grants you Furious Steel, a targeted spell. Using it will summon three \ - orbiting blades around you. These blades will protect you from all attacks, \ - but are consumed on use. Additionally, you can click to fire the blades \ + orbiting blades around you.
    These blades will protect you from all attacks, \ + but are consumed on use.
    You can click to fire the blades \ at a target, dealing damage and causing bleeding." gain_text = "Without thinking, I took the knife of a fallen soldier and threw with all my might. My aim was true! \ The Torn Champion smiled at their first taste of agony, and with a nod, their blades became my own." + required_atoms = list(/obj/item/knife = 1) action_to_add = /datum/action/cooldown/spell/pointed/projectile/furious_steel cost = 2 + max_charges = 6 + recharge_amount = 0.5 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.16 + transmute_text = "Can be manually recharged by completing a ritual with a knife - this will return half of the spell's maximum charges." + +/datum/heretic_knowledge/spell/furious_steel/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + RegisterSignal(user, COMSIG_MOB_BLADE_BARRIER_TRIGGERED, PROC_REF(blade_barrier_triggered)) + +/datum/heretic_knowledge/spell/furious_steel/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + UnregisterSignal(user, COMSIG_MOB_BLADE_BARRIER_TRIGGERED) + +/datum/heretic_knowledge/spell/furious_steel/proc/blade_barrier_triggered(mob/living/target, datum/status_effect/barrier) + SIGNAL_HANDLER + + var/datum/action/cooldown/spell/pointed/projectile/furious_steel/spell = created_action_ref + if(spell?.blade_effect == barrier) + remove_charges(1) /datum/heretic_knowledge/ultimate/blade_final name = "Maelstrom of Silver" - desc = "The ascension ritual of the Path of Blades. \ - Bring 3 corpses with either no head or a split skull to a transmutation rune to complete the ritual. \ + desc = "The ascension ritual of the Path of Blades.
    \ When completed, you will be surrounded in a constant, regenerating orbit of blades. \ - These blades will protect you from all attacks, but are consumed on use. \ - Your Furious Steel spell will also have a shorter cooldown. \ - Additionally, you become a master of combat, gaining full wound immunity and the ability to shrug off short stuns. \ + These blades will protect you from all attacks, but are consumed on use.
    \ + Your Furious Steel spell will also have a shorter cooldown.
    \ + Additionally, you become a master of combat, gaining full wound immunity and the ability to shrug off short stuns.
    \ Your Sundered Blades deal bonus damage and heal you on attack for a portion of the damage dealt." + transmute_text = "Transmute three corpses with either no head or a split skull." gain_text = "The Torn Champion is freed! I will become the blade reunited, and with my greater ambition, \ I AM UNMATCHED! A STORM OF STEEL AND SILVER IS UPON US! WITNESS MY ASCENSION!" diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm index 7acf182d30ef..d62814767c30 100644 --- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm @@ -49,9 +49,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_cosmic name = "Eternal Gate" - desc = "Opens up the Path of Cosmos to you. \ - Allows you to transmute a sheet of plasma and a knife into an Cosmic Blade. \ + desc = "Opens up the Path of Cosmos to you.
    \ + Allows you to create Cosmic Blades. \ You can only create two at a time." + transmute_text = "Transmute a sheet of plasma and a knife." gain_text = "A nebula appeared in the sky, its infernal birth shone upon me. This was the start of a great transcendence." required_atoms = list( /obj/item/knife = 1, @@ -81,6 +82,7 @@ action_to_add = /datum/action/cooldown/spell/cosmic_rune cost = 2 drafting_tier = 5 + max_charges = INFINITY /datum/heretic_knowledge/spell/star_blast name = "Star Blast" @@ -89,13 +91,17 @@ gain_text = "The Beast was behind me now at all times, with each sacrifice words of affirmation coursed through me." action_to_add = /datum/action/cooldown/spell/pointed/projectile/star_blast cost = 2 + max_charges = 4 + path_recharge_amount = 0.5 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 /datum/heretic_knowledge/armor/cosmic - - desc = "Allows you to transmute a table (or a suit), a mask and a sheet of plasma to create a Starwoven Cloak, grants protection from the hazards of space while granting to the user the ability to levitate at will. \ - Acts as a focus while hooded." + desc = "Create a Starwoven Cloak.
    Grants protection from the hazards of space \ + while granting to the user the ability to levitate at will." + transmute_text = "Transmute a table (or a suit), a mask and a sheet of plasma." gain_text = "Like radiant cords, the stars shone in union across the silken shape of a billowing cloak, that at once does and does not drape my shoulders. \ - The eyes of the Beast rested upon me, and through me." + The eyes of the Beast rested upon me, and through me." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/cosmic) research_tree_icon_state = "cosmic_armor" required_atoms = list( @@ -114,6 +120,10 @@ My veins now emitted a strange purple glow, the Beast knows I will surpass its expectations." action_to_add = /datum/action/cooldown/spell/touch/star_touch cost = 2 + max_charges = 4 + path_recharge_amount = 0.5 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 /datum/heretic_knowledge/blade_upgrade/cosmic name = "Cosmic Blade" @@ -214,18 +224,21 @@ action_to_add = /datum/action/cooldown/spell/conjure/cosmic_expansion cost = 2 is_final_knowledge = TRUE + max_charges = 2 + path_recharge_amount = 0.25 + holywater_drain_amount = 0.25 /datum/heretic_knowledge/ultimate/cosmic_final name = "Creators's Gift" - desc = "The ascension ritual of the Path of Cosmos. \ - Bring 3 corpses with a star mark to a transmutation rune to complete the ritual. \ + desc = "The ascension ritual of the Path of Cosmos.
    \ When completed, you become the owner of a Star Gazer. \ You will be able to command the Star Gazer with Alt+click. \ - You can also give it commands through speech. \ + You can also give it commands through speech.
    \ The Star Gazer is a strong ally who can even break down reinforced walls. \ - The Star Gazer has an aura that will heal you and damage opponents. \ - Star Touch can now teleport you to the Star Gazer when activated in your hand. \ + The Star Gazer has an aura that will heal you and damage opponents.
    \ + Star Touch can now teleport you to the Star Gazer when activated in your hand.
    \ Your cosmic expansion spell and your blades also become greatly empowered." + transmute_text = "Transmute three corpses with a star mark on them." gain_text = "The Beast held out its hand, I grabbed hold and they pulled me to them. Their body was towering, but it seemed so small and feeble after all their tales compiled in my head. \ I clung on to them, they would protect me, and I would protect it. \ I closed my eyes with my head laid against their form. I was safe. \ @@ -314,14 +327,14 @@ var/mob/living/to_reset = bad_dog.resolve() - to_chat(owner, span_hierophant("You prompt [to_reset] to shift it\'s personality...")) + to_chat(owner, span_mansus("You prompt [to_reset] to shift it\'s personality...")) var/mob/chosen_one = SSpolling.poll_ghost_candidates("Do you want to play as [span_danger("[owner.real_name]'s")] [span_notice(to_reset.name)]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, alert_pic = to_reset, jump_target = owner, role_name_text = to_reset.name, amount_to_pick = 1) if(isnull(chosen_one)) - to_chat(owner, span_hierophant("Your request to shift [to_reset]'\s personality appears to have been denied... Looks like you're stuck with it for now.")) + to_chat(owner, span_mansus("Your request to shift [to_reset]'\s personality appears to have been denied... Looks like you're stuck with it for now.")) StartCooldown() return FALSE - to_chat(to_reset, span_hierophant("Your summoner reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.")) - to_chat(owner, span_hierophant("The mind of [to_reset] has twisted itself to suit you better.")) + to_chat(to_reset, span_mansus("Your summoner reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.")) + to_chat(owner, span_mansus("The mind of [to_reset] has twisted itself to suit you better.")) message_admins("[key_name_admin(chosen_one)] has taken control of ([ADMIN_LOOKUPFLW(to_reset)])") to_reset.ghostize(FALSE) to_reset.PossessByPlayer(chosen_one.key) diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index f5282015b0a8..97bde95a079c 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -51,9 +51,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_flesh name = "Principle of Hunger" - desc = "Opens up the Path of Flesh to you. \ - Allows you to transmute a knife and a pool of blood into a Bloody Blade. \ + desc = "Opens up the Path of Flesh to you.
    \ + Allows you to create Bloody Blades. \ You can only create three at a time." + transmute_text = "Transmute a knife and a pool of blood." gain_text = "Hundreds of us starved, but not me... I found strength in my greed." required_atoms = list( /obj/item/knife = 1, @@ -72,7 +73,7 @@ summon_objective.owner = our_heretic.owner our_heretic.objectives += summon_objective - to_chat(user, span_hierophant("Undertaking the Path of Flesh, you are given another objective.")) + to_chat(user, span_mansus("Undertaking the Path of Flesh, you are given another objective.")) our_heretic.owner.announce_objectives() /datum/heretic_knowledge/limited_amount/starting/base_flesh/on_mansus_grasp(mob/living/source, mob/living/target) @@ -128,14 +129,15 @@ /datum/heretic_knowledge/limited_amount/flesh_ghoul name = "Imperfect Ritual" - desc = "Allows you to transmute a corpse and a poppy to create a Voiceless Dead. \ - The corpse does not need to have a soul. \ + desc = "Enchant a corpse into a Voiceless Dead. \ + The corpse does not need to have a soul.
    \ Voiceless Dead are mute ghouls and only have 50 health, but can use Bloody Blades effectively. \ You can only create two at a time." + transmute_text = "Transmute a corpse and a poppy." gain_text = "I found notes of a dark ritual, unfinished... yet still, I pushed forward." required_atoms = list( /mob/living/carbon/human = 1, - /obj/item/food/grown/poppy = 1, + /obj/item/food/grown/flower/poppy = 1, ) limit = 2 cost = 2 @@ -212,21 +214,23 @@ /datum/heretic_knowledge/spell/flesh_surgery name = "Knitting of Flesh" - desc = "Grants you the spell Knit Flesh. This spell allows you to remove organs from victims \ - without requiring a lengthy surgery. This process is much longer if the target is not dead. \ + desc = "Grants you the spell Knit Flesh.
    This spell allows you to remove organs from victims \ + without requiring a lengthy surgery.
    This process is much longer if the target is not dead.
    \ This spell also allows you to heal your minions and summons, or restore failing organs to acceptable status." gain_text = "But they were not out of my reach for long. With every step, the screams grew, until at last \ I learned that they could be silenced." action_to_add = /datum/action/cooldown/spell/touch/flesh_surgery cost = 2 drafting_tier = 5 + max_charges = INFINITY /datum/heretic_knowledge/armor/flesh - desc = "Allows you to transmute a table (or a suit), a mask and a pool of blood to create a writhing embrace. \ - It grants you the ability to detect the health condition of other living (and non-living) and an aura that slowly heals your summons. \ - Acts as a focus while hooded." + desc = "Create a Writhing Embrace.
    \ + It grants you the ability to detect the health condition of other living (and non-living) \ + and an aura that slowly heals your summons." + transmute_text = "Transmute a table (or a suit), a mask and a pool of blood." gain_text = "I tugged these wretched, slothing things about me, like one might a warm blanket. \ - With eyes-not-mine, they will witness. With teeth-not-mine, they will clench. With limbs-not-mine, they will break." + With eyes-not-mine, they will witness. With teeth-not-mine, they will clench. With limbs-not-mine, they will break." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/flesh) research_tree_icon_state = "flesh_armor" required_atoms = list( @@ -237,9 +241,10 @@ /datum/heretic_knowledge/summon/raw_prophet name = "Raw Ritual" - desc = "Allows you to transmute a pair of eyes, a left arm, and a pool of blood to create a Raw Prophet. \ + desc = "Summon a Raw Prophet.
    \ Raw Prophets have a greatly increased sight range and x-ray vision, as well as a long range jaunt and \ the ability to link minds to communicate with ease, but are very fragile and weak in combat." + transmute_text = "Transmute a pair of eyes, a left arm, and a pool of blood." gain_text = "I could not continue alone. I was able to summon The Uncanny Man to help me see more. \ The screams... once constant, now silenced by their wretched appearance. Nothing was out of reach." required_atoms = list( @@ -273,8 +278,9 @@ /datum/heretic_knowledge/summon/stalker name = "Lonely Ritual" - desc = "Allows you to transmute a tail of any kind, a stomach, a tongue, a pen and a piece of paper to create a Stalker. \ + desc = "Summon a Stalker.
    \ Stalkers can jaunt, release EMPs, shapeshift into animals or automatons, and are strong in combat." + transmute_text = "Transmute a tail of any kind, a stomach, a tongue, a pen and a piece of paper." gain_text = "I was able to combine my greed and desires to summon an eldritch beast I had never seen before. \ An ever shapeshifting mass of flesh, it knew well my goals. The Marshal approved." @@ -293,14 +299,14 @@ /datum/heretic_knowledge/ultimate/flesh_final name = "Priest's Final Hymn" - desc = "The ascension ritual of the Path of Flesh. \ - Bring 4 corpses to a transmutation rune to complete the ritual. \ + desc = "The ascension ritual of the Path of Flesh.
    \ When completed, you gain the ability to shed your human form \ and become the Lord of the Night, a supremely powerful creature. \ - Just the act of transforming causes nearby heathens great fear and trauma. \ - While in the Lord of the Night form, you can consume arms to heal and regain segments. \ + Just the act of transforming causes nearby heathens great fear and trauma.
    \ + While in the Lord of the Night form, you can consume arms to heal and regain segments.
    \ Additionally, you can summon three times as many Ghouls and Voiceless Dead, \ and can create unlimited blades to arm them all." + transmute_text = "Transmute four corpses." gain_text = "With the Marshal's knowledge, my power had peaked. The throne was open to claim. \ Men of this world, hear me, for the time has come! The Marshal guides my army! \ Reality will bend to THE LORD OF THE NIGHT or be unraveled! WITNESS MY ASCENSION!" diff --git a/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm b/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm index 243c84b2ed91..3797909b34a6 100644 --- a/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm @@ -1,7 +1,8 @@ /datum/heretic_knowledge/armor name = "Armorer's Ritual" - desc = "Allows you to transmute a table (or a suit) and a mask to create Eldritch Armor. \ + desc = "Create a set of Eldritch Armor. \ Eldritch Armor provides great protection while also acting as a focus when hooded." + transmute_text = "Transmute a table (or a suit) and a mask." gain_text = "The Rusted Hills welcomed the Blacksmith in their generosity. And the Blacksmith \ returned their generosity in kind." diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm index f83bfbdb4b2f..25e2af0ac6c6 100644 --- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm @@ -47,10 +47,11 @@ /datum/heretic_knowledge/limited_amount/starting/base_lock name = "A Steward's Secret" - desc = "Opens up the Path of Lock to you. \ - Allows you to transmute a knife and a crowbar into a Key Blade. \ + desc = "Opens up the Path of Lock to you.
    \ + Allows you to create Key Blades. \ You can only create two at a time and they function as fast crowbars. \ In addition, they can fit into utility belts." + transmute_text = "Transmute a knife and a crowbar." gain_text = "The Locked Labyrinth leads to freedom. But only the trapped Stewards know the correct path." required_atoms = list( /obj/item/knife = 1, @@ -118,13 +119,16 @@ return COMPONENT_USE_HAND /datum/heretic_knowledge/key_ring - name = "Key Keeper’s Burden" - desc = "Allows you to transmute a wallet, an iron rod, and an ID card to create an Eldritch Card. \ - Hit a pair of airlocks with it to create a pair of portals, which will teleport you between them, but teleport non-heretics randomly. \ + name = "Key Keeper's Burden" + desc = "Enchants an ID card into an Eldritch Card.
    \ + Hit a pair of airlocks with it to create a pair of portals, \ + which will teleport you between them, but teleport non-heretics randomly. \ You can ctrl-click the card to invert this behavior for created portals. \ - Each card may only sustain a single pair of portals at the same time. \ + Each card may only sustain a single pair of portals at the same time.
    \ It also functions and appears the same as a regular ID Card. \ - Attacking it with a normal ID card consumes it and gains its access, and you can use it in-hand to change its appearance to a card you fused." + Attacking it with a normal ID card consumes it and gains its access, \ + and you can use it in-hand to change its appearance to a card you fused." + transmute_text = "Transmute a wallet, an iron rod, and an ID card." gain_text = "The Keeper sneered. \"These plastic rectangles are a mockery of keys, and I curse every door that desires them.\"" required_atoms = list( /obj/item/storage/wallet = 1, @@ -150,8 +154,10 @@ /datum/heretic_knowledge/limited_amount/concierge_rite name = "Concierge's Rite" - desc = "Allows you to transmute a crayon, a wooden plank, and a multitool to create a Labyrinth Handbook. \ - It can materialize a barricade at range that only you and people resistant to magic can pass. Has 5 charges which regenerate over time." + desc = "Author a Labyrinth Handbook.
    \ + It can materialize a barricade at range that only you and people resistant to magic can pass.
    \ + Has 5 charges which regenerate over time." + transmute_text = "Transmute a crayon, a wooden plank, and a multitool." gain_text = "The Concierge scribbled my name into the Handbook. \"Welcome to your new home, fellow Steward.\"" required_atoms = list( /obj/item/toy/crayon = 1, @@ -165,12 +171,12 @@ drafting_tier = 5 /datum/heretic_knowledge/armor/lock - desc = "Allows you to transmute a table (or a suit), a mask and a crowbar to create a shifting guise. \ - It grants you camoflage from cameras, hides your identity, voice and muffles your footsteps. \ - Acts as a focus while hooded." + desc = "Create a Shifting Guise.
    \ + It grants you camouflage from cameras, hides your identity, voice and muffles your footsteps." + transmute_text = "Transmute a table (or a suit), a mask and a crowbar." gain_text = "While stewards are known to the Concierge, \ - they still consort between one another and with outsiders under shaded cloaks and drawn hoods. \ - Familiarity is treachery, even to oneself." + they still consort between one another and with outsiders under shaded cloaks and drawn hoods. \ + Familiarity is treachery, even to oneself." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/lock) research_tree_icon_state = "lock_armor" required_atoms = list( @@ -184,9 +190,13 @@ desc = "Grants you Burglar's Finesse, a single-target spell \ that puts a random item from the victims backpack into your hand." gain_text = "Consorting with Burglar spirits is frowned upon, but a Steward will always want to learn about new doors." - + required_atoms = list(/obj/item/clothing/gloves = 1) action_to_add = /datum/action/cooldown/spell/pointed/burglar_finesse cost = 2 + max_charges = 12 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.33 + transmute_text = "Can be manually recharged by completing a ritual with a pair of gloves." /datum/heretic_knowledge/blade_upgrade/flesh/lock name = "Opening Blade" @@ -202,25 +212,29 @@ return ..() /datum/heretic_knowledge/spell/caretaker_refuge - name = "Caretaker’s Last Refuge" - desc = "Gives you a spell that makes you transparent and not dense. Cannot be used near living sentient beings. \ - While in refuge, you cannot use your hands or spells, and you are immune to slowdown. \ - You are invincible but unable to harm anything. Cancelled by being hit with an anti-magic item." + name = "Caretaker's Last Refuge" + desc = "Gives you a spell that makes you transparent and not dense.
    \ + While the spell is active, you cannot use your hands or other spells, and you are immune to slowdown. \ + You are fully invincible but unable to harm anything. " gain_text = "Jealously, the Guard and the Hound hunted me. But I unlocked my form, and was but a haze, untouchable." action_to_add = /datum/action/cooldown/spell/caretaker cost = 2 is_final_knowledge = TRUE + max_charges = 3 + path_recharge_amount = 0.66 + holywater_drain_amount = 0.33 + notice = "• Cannot be used near living sentient beings.
    • Cancelled if you are hit with an anti-magic item." /datum/heretic_knowledge/ultimate/lock_final name = "Unlock the Labyrinth" desc = "The ascension ritual of the Path of Knock. \ - Bring 3 corpses without organs in their torso to a transmutation rune to complete the ritual. \ When completed, you gain the ability to transform into empowered eldritch creatures \ and your keyblades will become even deadlier. \ In addition, you will create a tear to the Labyrinth's heart; \ a tear in reality located at the site of this ritual. \ Eldritch creatures will endlessly pour from this rift \ who are bound to obey your instructions." + transmute_text = "Transmute three corpses without organs in their torso." gain_text = "The Stewards guided me, and I guided them. \ My foes were the Locks and my blades were the Key! \ The Labyrinth will be Locked no more, and freedom will be ours! WITNESS US!" diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index 5c611079f8cf..d944431d3ee6 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -53,9 +53,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_moon name = "Moonlight Troupe" - desc = "Opens up the Path of Moon to you. \ - Allows you to transmute 2 sheets of glass and a knife into an Lunar Blade. \ + desc = "Opens up the Path of Moon to you.
    \ + Allows you to create Lunar Blades. \ You can only create two at a time." + transmute_text = "Transmute 2 sheets of glass and a knife." gain_text = "Under the light of the moon the laughter echoes." required_atoms = list( /obj/item/knife = 1, @@ -90,20 +91,24 @@ /datum/heretic_knowledge/spell/mind_gate name = "Mind Gate" desc = "Grants you Mind Gate, a spell which mutes, deafens, blinds, inflicts hallucinations, \ - confusion, oxygen loss and brain damage to its target over 10 seconds.\ - The caster takes 20 brain damage per use." + confusion, oxygen loss and brain damage to its target over 10 seconds.
    \ + Casting the spell causes brain damage." gain_text = "My mind swings open like a gate, and its insight will let me perceive the truth." - action_to_add = /datum/action/cooldown/spell/pointed/mind_gate cost = 2 + max_charges = 6 + path_recharge_amount = 0.33 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.33 /datum/heretic_knowledge/moon_amulet name = "Moonlight Amulet" - desc = "Allows you to transmute 2 sheets of glass, a heart and a tie to create a Moonlight Amulet. \ - If the item is used on someone with low sanity they go berserk attacking everyone, \ - if their sanity isn't low enough it decreases their mood. \ - Wearing this will grant you the ability to see heathens through walls and make your blades harmless, they will instead directly attack their mind. \ - Provides thermal vision and doubles the brain regen of a moon heretic while worn." + desc = "Sculpt a Moonlight Amulet.
    \ + If the item is used on someone with low sanity, they go berserk - attacking anyone nearby. \ + If their sanity isn't low enough, it decreases their mood.
    \ + Wearing yourself this will heal your brain damage and grant you the ability to see heathens through walls, \ + but makes your blades harmless - instead damaging their mind directly." + transmute_text = "Transmute 2 sheets of glass, a heart and a tie." gain_text = "At the head of the parade he stood, the moon condensed into one mass, a reflection of the soul." required_atoms = list( @@ -111,7 +116,7 @@ /obj/item/stack/sheet/glass = 2, /obj/item/clothing/neck/tie = 1, ) - result_atoms = list(/obj/item/clothing/neck/heretic_focus/moon_amulet) + result_atoms = list(/obj/item/clothing/neck/moon_amulet) cost = 2 research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' @@ -119,12 +124,13 @@ research_tree_icon_frame = 9 /datum/heretic_knowledge/armor/moon - desc = "Allows you to transmute a table (or a suit), a mask and two sheets of glass to create a Resplendant Regalia. \ - This robe will render the user fully immune to disabling effects and convert all forms of damage into brain damage, while also pacifying the user and rendering them unable to use ranged weapons. \ - A Moonlight Amulet will be necessary to use blades while wearing it." + desc = "Create a Resplendant Regalia.
    While worn, renders you fully immune to disabling effects \ + While worn, pacifies you, while also rendering you fully immune to disabling effects and converting all forms of damage into brain damage." gain_text = "Trails of light and mirth flowed from every arm of this magnificent attire. \ - The troupe twirled in irridescent cascades, dazzling onlookers with the truth they sought. \ - I observed, basking in the light, to find my self." + The troupe twirled in irridescent cascades, dazzling onlookers with the truth they sought. \ + I observed, basking in the light, to find my self." + notice = "Despite the robe's pacifying effect, you can still use your Moon Blades, provided you ALSO wear a Moonlight Amulet." + transmute_text = "Transmute a table (or a suit), a mask and two sheets of glass." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/moon) research_tree_icon_state = "moon_armor" required_atoms = list( @@ -135,17 +141,23 @@ /datum/heretic_knowledge/spell/moon_parade name = "Lunar Parade" - desc = "Grants you Lunar Parade, a spell that - after a short charge - sends a carnival forward \ - when hitting someone they are forced to join the parade and suffer hallucinations." + desc = "Grants you Lunar Parade, a spell that - after a short charge - fires a projectile.
    \ + Anyone hit by it is forced to join the parade, following the projectile while suffering hallucinations." gain_text = "The music like a reflection of the soul compelled them, like moths to a flame they followed" action_to_add = /datum/action/cooldown/spell/pointed/projectile/moon_parade + notice = "There is no cap to the number of charges on the spell from applying Moonlight Amulets." cost = 2 drafting_tier = 5 + max_charges = 4 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 + path_recharge_can_surpass_cap = TRUE /datum/heretic_knowledge/blade_upgrade/moon name = "Moonlight Blade" - desc = "Your blade now deals brain damage, causes random hallucinations and does sanity damage. \ - Deals more brain damage if your victim is insane or unconscious." + desc = "Your blade now deals brain damage, causes random hallucinations and does sanity damage.
    \ + Deals more brain damage if your victim is insane or unconscious." gain_text = "His wit was sharp as a blade, cutting through the lie to bring us joy." research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' @@ -164,32 +176,34 @@ ) target.emote(pick("giggle", "laugh")) target.mob_mood?.adjust_sanity(-10) - if(target.stat == CONSCIOUS && target.mob_mood?.sanity >= SANITY_NEUTRAL) + if(!IS_UNCONSCIOUS_OR_CRIT(target) && target.mob_mood?.sanity >= SANITY_NEUTRAL) target.adjust_organ_loss(ORGAN_SLOT_BRAIN, 10) return target.adjust_organ_loss(ORGAN_SLOT_BRAIN, 25) /datum/heretic_knowledge/spell/moon_ringleader name = "Ringleaders Rise" - desc = "Grants you Ringleaders Rise, an AoE spell that deals more brain damage the lower the sanity of everyone in the AoE \ - and causes hallucinations, with those who have less sanity getting more. \ - If their sanity is low enough this turns them insane, the spell then halves their sanity." + desc = "Grants you Ringleaders Rise, an AoE spell that deals brain damage and causes hallucinations, scaling with sanity." gain_text = "I grabbed his hand and we rose, those who saw the truth rose with us. \ The ringleader pointed up and the dim light of truth illuminated us further." - + notice = "There is no cap to the number of charges on the spell from applying Moonlight Amulets." action_to_add = /datum/action/cooldown/spell/aoe/moon_ringleader cost = 2 - research_tree_icon_frame = 5 is_final_knowledge = TRUE + max_charges = 2 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 + path_recharge_can_surpass_cap = TRUE /datum/heretic_knowledge/ultimate/moon_final name = "The Last Act" - desc = "The ascension ritual of the Path of Moon. \ - Bring 3 corpses with more than 50 brain damage to a transmutation rune to complete the ritual. \ + desc = "The ascension ritual of the Path of Moon.
    \ When completed, you become a harbinger of madness gaining and aura of passive sanity decrease, \ - crewmembers with low enough sanity will be converted into acolytes. \ + crewmembers with low enough sanity will be converted into acolytes.
    \ 1/5th of the crew will turn into acolytes and follow your command, they will all receive moonlight amulets." + transmute_text = "Transmute 3 corpses with more than 50 brain damage." gain_text = "We dived down towards the crowd, his soul splitting off in search of greater venture \ for where the Ringleader had started the parade, I shall continue it unto the suns demise \ WITNESS MY ASCENSION, THE MOON SMILES ONCE MORE AND FOREVER MORE IT SHALL!" @@ -217,7 +231,7 @@ var/amount_of_lunatics = 0 var/list/lunatic_candidates = list() for(var/mob/living/carbon/human/crewmate as anything in shuffle(GLOB.human_list)) - if(QDELETED(crewmate) || isnull(crewmate.client) || isnull(crewmate.mind) || crewmate.stat != CONSCIOUS || crewmate.can_block_magic(MAGIC_RESISTANCE_MIND)) + if(QDELETED(crewmate) || isnull(crewmate.client) || isnull(crewmate.mind) || IS_UNCONSCIOUS_OR_CRIT(crewmate) || crewmate.can_block_magic(MAGIC_RESISTANCE_MIND)) continue var/turf/crewmate_turf = get_turf(crewmate) var/crewmate_z = crewmate_turf?.z @@ -252,7 +266,7 @@ var/datum/antagonist/lunatic/lunatic = convertee.mind.add_antag_datum(/datum/antagonist/lunatic) lunatic.set_master(user.mind, user) - var/obj/item/clothing/neck/heretic_focus/moon_amulet/amulet = new(convertee.drop_location()) + var/obj/item/clothing/neck/moon_amulet/amulet = new(convertee.drop_location()) var/static/list/slots = list( LOCATION_NECK, LOCATION_HANDS, @@ -274,7 +288,7 @@ for(var/mob/living/carbon/carbon_view in range(7, source)) var/carbon_sanity = carbon_view.mob_mood.sanity - if(carbon_view.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(carbon_view)) continue if(IS_HERETIC_OR_MONSTER(carbon_view)) continue diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm index 565879a7c8c3..cada5883f386 100644 --- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm @@ -52,9 +52,10 @@ /datum/heretic_knowledge/limited_amount/starting/base_rust name = "Blacksmith's Tale" - desc = "Opens up the Path of Rust to you. \ - Allows you to transmute a knife with any trash item into a Rusty Blade. \ + desc = "Opens up the Path of Rust to you.
    \ + Allows you to create Rusty Blades. \ You can only create two at a time." + transmute_text = "Transmute a knife with any trash item." gain_text = "\"Let me tell you a story\", said the Blacksmith, as he gazed deep into his rusty blade." required_atoms = list( /obj/item/knife = 1, @@ -104,28 +105,34 @@ /datum/heretic_knowledge/spell/rust_charge name = "Rust Charge" - desc = "A charge that must be started on a rusted tile and will destroy any rusted objects you come into contact with, will deal high damage to others and rust around you during the charge." + desc = "Allows you to muster a mighty charge while standing on rusted tile.
    \ + As you charge, you will spread rust, destroy any rusted objects, and deal high damage to anyone you collide with." gain_text = "The hills sparkled now, as I neared them my mind began to wander. I quickly regained my resolve and pushed forward, this last leg would be the most treacherous." action_to_add = /datum/action/cooldown/mob_cooldown/charge/rust cost = 2 is_final_knowledge = TRUE + max_charges = INFINITY /datum/heretic_knowledge/spell/rust_construction name = "Rust Construction" - desc = "Grants you Rust Construction, a spell that allows you to raise a wall out of a rusted floor. \ + desc = "Grants you Rust Construction, a spell that allows you to raise a wall out of a rusted floor.
    \ Anyone overtop the wall will be throw aside (or upwards) and sustain damage." gain_text = "Images of foreign and ominous structures began to dance in my mind. Covered head to toe in thick rust, \ they no longer looked man made. Or perhaps they never were in the first place." action_to_add = /datum/action/cooldown/spell/pointed/rust_construction cost = 2 + max_charges = 20 + path_recharge_amount = 0.2 + focus_recharge_amount = 0.5 + holywater_drain_amount = 0.1 /datum/heretic_knowledge/armor/rust - desc = "Allows you to transmute a table (or a suit), a mask and any trash item to create a Salvaged Remains. \ - Has extra armor, tackle resistance and syringe immunity while standing on rust. \ - Acts as a focus while hooded." + desc = "Create a Salvaged Remains.
    \ + Has extra armor, tackle resistance and syringe immunity while standing on rust." + transmute_text = "Transmute a table (or a suit), a mask and any trash item." gain_text = "From beneath warped scrap, the Blacksmith pulls forth an ancient fabric. \ - \"Whatever this once stood for is lost. So now, we give it new purpose.\"" + \"Whatever this once stood for is lost. So now, we give it new purpose.\"" result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/rust) research_tree_icon_state = "rust_armor" required_atoms = list( @@ -136,16 +143,24 @@ /datum/heretic_knowledge/spell/area_conversion name = "Aggressive Spread" - desc = "Grants you Aggressive Spread, a spell that spreads rust to nearby surfaces. \ - Already rusted surfaces are destroyed \ Also improves the rusting abilities of non rust-heretics." + desc = "Grants you Aggressive Spread, a spell that spreads rust to nearby surfaces.
    \ + Already rusted surfaces are destroyed.
    Improves the rusting abilities of non rust-heretics." gain_text = "All wise men know well not to visit the Rusted Hills... Yet the Blacksmith's tale was inspiring." + required_atoms = list( + /obj/item/storage/toolbox = 1, + ) action_to_add = /datum/action/cooldown/spell/aoe/rust_conversion cost = 2 research_tree_icon_frame = 5 + max_charges = 12 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.16 + transmute_text = "Can be manually recharged by completing a ritual with a toolbox." /datum/heretic_knowledge/blade_upgrade/rust name = "Toxic Blade" - desc = "Your Rusty Blade now disgusts enemies on attack \ Allows you to rust Titanium and Plastitanium.." + desc = "Your Rusty Blade now disgusts enemies on attack.
    Allows you to rust Titanium and Plastitanium." gain_text = "The Blacksmith hands you their blade. \"The Blade will guide you through the flesh, should you let it.\" \ The heavy rust weights it down. You stare deeply into it. The Rusted Hills call for you, now." research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' @@ -161,23 +176,28 @@ /datum/heretic_knowledge/spell/entropic_plume name = "Entropic Plume" - desc = "Grants you Entropic Plume, a spell that releases a vexing wave of Rust. \ + desc = "Grants you Entropic Plume, a spell that releases a vexing wave of Rust.
    \ Blinds, poisons, and inflicts Amok on any heathen it hits, causing them to strike \ - at friend or foe wildly. Also rusts and destroys and surfaces it hits and improves the rusting abilities of non-rust heretics." + at friend or foe wildly.
    Also rusts and destroys and surfaces it hits \ + and improves the rusting abilities of non-rust heretics." gain_text = "The corrosion was unstoppable. The rust was unpleasable. \ The Blacksmith was gone, and you hold their blade. Champions of hope, the Rustbringer is nigh!" action_to_add = /datum/action/cooldown/spell/cone/staggered/entropic_plume cost = 2 drafting_tier = 5 + max_charges = 4 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 /datum/heretic_knowledge/ultimate/rust_final name = "Rustbringer's Oath" - desc = "The ascension ritual of the Path of Rust. \ - Bring 3 corpses to a transmutation rune on the bridge of the station to complete the ritual. \ - When completed, the ritual site will endlessly spread rust onto any surface, stopping for nothing. \ + desc = "The ascension ritual of the Path of Rust.
    \ + When completed, the ritual site will endlessly spread rust onto any surface, stopping for nothing.
    \ Additionally, you will become extremely resilient on rust, healing at triple the rate \ and becoming immune to many effects and dangers \ You will be able to rust almost anything upon ascending." + transmute_text = "Transmute 3 corpses on the bridge of the station." gain_text = "Champion of rust. Corruptor of steel. Fear the dark, for the RUSTBRINGER has come! \ The Blacksmith forges ahead! Rusted Hills, CALL MY NAME! WITNESS MY ASCENSION!" diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/reroll_targets.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/reroll_targets.dm index b2c2b5b6e13a..f905e04c7439 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/reroll_targets.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/reroll_targets.dm @@ -4,11 +4,11 @@ /datum/heretic_knowledge/reroll_targets name = "The Relentless Heartbeat" - desc = "Allows you transmute a harebell, a book, and a jumpsuit while standing over a rune \ - to reroll your sacrifice targets." + desc = "Allows you to reroll your sacrifice targets." + transmute_text = "Transmute a harebell, a book, and a jumpsuit." gain_text = "The heart is the principle that continues and preserves." required_atoms = list( - /obj/item/food/grown/harebell = 1, + /obj/item/food/grown/flower/harebell = 1, /obj/item/book = 1, /obj/item/clothing/under = 1, ) diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm index 782ec6ec55dc..0857a06974e2 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm @@ -43,7 +43,7 @@ /// If we attack the guy who cursed us, that's no good /datum/status_effect/heretic_curse/proc/on_curser_attacked(datum/source, mob/attacker) SIGNAL_HANDLER - if (attacker != owner || !HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING)) + if (attacker != owner) return log_combat(owner, the_curser, "attacked", addition = "and lost some organs because they had previously been sacrificed by them.") experience_the_consequences() @@ -51,14 +51,14 @@ /// If we are attacked by the guy who cursed us, that's also no good /datum/status_effect/heretic_curse/proc/on_owner_attacked(datum/source, mob/attacker) SIGNAL_HANDLER - if (attacker != the_curser || !HAS_TRAIT(attacker, TRAIT_ALLOW_HERETIC_CASTING)) + if (attacker != the_curser) return log_combat(the_curser, owner, "attacked", addition = "and as they had previously sacrificed them, removed some of their organs.") experience_the_consequences() /// Experience something you may not enjoy which may also significantly shorten your lifespan /datum/status_effect/heretic_curse/proc/experience_the_consequences() - if (!COOLDOWN_FINISHED(src, consequence_cooldown) || owner.stat != CONSCIOUS) + if (!COOLDOWN_FINISHED(src, consequence_cooldown) || IS_UNCONSCIOUS_OR_CRIT(owner)) return var/mob/living/carbon/carbon_owner = owner diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm index b7b544bf23c7..dcf57d12a3f3 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm @@ -10,8 +10,8 @@ */ /datum/heretic_knowledge/hunt_and_sacrifice name = "Heartbeat of the Mansus" - desc = "Allows you to sacrifice targets to the Mansus by bringing them to a rune in critical (or worse) condition. \ - If you have no targets, stand on a transmutation rune and invoke it to acquire some." + desc = "Allows you to sacrifice targets to the Mansus by bringing them to a rune in critical (or worse) condition." + notice = "If you have no targets, stand on a transmutation rune and invoke it to acquire some." required_atoms = list(/mob/living/carbon/human = 1) cost = 0 priority = MAX_KNOWLEDGE_PRIORITY // Should be at the top @@ -39,6 +39,7 @@ /obj/item/organ/stomach/corrupt, /obj/item/organ/tongue/corrupt, ) + var/backdoor_sacrifice_attempts = 0 /datum/heretic_knowledge/hunt_and_sacrifice/Destroy(force) heretic_mind = null @@ -79,6 +80,30 @@ atoms += user return TRUE + if(istype(get_area(loc), /area/centcom/heretic_backdoor)) + loc.balloon_alert(user, "ritual failed, invalid location!") + switch(backdoor_sacrifice_attempts) + if(0) + to_chat(user, span_mansus("Attempting a sacrifice so close to the gods is risky...")) + if(1) + to_chat(user, span_mansus("You hear a knocking sound[HAS_TRAIT(user, TRAIT_DEAF) ? ", despite your deafness" : ""]...")) + if(2) + to_chat(user, span_mansus("The knocking grows louder...")) + user.soundbang_act(SOUNDBANG_NORMAL, deafen_pwr = 10 SECONDS, stun_pwr = 1 SECONDS, damage_pwr = 10, ignore_deafness = TRUE) + if(3) + to_chat(user, span_mansus("The knocking becomes deafening!")) + user.soundbang_act(SOUNDBANG_OVERWHELMING, deafen_pwr = 20 SECONDS, stun_pwr = 4 SECONDS, damage_pwr = 20, ignore_deafness = TRUE) + if(4) + if(begin_sacrifice(user)) + to_chat(user, span_mansus("Your insolence is punished!")) + else + to_chat(user, span_mansus("The knocking stops - but you can't help but feel like you've dodged a bullet, somehow.")) + if(5 to INFINITY) + to_chat(user, span_mansus("You don't think trying it again would provide any more insight...")) + + backdoor_sacrifice_attempts++ + return FALSE + // If we have targets, we can check to see if we can do a sacrifice // Let's remove any humans in our atoms list that aren't a sac target for(var/mob/living/carbon/human/sacrifice in atoms) @@ -135,7 +160,7 @@ if(!length(valid_targets)) if(!silent) - to_chat(user, span_hierophant_warning("No sacrifice targets could be found!")) + to_chat(user, span_mansus("No sacrifice targets could be found!")) return FALSE // Now, let's try to get four targets. @@ -209,6 +234,7 @@ var/datum/antagonist/cult/cultist_datum = GET_CULTIST(sacrifice) // Heads give 3 points, cultists give 1 point (and a special reward), normal sacrifices give 2 points. heretic_datum.total_sacrifices++ + SEND_SIGNAL(heretic_datum, COMSIG_HERETIC_SACRIFICE, sacrifice, (sac_job_flag & JOB_HEAD_OF_STAFF)) if((sac_job_flag & JOB_HEAD_OF_STAFF)) heretic_datum.adjust_knowledge_points(3) heretic_datum.high_value_sacrifices++ @@ -247,7 +273,7 @@ // Visible and audible encouragement! to_chat(user, span_big(span_hypnophrase("A servant of the Sanguine Apostate!"))) - to_chat(user, span_hierophant("Your patrons are rapturous!")) + to_chat(user, span_mansus("Your patrons are rapturous!")) playsound(sacrifice, 'sound/effects/magic/disintegrate.ogg', 75, TRUE) // Drop all items and splatter them around messily. @@ -537,7 +563,7 @@ composed_return_message += span_green("alive, but with a shattered mind. ") composed_return_message += span_notice("You hear a whisper... ") - composed_return_message += span_hypnophrase(get_area_name(safe_turf, TRUE)) + composed_return_message += span_mansus(get_area_name(safe_turf, TRUE)) to_chat(heretic_mind.current, composed_return_message) /** diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm index 726665f97dfd..6e5006d97020 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm @@ -60,7 +60,7 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks) // Rooms for where heretic sacrifices send people. /area/centcom/heretic_sacrifice - name = "Mansus" + name = "Mansus Gate" icon_state = "heretic" default_gravity = STANDARD_GRAVITY ambience_index = AMBIENCE_SPOOKY diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_four.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_four.dm index 87345d7ed0c4..51a8f8a4ecc3 100644 --- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_four.dm +++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_four.dm @@ -4,7 +4,7 @@ /datum/heretic_knowledge/spell/space_phase name = "Space Phase" - desc = "Grants you Space Phase, a spell that allows you to move freely through space. \ + desc = "Grants you Space Phase, a spell that allows you to move freely through space.
    \ You can only phase in and out when you are on a space or misc turf." gain_text = "You feel like your body can move through space as if you where dust." @@ -12,12 +12,37 @@ cost = 2 research_tree_icon_frame = 6 drafting_tier = 4 + max_charges = 2 + path_recharge_amount = 0.0 + holywater_drain_amount = 0.5 + transmute_text = "To recharge, crush a bluespace crystal while standing over a rune." + /// Tracks tim ein EVA + var/seconds_in_eva = 0 + +/datum/heretic_knowledge/spell/space_phase/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + RegisterSignal(user, COMSIG_MOB_CRUSHED_BLUESPACE_CRYSTAL, PROC_REF(on_crystal_crushed)) + +/datum/heretic_knowledge/spell/space_phase/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + UnregisterSignal(user, COMSIG_MOB_CRUSHED_BLUESPACE_CRYSTAL) + +/datum/heretic_knowledge/spell/space_phase/proc/on_crystal_crushed(mob/living/source, obj/item/crystal) + SIGNAL_HANDLER + + var/obj/effect/heretic_rune/rune = locate() in view(1, source) + if(isnull(rune)) + return + + rune.ritual_animation() + add_charges(1) /datum/heretic_knowledge/unfathomable_curio name = "Unfathomable Curio" - desc = "Allows you to transmute 3 rods, lungs, and any belt into an Unfathomable Curio - \ - a belt that can hold blades and items for rituals. Whilst worn it will veil you, \ - blocking one blow of incoming damage, at the cost of the veil. The veil will recharge itself out of combat." + desc = "Fashion an Unfathomable Curio - \ + a belt that can hold blades and items for rituals.
    Whilst worn it will veil you, \ + blocking one blow of incoming damage, at the cost of the veil. The veil will recharge itself out of combat." + transmute_text = "Transmute 3 rods, lungs, and any belt." gain_text = "The mansus holds many a curio, some are not meant for the mortal eye." required_atoms = list( @@ -33,7 +58,9 @@ /datum/heretic_knowledge/rust_sower name = "Rust Sower Grenade" - desc = "Allows you to combine a chemical grenade casing and some moldy food to conjure a cursed grenade filled with Eldritch Rust, upon detonating it releases a huge cloud that blinds organics, rusts affected turfs and obliterates Silicons and Mechs." + desc = "Conjure a cursed grenade filled with Eldritch Rust.
    Upon detonation, it releases a huge cloud that \ + blinds organics, rusts affected turfs and obliterates silicons and mechs." + transmute_text = "Transmute a chemical grenade casing and some moldy food." gain_text = "The choked vines of the Rusted Hills are burdened with such overripe fruits. It undoes the markers of progress, leaving a clean slate to work into new shapes." required_atoms = list( list( @@ -54,21 +81,39 @@ /datum/heretic_knowledge/spell/crimson_cleave name = "Crimson Cleave" - desc = "Grants you Crimson Cleave, a targeted spell which siphons health in a small AOE. Cleanses all wounds upon casting" + desc = "Grants you Crimson Cleave, a targeted spell which siphons health in a small AoE.\ +
    Also cleanses all wounds upon casting." gain_text = "At first I didn't understand these instruments of war, but the Priest \ - told me to use them regardless. Soon, he said, I would know them well." + told me to use them regardless. Soon, he said, I would know them well." + required_atoms = list( + list(/obj/effect/decal/cleanable/blood, /obj/item/rag, /obj/item/stack/medical/wrap/gauze) = 1, + ) action_to_add = /datum/action/cooldown/spell/pointed/crimson_cleave cost = 2 drafting_tier = 4 + max_charges = 3 + path_recharge_amount = 0.0 + focus_recharge_amount = 0.33 + holywater_drain_amount = 0.33 + +/datum/heretic_knowledge/spell/crimson_cleave/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + . = ..() + for(var/obj/item/rag/rag in atoms) + if(!GET_ATOM_BLOOD_DNA_LENGTH(rag)) + atoms -= rag + + for(var/obj/item/stack/medical/wrap/gauze/medwrap in atoms) + if(!GET_ATOM_BLOOD_DNA_LENGTH(medwrap)) + atoms -= medwrap /datum/heretic_knowledge/rifle name = "Lionhunter's Rifle" - desc = "Allows you to transmute a piece of wood, with hide \ - from any animal, and a camera to create the Lionhunter's rifle. \ + desc = "Unleash the Lionhunter's rifle.
    \ The Lionhunter's Rifle is a long ranged ballistic weapon with three shots. \ These shots function as normal, albeit weak high-caliber munitions when fired from \ close range or at inanimate objects. You can aim the rifle at distant foes, \ causing the shot to mark your victim with your grasp and teleport you directly to them." + transmute_text = "Transmute a piece of wood, hide from any animal, and a camera." gain_text = "I met an old man in an antique shop who wielded a very unusual weapon. \ I could not purchase it at the time, but they showed me how they made it ages ago." required_atoms = list( @@ -84,8 +129,8 @@ /datum/heretic_knowledge/rifle_ammo name = "Lionhunter Rifle Ammunition" - desc = "Allows you to transmute 3 ballistic ammo casings (used or unused) of any caliber, \ - including shotgun shells to create an extra clip of ammunition for the Lionhunter Rifle." + desc = "Create an extra clip of ammunition for the Lionhunter Rifle." + transmute_text = "Transmute 3 ballistic ammo casings (used or unused) of any caliber, including shotgun shells." gain_text = "The weapon came with three rough iron balls, intended to be used as ammunition. \ They were very effective, for simple iron, but used up quickly. I soon ran out. \ No replacement munitions worked in their stead. It was peculiar in what it wanted." @@ -107,6 +152,13 @@ CALIBER_HOOK, ) +/datum/heretic_knowledge/rifle_ammo/pre_research(mob/user, datum/antagonist/heretic/our_heretic) + if(!our_heretic.get_knowledge(/datum/heretic_knowledge/rifle)) + tgui_alert(user, "You must research the Lionhunter's Rifle knowledge before you can research its ammunition.") + return FALSE + + return TRUE + /datum/heretic_knowledge/rifle_ammo/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) for(var/obj/item/ammo_casing/casing in atoms) if(!(casing.caliber in caliber_blacklist)) @@ -114,7 +166,6 @@ // Remove any casings in the caliber_blacklist list from atoms atoms -= casing - // We removed any invalid casings from the atoms list, // return to allow the ritual to fill out selected atoms with the new list return TRUE diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_one.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_one.dm index 3706408b464e..2dddb2affe20 100644 --- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_one.dm +++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_one.dm @@ -4,10 +4,10 @@ /datum/heretic_knowledge/void_cloak name = "Void Cloak" - desc = "Allows you to transmute a glass shard, a bedsheet, and any outer clothing item (such as armor or a suit jacket) \ - to create a Void Cloak. While the hood is down, the cloak functions as a focus and protects you from space. \ - While the hood is up, the cloak is completely invisible. It also provide decent armor and \ + desc = "Fashion a Void Cloak.
    While the hood is down, protects you from space. \ + While the hood is up, the cloak is completely invisible.
    It also provide decent armor and \ has pockets which can hold one of your blades, various ritual components (such as organs), and small heretical trinkets." + transmute_text = "Transmute a glass shard, a bedsheet, and any outer clothing item (such as armor or a suit jacket)." gain_text = "The Owl is the keeper of things that are not quite in practice, but in theory are. Many things are." required_atoms = list( /obj/item/shard = 1, @@ -22,8 +22,9 @@ /datum/heretic_knowledge/medallion name = "Ashen Eyes" - desc = "Allows you to transmute a pair of eyes, a candle, and a glass shard into an Eldritch Medallion. \ + desc = "Sculpt an Eldritch Medallion.
    \ The Eldritch Medallion grants you thermal vision while worn, and also functions as a focus." + transmute_text = "Transmute a pair of eyes, a candle, and a glass shard." gain_text = "Piercing eyes guided them through the mundane. Neither darkness nor terror could stop them." required_atoms = list( /obj/item/organ/eyes = 1, @@ -38,8 +39,9 @@ /datum/heretic_knowledge/essence // AKA Eldritch Flask name = "Priest's Ritual" - desc = "Allows you to transmute a tank of water and a glass shard into a Flask of Eldritch Essence. \ + desc = "Fill a flask of Eldritch Essence.
    \ Eldritch Essence can be consumed for potent healing, or given to heathens for deadly poisoning." + transmute_text = "Transmute a tank of water and a glass shard." gain_text = "This is an old recipe. The Owl whispered it to me. \ Created by the Priest - the Liquid that both was and is not." required_atoms = list( @@ -54,24 +56,29 @@ /datum/heretic_knowledge/phylactery name = "Phylactery of Damnation" - desc = "Allows you to transmute a sheet of glass and a poppy into a Phylactery that can instantly draw blood, even from long distances. \ - Be warned, your target may still feel a prick." + desc = "Create a Phylactery that can instantly draw blood, even from long distances." + transmute_text = "Transmute a sheet of glass and a poppy." gain_text = "A tincture twisted into the shape of a bloodsucker vermin. \ Whether it chose the shape for itself, or this is the humor of the sickened mind that conjured this vile implement into being is something best not pondered." required_atoms = list( /obj/item/stack/sheet/glass = 1, - /obj/item/food/grown/poppy = 1, + /obj/item/food/grown/flower/poppy = 1, ) result_atoms = list(/obj/item/reagent_containers/cup/phylactery) cost = 1 research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "phylactery_2" drafting_tier = 1 + notice = "Target of the Phylactery may feel a prick." /datum/heretic_knowledge/crucible name = "Mawed Crucible" - desc = "Allows you to transmute a portable water tank and a table to create a Mawed Crucible. \ - The Mawed Crucible can brew powerful potions for combat and utility, but must be fed bodyparts and organs between uses." + desc = "Create a Mawed Crucible.
    \ + The Mawed Crucible can brew powerful but temporary potions for combat and utility, but must be fed bodyparts and organs between uses. \ +
    • Brew of the Crucible Soul: Allows you to walk through walls. Returns you to the place you consumed the potion after it expires. \ +
    • Brew of Dusk and Dawn: Allows you to see through walls. \ +
    • Brew of the Wounded Soldier: Heals you over time. The more severe your wounds (such as fractures or cuts), the faster it heals you." + transmute_text = "Transmute a portable water tank and a table." gain_text = "This is pure agony. I wasn't able to summon the figure of the Aristocrat, \ but with the Priest's attention I stumbled upon a different recipe..." required_atoms = list( @@ -86,10 +93,10 @@ /datum/heretic_knowledge/eldritch_coin name = "Eldritch Coin" - desc = "Allows you to transmute a sheet of plasma and a diamond to create an Eldritch Coin. \ - The coin will open or close nearby doors when landing on heads and toggle their bolts \ - when landing on tails. If you insert the coin into an airlock, it will be consumed \ - to fry its electronics, opening the airlock permanently unless bolted. " + desc = "Create an Eldritch Coin.
    \ + Flip the coin. On heads, nearby airlocks will open or close. On tails, nearby airlocks will bolt to their current state.
    \ + If you insert the coin into an airlock, it will be consumed to fry its electronics, keeping it open or closed permanently until repaired." + transmute_text = "Transmute a sheet of plasma and a diamond." gain_text = "The Mansus is a place of all sorts of sins. But greed held a special role." required_atoms = list( /obj/item/stack/sheet/mineral/diamond = 1, @@ -109,9 +116,10 @@ */ /datum/heretic_knowledge/codex_cicatrix name = "Codex Cicatrix" - desc = "Allows you to transmute a book, any pen, and your pick from any carcass (animal or human), leather, or hide to create a Codex Cicatrix. \ - The Codex Cicatrix can be used when draining influences to gain additional knowledge, but comes at greater risk of being noticed. \ - It can also be used to draw and remove transmutation runes easier, and as a spell focus in a pinch." + desc = "Author the Codex Cicatrix.
    \ + The Codex Cicatrix can be used when draining influences to gain additional knowledge, but comes at greater risk of being noticed.
    \ + It can also be used to draw and remove transmutation runes easier, and can be opened to restore charges to your Mansus Grasp." + transmute_text = "Transmute a book, any pen, and your pick from any carcass (animal or human), leather" gain_text = "The occult leaves fragments of knowledge and power anywhere and everywhere. The Codex Cicatrix is one such example. \ Within the leather-bound faces and age old pages, a path into the Mansus is revealed." required_atoms = list( @@ -121,7 +129,6 @@ ) result_atoms = list(/obj/item/codex_cicatrix) cost = 1 - priority = MAX_KNOWLEDGE_PRIORITY - 4 drafting_tier = 1 is_shop_only = TRUE research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' @@ -191,9 +198,10 @@ */ /datum/heretic_knowledge/bookworm name = "Warren King's Welcome" - desc = "Allows you to transmute 10 cable pieces, a piece of paper, and a multitool to brand nearby ID cards and airlocks. \ - Branded ID cards will gain access to maintenance, external airlocks, as well to branded airlocks. \ + desc = "Brand all present ID cards and nearby airlocks.
    \ + Branded ID cards will gain access to maintenance, external airlocks, as well to branded airlocks.
    \ Branded airlocks will only be accessible by those with a branded ID card." + transmute_text = "Transmute 10 cable pieces, a piece of paper, and a multitool." gain_text = "Gnawed into vicious-stained fingerbones, my grim invitation snaps my nauseous and clouded mind towards the heavy-set door. \ Slowly, the light dances between a crawling darkness, blanketing the fetid promenade with infinite machinations. \ But the King will soon take his pound of flesh. Even here, the taxman takes their cut. For there are a thousands mouths to feed." @@ -203,7 +211,6 @@ /obj/item/multitool = 1, ) cost = 1 - priority = MAX_KNOWLEDGE_PRIORITY - 3 drafting_tier = 1 research_tree_icon_path = 'icons/obj/card.dmi' research_tree_icon_state = "eldritch" @@ -233,3 +240,69 @@ playsound(door, SFX_SPARKS, 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, ignore_walls = FALSE) return TRUE + +/** + * Allows the heretic to craft a spell focus, which passively regenerates some spell charges + */ +/datum/heretic_knowledge/amber_focus + name = "Amber Focus" + desc = "Sculpts an Amber Focus.
    \ + Recharges some spell charges every few minutes while worn, barring some exceptional spells." + transmute_text = "Transmute a sheet of glass and a pair of eyes." + gain_text = "I felt lost and directionless. Everything that I tried to grasp, slipped through my fingers. \ + I needed something to hold onto, something to focus on, to keep me from straying too far from the path." + required_atoms = list( + /obj/item/organ/eyes = 1, + /obj/item/stack/sheet/glass = 1, + ) + result_atoms = list(/obj/item/clothing/neck/heretic_focus) + cost = 1 + drafting_tier = 1 + research_tree_icon_path = 'icons/obj/clothing/neck.dmi' + research_tree_icon_state = "eldritch_necklace" + +/datum/heretic_knowledge/miraculous_mirror + name = "Miraculous Mirror" + desc = "Craft a Miraculous Mirror.
    \ + The Miraculous Mirror allows you to freely change any aspect of your appearance. \ + You can also use it to change your species, but doing so will cause the mirror to shatter in the process." + transmute_text = "Transmute five bars of silver and a pair of organic eyes." + gain_text = "I was imperfect, weak. How could I achieve such great things in such a sorry state? \ + Every window I passed by, I saw a reflection of myself, and every time I did, I felt a burning desire to change, to be better, to start anew." + required_atoms = list( + /obj/item/organ/eyes = 1, + /obj/item/stack/sheet/mineral/silver = 5, + ) + result_atoms = list(/obj/item/wallframe/mirror/heretic) + cost = 1 + drafting_tier = 1 + research_tree_icon_path = 'icons/obj/watercloset.dmi' + research_tree_icon_state = "magic_mirror" + research_tree_icon_frame = 2 + is_shop_only = TRUE + +/datum/heretic_knowledge/miraculous_mirror/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + . = ..() + for(var/obj/item/organ/eyes/eye in atoms) + if(!IS_ORGANIC_ORGAN(eye)) + atoms -= eye + +/datum/heretic_knowledge/spell/cloak_of_shadows + name = "Cloak of Shadow" + desc = "Grants you the spell Cloak of Shadow.
    \ + This spell will completely conceal your identity in a purple smoke for three minutes, assisting you in keeping secrecy." + notice = "Can only be cast if you have a Living Heart." + action_to_add = /datum/action/cooldown/spell/shadow_cloak + cost = 1 + drafting_tier = 1 + max_charges = INFINITY + is_shop_only = TRUE // not actually but it's got special requirements + +/datum/heretic_knowledge/spell/cloak_of_shadows/spell_check(datum/action/the_spell, feedback) + var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(the_spell.owner) + if(heretic_datum?.has_living_heart()) + return ..() + + if(feedback) + to_chat(the_spell.owner, span_mansus("You need a Living Heart to cast [the_spell]!")) + return SPELL_CANCEL_CAST diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_three.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_three.dm index 03b27b7965e8..358a2e511c78 100644 --- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_three.dm +++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_three.dm @@ -4,8 +4,9 @@ /datum/heretic_knowledge/summon/rusty name = "Rusted Ritual" - desc = "Allows you to transmute a pool of vomit, some cable coil, and 10 sheets of iron into a Rust Walker. \ + desc = "Summon a Rust Walker.
    \ Rust Walkers excel at spreading rust and are moderately strong in combat." + transmute_text = "Transmute a pool of vomit, some cable coil, and 10 sheets of iron." gain_text = "I combined my knowledge of creation with my desire for corruption. The Marshal knew my name, and the Rusted Hills echoed out." required_atoms = list( /obj/effect/decal/cleanable/vomit = 1, @@ -19,9 +20,10 @@ /datum/heretic_knowledge/summon/maid_in_mirror name = "Maid in the Mirror" - desc = "Allows you to transmute five sheets of glass, any suit, and a pair of lungs to create a Maid in the Mirror. \ - Maid in the Mirrors are decent combatants that can become incorporeal by phasing in and out of the mirror realm, serving as powerful scouts and ambushers. \ - Their attacks also apply a stack of void chill." + desc = "Summon a Maid in the Mirror.
    \ + Maid in the Mirrors are decent combatants that can become incorporeal by phasing in and out of the mirror realm, \ + serving as powerful scouts and ambushers. Their attacks also apply a stack of void chill." + transmute_text = "Transmute five sheets of glass, any suit, and a pair of lungs." gain_text = "Within each reflection, lies a gateway into an unimaginable world of colors never seen and \ people never met. The ascent is glass, and the walls are knives. Each step is blood, if you do not have a guide." @@ -38,10 +40,11 @@ /datum/heretic_knowledge/summon/ashy name = "Ashen Ritual" - desc = "Allows you to transmute a Bonfire and a book to create an Ash Spirit. \ + desc = "Summon an Ash Spirit.
    \ Ash Spirits have a short range jaunt and the ability to cause bleeding in foes at range. \ - They also have the ability to create a ring of fire around themselves for a length of time. \ + They also have the ability to create a ring of fire around themselves for a length of time.
    \ They have a low amount of health, but will passively recover given enough time to do so." + transmute_text = "Transmute a pool of ash, a book, and a bonfire." gain_text = "I combined my principle of hunger with my desire for destruction. The Marshal knew my name, and the Nightwatcher gazed on." required_atoms = list( /obj/effect/decal/cleanable/ash = 1, @@ -59,10 +62,10 @@ /datum/heretic_knowledge/limited_amount/risen_corpse name = "Shattered Ritual" - desc = "Allows you to transmute a corpse with a soul, a pair of latex or nitrile gloves, and \ - and any exosuit clothing (such as armor) to create a Shattered Risen. \ + desc = "Enchant a corpse into a Shattered Risen.
    \ Shattered Risen are strong ghouls that have 125 health, but cannot hold items, \ instead having two brutal weapons for hands. You can only create one at a time." + transmute_text = "Transmute a corpse with a soul, a pair of latex or nitrile gloves." gain_text = "I witnessed a cold, rending force drag this corpse back to near-life. \ When it moves, it crunches like broken glass. Its hands are no longer recognizable as human - \ each clenched fist contains a brutal nest of sharp bone-shards instead." @@ -159,9 +162,10 @@ /datum/heretic_knowledge/summon/fire_shark name = "Scorching Shark" - desc = "Allows you to transmute a pool of ash, a liver, and a sheet of plasma into a Fire Shark. \ - Fire Sharks are fast and strong in groups, but die quickly. They are also highly resistant against fire attacks. \ - Fire Sharks inject phlogiston into its victims and spawn plasma once they die." + desc = "Summon a Fire Shark.
    \ + Fire Sharks are fast and strong in groups, but fragile to non-burning damage.
    \ + They also inject phlogiston on attack and spawn plasma on death." + transmute_text = "Transmute a pool of ash, a liver, and a sheet of plasma." gain_text = "The cradle of the nebula was cold, but not dead. Light and heat flits even through the deepest darkness, and is hunted by its own predators." required_atoms = list( @@ -176,3 +180,169 @@ research_tree_icon_dir = EAST drafting_tier = 3 + +/datum/heretic_knowledge/mad_mask + name = "Mask of Madness" + desc = "Create a Mask of Madness.
    \ + The mask instills fear into heathens who witness it, causing stamina damage, hallucinations, and insanity.
    \ + It can also be forced onto a heathen, to make them unable to take it off..." + transmute_text = "Transmute any mask, four lit candles, a stun baton, and a liver." + gain_text = "The Watch wore strange garb on duty. It allowed them to walk the city, seemingly unnoticed by the masses." + required_atoms = list( + /obj/item/organ/liver = 1, + /obj/item/melee/baton/security = 1, // Technically means a cattleprod is valid + /obj/item/clothing/mask = 1, + /obj/item/flashlight/flare/candle = 4, + ) + result_atoms = list(/obj/item/clothing/mask/madness_mask) + cost = 2 + research_tree_icon_path = 'icons/obj/clothing/masks.dmi' + research_tree_icon_state = "mad_mask" + drafting_tier = 3 + +/datum/heretic_knowledge/mad_mask/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + . = ..() + for(var/obj/item/flashlight/flare/candle/candle in atoms) + if(!candle.light_on) + atoms -= candle + +/datum/heretic_knowledge/mad_mask/prepare_atom_for_ritual_test(atom/what) + . = ..() + if(istype(what, /obj/item/flashlight/flare/candle)) + what.set_light_on(TRUE) + +/datum/heretic_knowledge/mansus_gate + name = "Keys to the Backdoor" + desc = "Open a backdoor to the Mansus.
    \ + Entering the container will transport you to the Mansus, granting you a safe haven to transmute or store equipment." + transmute_text = "Transmute a locker and a Codex Cicatrix or Codex Morbus." + notice = "Only willing individuals or the deceased can pass through the backdoor.\ +
    Attempting to perform a sacrifice so close to the gods may anger them.\ +
    You can only create one backdoor." + gain_text = "With any domain, its security is only as strong as its weakest point. \ + The Codex speaks of backdoors to the Mansus - gates with shoddy chains, doors with brittle locks, trapdoors with rusted hinges. \ + With the right key, I could easily exploit these." + required_atoms = list( + /obj/structure/closet = 1, + list(/obj/item/codex_cicatrix, /obj/item/codex_cicatrix/morbus) = 1, + ) + cost = 2 + research_tree_icon_path = 'icons/effects/effects.dmi' + research_tree_icon_state = "anom" + drafting_tier = 3 + is_shop_only = TRUE + VAR_PRIVATE/used = FALSE + +/datum/heretic_knowledge/mansus_gate/can_be_invoked(datum/antagonist/heretic/invoker) + return !used + +/datum/heretic_knowledge/mansus_gate/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) + var/obj/structure/closet/locker = locate() in selected_atoms + if(isnull(locker)) + stack_trace("Locker not found in selected atoms for mansus gate recipe!") + return FALSE + + used = TRUE + INVOKE_ASYNC(src, PROC_REF(setup_link), locker) + return TRUE + +/datum/heretic_knowledge/mansus_gate/cleanup_atoms(list/selected_atoms) + for(var/obj/structure/closet/locker in selected_atoms) + selected_atoms -= locker + return ..() + +/datum/heretic_knowledge/mansus_gate/proc/setup_link(obj/structure/closet/crate/locker) + var/datum/map_template/masus_backdoor/backdoor = new() + var/datum/turf_reservation/reservation = SSmapping.request_turf_block_reservation( + width = backdoor.width, + height = backdoor.height, + reservation_type = /datum/turf_reservation/indestructible_plating, + ) + var/turf/bottom_left = reservation.bottom_left_turfs[1] + backdoor.load(bottom_left) + var/obj/structure/closet/new_link = locate() in backdoor.created_atoms + new_link.resistance_flags |= INDESTRUCTIBLE + new_link.set_anchored(TRUE) + new_link.anchorable = FALSE + new_link.divable = FALSE + new_link.contents_pressure_protection = 1 + new_link.contents_thermal_insulation = 1 + locker.resistance_flags |= INDESTRUCTIBLE + locker.divable = FALSE + locker.contents_pressure_protection = 1 + locker.contents_thermal_insulation = 1 + GLOB.closet_teleport_controller.create_new_link(list(locker, new_link), subtle = TRUE) + RegisterSignal(new_link, COMSIG_CLOSET_TELEPORTER_PRE_SENDING, PROC_REF(closet_teleport_logic)) + RegisterSignal(locker, COMSIG_CLOSET_TELEPORTER_PRE_SENDING, PROC_REF(closet_teleport_logic)) + +/datum/heretic_knowledge/mansus_gate/proc/closet_teleport_logic(obj/structure/closet/crate/locker, atom/movable/sending_through) + SIGNAL_HANDLER + + if(!is_station_level(locker.z)) + return CLOSET_TELEPORT_FORCED + + if(isliving(sending_through) && !consents_to_entry(sending_through)) + locker.balloon_alert(sending_through, "the door refuses you!") + return CLOSET_TELEPORT_BLOCKED + + for(var/mob/living/entering in sending_through.get_all_contents()) + if(!consents_to_entry(entering)) + if(isliving(sending_through)) + locker.balloon_alert(sending_through, "the door refuses you!") + return CLOSET_TELEPORT_BLOCKED + + return CLOSET_TELEPORT_FORCED + +/datum/heretic_knowledge/mansus_gate/proc/consents_to_entry(mob/living/entering) + if(IS_HERETIC_OR_MONSTER(entering)) + return TRUE + if(!INCAPACITATED_IGNORING(entering, INCAPABLE_GRAB|INCAPABLE_STASIS)) + return TRUE + return FALSE + +/datum/map_template/masus_backdoor + name = "The Mansus" + mappath = "_maps/templates/mansus_backdoor.dmm" + width = 59 + height = 51 + returns_created_atoms = TRUE + +/area/centcom/heretic_backdoor + name = "Mansus" + icon_state = "heretic" + requires_power = TRUE + always_unpowered = TRUE + ambience_index = AMBIENCE_SPOOKY + sound_environment = SOUND_ENVIRONMENT_PLAIN + area_flags = NOTELEPORT | HIDDEN_AREA | BLOCK_SUICIDE | NO_BOH + static_lighting = FALSE + base_lighting_alpha = 200 + base_lighting_color = "#FFF4AA" + +/area/centcom/heretic_backdoor/Entered(atom/movable/arrived, area/old_area) + . = ..() + if(isliving(arrived)) + var/mob/living/arrived_mob = arrived + arrived_mob.add_movespeed_modifier(/datum/movespeed_modifier/heretic_backdoor_slowdown) + arrived_mob.adjust_temp_blindness(1 SECONDS) + arrived_mob.adjust_eye_blur(2 SECONDS) + addtimer(CALLBACK(src, PROC_REF(greet_message), arrived_mob), 2 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) + if(!IS_HERETIC_OR_MONSTER(arrived_mob)) + arrived_mob.apply_status_effect(/datum/status_effect/necropolis_curse, CURSE_BLINDING) + +/area/centcom/heretic_backdoor/Exited(atom/movable/gone, direction) + . = ..() + if(isliving(gone)) + var/mob/living/gone_mob = gone + gone_mob.remove_movespeed_modifier(/datum/movespeed_modifier/heretic_backdoor_slowdown) + gone_mob.remove_status_effect(/datum/status_effect/necropolis_curse) + gone_mob.adjust_temp_blindness(1 SECONDS) + gone_mob.adjust_eye_blur(2 SECONDS) + +/area/centcom/heretic_backdoor/proc/greet_message(mob/living/arrived_mob) + if(QDELETED(arrived_mob) || get_area(arrived_mob) != src) + return + to_chat(arrived_mob, span_mansus("A hollow sun shines down from above.")) + +/datum/movespeed_modifier/heretic_backdoor_slowdown + multiplicative_slowdown = 0.5 diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm index 5d155281e5eb..9b8923b66038 100644 --- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm +++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm @@ -9,9 +9,10 @@ */ /datum/heretic_knowledge/codex_morbus name = "Codex Morbus" - desc = "Allows you to to combine a codex cicatrix, and a body into a Codex Morbus. \ - It draws runes and siphons essences a bit faster. \ - Right Click on a rune to curse crewmembers, the target's blood is required in your off hand for a curse to take effect (Best combined with Phylactery Of Damnation)." + desc = "Author the Codex Morbus.
    Draws runes and siphons essences a bit faster than a Codex Cicatrix.
    \ + Right Click on a rune to curse crewmembers - the target's blood is required in your off hand for a curse to take effect \ + (Best combined with Phylactery Of Damnation)." + transmute_text = "Transmute the Codex Cicatrix and a body into a Codex Morbus." gain_text = "The spine of this leather-bound tome creaks with an eerily pained sigh. \ To ply page from place takes considerable effort, and I dare not linger on the suggestions the book makes for longer than necessary. \ It speaks of coming plagues, of waiting supplicants of dead and forgotten gods, and the undoing of mortal kind. \ @@ -37,8 +38,9 @@ return TRUE /datum/heretic_knowledge/greaves_of_the_prophet - name = "Greaves Of The Prophet" - desc = "Allows you to combine a pair of shoes and 2 sheets of titanium or silver into a pair of Armored Greaves, they confer to the user full immunity to slips." + name = "Greaves of the Prophet" + desc = "Forge a pair of Armored Greaves, which confer to the user full immunity to slips." + transmute_text = "Transmute a pair of shoes and two sheets of titanium or silver." gain_text = " \ Gristle churns into joint, a pop, and the fool twists a blackened foot from the \ jaws of another. At their game for centuries, this mangled tree of limbs twists, \ @@ -59,19 +61,25 @@ /datum/heretic_knowledge/spell/opening_blast name = "Wave Of Desperation" - desc = "Grants you Wave Of Desparation, a spell which can only be cast while restrained. \ + desc = "Grants you Wave Of Desparation, a spell which can only be cast while restrained.
    \ It removes your restraints, repels and knocks down adjacent people, and applies the Mansus Grasp to everything nearby." gain_text = "My shackles undone in dark fury, their feeble bindings crumble before my power." - + required_atoms = list( + /obj/item/clothing/suit/jacket/straight_jacket = 1, + ) action_to_add = /datum/action/cooldown/spell/aoe/wave_of_desperation cost = 2 drafting_tier = 2 + max_charges = 1 + path_recharge_amount = 0.0 + transmute_text = "To recharge, complete a ritual with a straight jacket." /datum/heretic_knowledge/rune_carver name = "Carving Knife" - desc = "Allows you to transmute a knife, a shard of glass, and a piece of paper to create a Carving Knife. \ - The Carving Knife allows you to etch difficult to see traps that trigger on heathens who walk overhead. \ + desc = "Create a Carving Knife.
    \ + The Carving Knife allows you to etch difficult to see traps that trigger on heathens who walk overhead.
    \ Also makes for a handy throwing weapon." + transmute_text = "Transmute a knife, a shard of glass, and a piece of paper." gain_text = "Etched, carved... eternal. There is power hidden in everything. I can unveil it! \ I can carve the monolith to reveal the chains!" required_atoms = list( @@ -87,8 +95,10 @@ /datum/heretic_knowledge/ether name = "Ether Of The Newborn" - desc = "Transmutes a pool of vomit and a shard into a single use potion, drinking it will remove any sort of abnormality from your body including diseases, traumas and implants \ - on top of restoring it to full health, at the cost of losing consciousness for an entire minute." + desc = "Brews a single use potion.
    Imbibing it will restore you to full health and \ + remove any sort of abnormality from your body (including diseases, traumas and implants) - \ + however, you will lose consciousness for a full minute." + transmute_text = "Transmute a pool of vomit and a shard." gain_text = "Vision and thought grow hazy as the fumes of this ichor swirl up to meet me. \ Through the haze, I find myself staring back in relief, or something grossly resembling my visage. \ It is this wretched thing that I consign to my fate, and whose own that I snatch through the haze of dreams. Fools that we are." @@ -104,15 +114,21 @@ /datum/heretic_knowledge/painting name = "Unsealed Arts" - desc = "Allows you to transmute a canvas and an additional item to create a painting. \ - Each painting has a unique effect and recipe. Possible paintings: \ - The Sister and He Who Wept: Requires a pair of Eyes. Clears your own mind, and curses non-heretics with hallucinations. \ - The Feast of Desire: Requires a severed limb. Supplies you with random organs, and curses non-heretics with a hunger for flesh. \ - Great Chaparral Over Rolling Hills: Requires any plant produce. Spreads kudzu when placed, and supplies you with poppies and harebells. \ - Lady of the Gate: Requires any pair of Gloves. Clears your mutations, mutates non-heretics and curses them with scratching. \ - Master of the Rusted Mountain: Requires a piece of Trash. Curses non-heretics to rust the floor they walk on." + desc = "Paint a curse into existence. \ + Each painting has a unique effect and recipe. \ +
    • The Sister and He Who Wept: Clears your mind, while cursing heathens with hallucinations. \ +
    • The Feast of Desire: Supplies you with random organs, while cursing heathens with a hunger for flesh. \ +
    • Great Chaparral Over Rolling Hills: Spreads kudzu when placed, and supplies you with poppies and harebells. \ +
    • Lady of the Gate: Clears your mutations, while mmutating and cursing heathens them with scratching. \ +
    • Master of the Rusted Mountain: Curses heathens to rust the floor they walk on." + transmute_text = "Transmute a canvas and an additional item to create a painting. \ +
    • A pair of eyes for The Sister and He Who Wept \ +
    • A severed limb for The Feast of Desire \ +
    • Any plant produce for Great Chaparral Over Rolling Hills \ +
    • Any pair of gloves for Lady of the Gate \ +
    • A piece of trash for Master of the Rusted Mountain" gain_text = "A wind of inspiration blows through me. Beyond the veil and past the gate great works exist, yet to be painted. \ - They yearn for mortal eyes, so I shall give them an audience." + They yearn for mortal eyes, so I shall give them an audience." required_atoms = list(/obj/item/canvas = 1) result_atoms = list(/obj/item/canvas) cost = 2 @@ -163,3 +179,65 @@ user.balloon_alert(user, "no additional atom present!") return FALSE + +/datum/heretic_knowledge/hypnosis_ritual + name = "Unwrap Minds" + desc = "Exposes a healthen directly to the horrors of the Mansus, hypnotizing them." + transmute_text = "Transmute a scalpel, a shard of glass, a piece of paper, and a living heathen." + notice = "Whatever is written on the paper supplied, the heathen will be hypnotized with.\ +
    If the heathen is mindshielded, it will shatter - but the resulting hypnosis may not be what you expect.\ +
    Other Heretics are unaffected by this ritual." + gain_text = "My rise has been lonely, but I had realized it did not have to be. \ + I can show them the truth. Their weak, mortal minds may not be able to withstand the revelation, but in its ashes, a phoenix will rise, free and true." + required_atoms = list( + /obj/item/scalpel = 1, + /obj/item/shard = 1, + /obj/item/paper = 1, + /mob/living/carbon/human = 1, + ) + cost = 2 + research_tree_icon_path = 'icons/hud/screen_alert.dmi' + research_tree_icon_state = "hypnosis" + drafting_tier = 2 + +/datum/heretic_knowledge/hypnosis_ritual/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + for(var/mob/living/carbon/human/victim in atoms) + if(victim.stat == DEAD || IS_HERETIC(victim) || victim.has_trauma_type(/datum/brain_trauma/hypnosis)) + atoms -= victim + + var/has_paper = FALSE + var/has_written_text = FALSE + for(var/obj/item/paper/paper in atoms) + has_paper = TRUE + for(var/datum/paper_input/text as anything in paper.raw_text_inputs) + has_written_text = TRUE + + if(!has_written_text && has_paper) + loc.balloon_alert(user, "write your hypnosis on the paper!") + return FALSE + + return ..() + +/datum/heretic_knowledge/hypnosis_ritual/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc) + var/hypnosis_text = "" + for(var/obj/item/paper/paper in selected_atoms) + for(var/datum/paper_input/text as anything in paper.raw_text_inputs) + hypnosis_text += "[STRIP_HTML_FULL(text.raw_text, MAX_MESSAGE_LEN)] " + paper.burn() + selected_atoms -= paper + + hypnosis_text = trim(hypnosis_text, MAX_MESSAGE_LEN) + for(var/mob/living/carbon/human/victim in selected_atoms) + var/specific_hypnosis_text = (HAS_TRAIT(victim, TRAIT_UNCONVERTABLE) || !hypnosis_text) ? pick_list(HERETIC_INFLUENCE_FILE, "hypnosis") : hypnosis_text + for(var/obj/item/implant/mindshield/shield in victim.implants) + shield.removed(victim, silent = FALSE) + shield.forceMove(victim.drop_location()) + shield.burn() + + selected_atoms -= victim + // lobotomy resistance because it might be a bit rough to make this permanent aye + // future note: if people just spam hypnosis to make an army to rush ascension, make it so hypnosis is broken by witnessing sacrifices + var/datum/brain_trauma/hypnosis/trauma = new(specific_hypnosis_text) + victim.gain_trauma(trauma, TRAUMA_RESILIENCE_LOBOTOMY) + + return TRUE diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm index 4105a4d08c98..d66b6ce3575e 100644 --- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm @@ -18,18 +18,29 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) */ /datum/heretic_knowledge/spell/basic name = "Break of Dawn" - desc = "Starts your journey into the Mansus. \ - Grants you the Mansus Grasp, a powerful and upgradable \ - disabling spell that can be cast regardless of having a focus." + desc = "Starts your journey into the Mansus.
    \ + Grants you the Mansus Grasp, a powerful and upgradable disabling spell." action_to_add = /datum/action/cooldown/spell/touch/mansus_grasp cost = 0 is_starting_knowledge = TRUE + max_charges = 8 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.125 + transmute_text = "Tapping influences and completing sacrifices will recharge the spell." // Heretics can enhance their fishing rods to fish better - fishing content. // Lasts until successfully fishing something up. /datum/heretic_knowledge/spell/basic/on_gain(mob/user, datum/antagonist/heretic/our_heretic) - ..() + . = ..() RegisterSignal(user, COMSIG_TOUCH_HANDLESS_CAST, PROC_REF(on_grasp_cast)) + RegisterSignal(our_heretic, COMSIG_HERETIC_INFLUENCE_DRAINED, PROC_REF(on_influence_tap)) + RegisterSignal(our_heretic, COMSIG_HERETIC_SACRIFICE, PROC_REF(on_sacrifice)) + +/datum/heretic_knowledge/spell/basic/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + UnregisterSignal(user, COMSIG_TOUCH_HANDLESS_CAST) + UnregisterSignal(our_heretic, COMSIG_HERETIC_INFLUENCE_DRAINED) + UnregisterSignal(our_heretic, COMSIG_HERETIC_SACRIFICE) /datum/heretic_knowledge/spell/basic/proc/on_grasp_cast(mob/living/carbon/cast_on, datum/action/cooldown/spell/touch/touch_spell) SIGNAL_HANDLER @@ -58,6 +69,14 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) REMOVE_TRAIT(item, TRAIT_ROD_MANSUS_INFUSED, REF(item)) item.difficulty_modifier += 20 +/datum/heretic_knowledge/spell/basic/proc/on_influence_tap(...) + SIGNAL_HANDLER + add_charges(max_charges) + +/datum/heretic_knowledge/spell/basic/proc/on_sacrifice(...) + SIGNAL_HANDLER + add_charges(max_charges) + /** * The Living Heart heretic knowledge. * @@ -66,13 +85,12 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) */ /datum/heretic_knowledge/living_heart name = "The Living Heart" - desc = "Grants you a Living Heart, allowing you to track sacrifice targets. \ - Should you lose your heart, you can transmute a poppy and a pool of blood \ - to awaken your heart into a Living Heart. If your heart is Cybernetic, \ - you will be unable to reawaken it." + desc = "Grants you a Living Heart, allowing you to track sacrifice targets." + transmute_text = "Should you lose your heart, you can transmute a poppy and a pool of blood \ + to awaken your heart into a Living Heart." required_atoms = list( /obj/effect/decal/cleanable/blood = 1, - /obj/item/food/grown/poppy = 1, + /obj/item/food/grown/flower/poppy = 1, ) cost = 0 priority = MAX_KNOWLEDGE_PRIORITY - 1 // Knowing how to remake your heart is important @@ -80,6 +98,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' research_tree_icon_state = "living_heart" research_tree_icon_frame = 1 + notice = "If your heart is Cybernetic, you will be unable to reawaken it." /datum/heretic_knowledge/living_heart/on_research(mob/user, datum/antagonist/heretic/our_heretic) . = ..() @@ -111,8 +130,8 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) if(where_to_put_our_heart) where_to_put_our_heart.AddComponent(/datum/component/living_heart) - desc = "Grants you a Living Heart, tied to your [where_to_put_our_heart.name], allowing you to track sacrifice targets. \ - Should you lose your [where_to_put_our_heart.name], you can transmute a poppy and a pool of blood \ + desc = "Grants you a Living Heart, tied to your [where_to_put_our_heart.name], allowing you to track sacrifice targets." + transmute_text = "Should you lose your [where_to_put_our_heart.name], you can transmute a poppy and a pool of blood \ to awaken your [where_to_put_our_heart.name] into a Living Heart. \ Cybernetic [where_to_put_our_heart.name]\s will block the ritual!" @@ -173,41 +192,15 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge()) return TRUE -/** - * Allows the heretic to craft a spell focus. - * They require a focus to cast advanced spells. - */ -/datum/heretic_knowledge/amber_focus - name = "Amber Focus" - desc = "Allows you to transmute a sheet of glass and a pair of eyes to create an Amber Focus. \ - A focus must be worn in order to cast more advanced spells." - required_atoms = list( - /obj/item/organ/eyes = 1, - /obj/item/stack/sheet/glass = 1, - ) - result_atoms = list(/obj/item/clothing/neck/heretic_focus) - cost = 0 - priority = MAX_KNOWLEDGE_PRIORITY - 2 // Not as important as making a heart or sacrificing, but important enough. - is_starting_knowledge = TRUE - research_tree_icon_path = 'icons/obj/clothing/neck.dmi' - research_tree_icon_state = "eldritch_necklace" - -/datum/heretic_knowledge/spell/cloak_of_shadows - name = "Cloak of Shadow" - desc = "Grants you the spell Cloak of Shadow. This spell will completely conceal your identity in a purple smoke \ - for three minutes, assisting you in keeping secrecy. Requires a focus to cast." - action_to_add = /datum/action/cooldown/spell/shadow_cloak - cost = 0 - is_starting_knowledge = TRUE - /datum/heretic_knowledge/feast_of_owls name = "Feast of Owls" - desc = "Allows you to undergo a ritual that gives you 5 knowledge points but locks you out of ascension. This can only be done once and cannot be reverted." + desc = "Allows you to undergo a ritual that grants you five knowledge points, but locks you out of ascension." gain_text = "Under the soft glow of unreason there is a beast that stalks the night. I shall bring it forth and let it enter my presence. It will feast upon my amibitions and leave knowledge in its wake." is_starting_knowledge = TRUE required_atoms = list() research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi' research_tree_icon_state = "god_transmit" + notice = "This can only be done once and cannot be reverted." /// amount of research points granted var/reward = 5 diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm index b5deceaca5f2..44fc3a08a257 100644 --- a/code/modules/antagonists/heretic/knowledge/void_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm @@ -27,7 +27,7 @@ ) tips = list( "Your Mansus Grasp allows you to mute your targets, making it ideal for silent assassinations (keep in mind that it won't short circuit their suit sensors, make sure you turn them off after you kill them). Yhe grasp also applies a mark that when triggered by the void blade will apply the maximum amount of stacks of void chill to your target, slowing them down to a crawl.", - "Void Cloak can be used to hide one of your blades and a Codex Cicatrix when the hood is down, while acting as a focus when it's up.", + "Void Cloak can be used to hide one of your blades and a Codex Cicatrix.", "Void chill is a debuff applied by your spells, your grasp, your mark and your blade once you unlock the upgrade. Each stack slows your target movement speed by 10% and make them gradually colder, up to a maximum of 5 stacks.", "At 5 stacks void chill will also prevent your target from heating up.", "You are immune to low pressure and cold damage at the start of the shift. Upgrade your passive to level 2 to no longer need to breathe. Use this to your advantage.", @@ -38,20 +38,21 @@ start = /datum/heretic_knowledge/limited_amount/starting/base_void knowledge_tier1 = /datum/heretic_knowledge/spell/void_phase guaranteed_side_tier1 = /datum/heretic_knowledge/void_cloak - knowledge_tier2 = /datum/heretic_knowledge/spell/void_prison + knowledge_tier2 = /datum/heretic_knowledge/void_prison guaranteed_side_tier2 = /datum/heretic_knowledge/ether robes = /datum/heretic_knowledge/armor/void knowledge_tier3 = /datum/heretic_knowledge/spell/void_pull guaranteed_side_tier3 = /datum/heretic_knowledge/summon/maid_in_mirror blade = /datum/heretic_knowledge/blade_upgrade/void - knowledge_tier4 = /datum/heretic_knowledge/spell/void_conduit + knowledge_tier4 = /datum/heretic_knowledge/void_conduit ascension = /datum/heretic_knowledge/ultimate/void_final /datum/heretic_knowledge/limited_amount/starting/base_void name = "Glimmer of Winter" - desc = "Opens up the Path of Void to you. \ - Allows you to transmute a knife in sub-zero temperatures into a Void Blade. \ + desc = "Opens up the Path of Void to you.
    \ + Allows you to create Void Blades. \ You can only create two at a time." + transmute_text = "Transmute a knife in sub-zero temperatures." gain_text = "I feel a shimmer in the air, the air around me gets colder. \ I start to realize the emptiness of existence. Something's watching me." required_atoms = list(/obj/item/knife = 1) @@ -85,35 +86,66 @@ /datum/heretic_knowledge/spell/void_phase name = "Void Phase" - desc = "Grants you Void Phase, a long range targeted teleport spell. \ - Additionally causes damage to heathens around your original and target destination." + desc = "Grants you Void Phase, a long range targeted teleport spell.
    \ + Causes damage to heathens around your original and target destination." gain_text = "The entity calls themself the Aristocrat. They effortlessly walk through air like \ nothing - leaving a harsh, cold breeze in their wake. They disappear, and I am left in the blizzard." action_to_add = /datum/action/cooldown/spell/pointed/void_phase cost = 2 research_tree_icon_frame = 7 + max_charges = 4 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 -/datum/heretic_knowledge/spell/void_prison +/datum/heretic_knowledge/void_prison name = "Void Prison" - desc = "Grants you Void Prison, a spell that places your victim into a ball, making them unable to do anything or speak. \ - Applies void chill afterwards." + desc = "Transmute a set of handcuffs, a stun baton, and a closet in sub-zero temperatures to gain a Void Prison.
    \ + A Void Prison is an orb that, when used, traps all nearby unmarked heathens into a stasis ball for 10 seconds. \ + While in the ball, they are unable to speak, act, or be harmed. The Void Prison is consumed after one use." + transmute_text = "Transmute a set of handcuffs, a stun baton, and a closet in sub-zero temperatures." gain_text = "At first, I see myself, waltzing along a snow-laden street. \ I try to yell, grab hold of this fool and tell them to run. \ But the only welts made are on my own beating fist. \ My smiling face turns to regard me, reflecting back in glassy eyes the empty path I have been lead down." - - action_to_add = /datum/action/cooldown/spell/pointed/void_prison + required_atoms = list( + /obj/item/restraints/handcuffs = 1, + /obj/structure/closet = 1, + /obj/item/melee/baton/security = 1, + ) + result_atoms = list(/obj/item/void_prison) cost = 2 drafting_tier = 5 + research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi' + research_tree_icon_state = "voidball" + + var/list/closet_blacklist = list( + /obj/structure/closet/crate, + /obj/structure/closet/body_bag, + /obj/structure/closet/cardboard, + /obj/structure/closet/infinite, + ) + +/datum/heretic_knowledge/void_prison/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc) + . = ..() + for(var/obj/structure/closet/closet in atoms) + if(is_type_in_list(closet, closet_blacklist)) + atoms -= closet + +/datum/heretic_knowledge/void_prison/cleanup_atoms(list/selected_atoms) + for(var/obj/structure/closet/closet in selected_atoms) + closet.dump_contents() + + return ..() /datum/heretic_knowledge/armor/void - name = "Hollow Weave" - desc = "Allows you to transmute a table (or a suit) and a mask in sub-zero temperatures to create a Hollow Weave, this armor will periodically nullify attacks and grant you a short stealth camoflage to reposition yourself. \ - Acts as a focus while hooded." + desc = "Create a Hollow Weave.
    \ + This armor will periodically nullify attacks and grant you a short stealth camouflage to reposition yourself." + transmute_text = "Transmute a table (or a suit) and a mask in sub-zero temperatures." gain_text = "Stepping through the cold air, I am shocked by a new sensation. \ - Thousands of almost imperceivable threads cling to my form. \ - I am left adrift with every step. \ - Even as I hear the crunch of snow as I plant my foot to the ground, I feel nothing." + Thousands of almost imperceivable threads cling to my form. \ + I am left adrift with every step. \ + Even as I hear the crunch of snow as I plant my foot to the ground, I feel nothing." result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch/void) research_tree_icon_state = "void_armor" required_atoms = list( @@ -142,13 +174,17 @@ action_to_add = /datum/action/cooldown/spell/aoe/void_pull cost = 2 research_tree_icon_frame = 6 + max_charges = 4 + path_recharge_amount = 0.25 + focus_recharge_amount = 0.25 + holywater_drain_amount = 0.25 /datum/heretic_knowledge/blade_upgrade/void name = "Seeking Blade" - desc = "Your blade now freezes enemies. Additionally, you can now attack distant marked targets with your Void Blade, teleporting directly next to them." + desc = "Your blade now freezes enemies.
    \ + Additionally, you can now attack distant marked targets with your Void Blade, teleporting directly next to them." gain_text = "Fleeting memories, fleeting feet. I mark my way with frozen blood upon the snow. Covered and forgotten." - research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi' research_tree_icon_state = "blade_upgrade_void" @@ -158,8 +194,11 @@ target.apply_status_effect(/datum/status_effect/void_chill, 2) -/datum/heretic_knowledge/blade_upgrade/void/do_ranged_effects(mob/living/user, mob/living/target, obj/item/melee/sickly_blade/blade) - if(!target.has_status_effect(/datum/status_effect/eldritch)) +/datum/heretic_knowledge/blade_upgrade/void/do_ranged_effects(mob/living/user, atom/target, obj/item/melee/sickly_blade/blade) + if(!isliving(target)) + return + var/mob/living/living_target = target + if(!living_target.has_status_effect(/datum/status_effect/eldritch)) return var/dir = angle2dir(dir2angle(get_dir(user, target)) + 180) @@ -170,23 +209,94 @@ /datum/heretic_knowledge/blade_upgrade/void/proc/follow_up_attack(mob/living/user, mob/living/target, obj/item/melee/sickly_blade/blade) blade.melee_attack_chain(user, target) -/datum/heretic_knowledge/spell/void_conduit +/datum/heretic_knowledge/void_conduit name = "Void Conduit" - desc = "Grants you Void Conduit, a spell which summons a pulsing gate to the Void itself. Every pulse breaks windows and airlocks, while afflicting Heathens with an eldritch chill and shielding Heretics against low pressure." + desc = "Empowers your blade, allowing you to rip a hole through space itself.
    \ + Attacking space with one of your void blades will create conduit to the void, \ + damaging and chilling nearby heathens, and destroying windows and airlocks in the area." + notice = "The blade is consumed in the process. You can also use this ability on snow, or any tile in a complete vacuum." gain_text = "The hum in the still, cold air turns to a cacophonous rattle. \ Over the noise, there is no distinction to the clattering of window panes and the yawning knowledge that ricochets through my skull. \ The doors won't close. I can't keep the cold out now." - action_to_add = /datum/action/cooldown/spell/conjure/void_conduit cost = 2 is_final_knowledge = TRUE + research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi' + research_tree_icon_state = "void_rift" + research_tree_icon_frame = 12 + +/datum/heretic_knowledge/void_conduit/on_gain(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + RegisterSignal(user, COMSIG_HERETIC_BLADE_PREATTACK, PROC_REF(on_blade_preattack)) + +/datum/heretic_knowledge/void_conduit/on_lose(mob/user, datum/antagonist/heretic/our_heretic) + . = ..() + UnregisterSignal(user, COMSIG_HERETIC_BLADE_PREATTACK) + +/datum/heretic_knowledge/void_conduit/proc/is_valid_turf(turf/open/affected_turf) + // space is the obvious one, snow is for icebox + if(isspaceturf(affected_turf) || issnowturf(affected_turf)) + return TRUE + // works in any vacuum as a backup + var/datum/gas_mixture/air = affected_turf.return_air() + if(air?.return_pressure() <= 0) + return TRUE + return FALSE + +/datum/heretic_knowledge/void_conduit/proc/is_valid_turf_callback(turf/open/affected_turf, mob/living/source, obj/item/sword) + if(!source.is_holding(sword)) + return FALSE + if(!is_valid_turf(affected_turf)) + return FALSE + if(affected_turf.GetTemperature() > T0C) + return FALSE + return TRUE + +/datum/heretic_knowledge/void_conduit/proc/on_blade_preattack(mob/living/source, atom/target, obj/item/sword) + SIGNAL_HANDLER + if(!isopenturf(target)) + return NONE + + var/turf/open/affected_turf = target + if(!is_valid_turf(affected_turf)) + return NONE + + if(affected_turf.GetTemperature() <= T0C) + INVOKE_ASYNC(src, PROC_REF(create_conduit), affected_turf, source, sword) + return COMPONENT_CANCEL_ATTACK_CHAIN + + to_chat(source, span_mansus("[sword] hums with power, but [target] is not cold enough to create a conduit!")) + return NONE + +/datum/heretic_knowledge/void_conduit/proc/create_conduit(turf/open/affected_turf, mob/living/source, obj/item/sword) + playsound(source, 'sound/effects/cloth_rip.ogg', 50, TRUE) // funny thing is, can't hear sound in a vacuum + to_chat(source, span_mansus("You plunge [sword] deep into [affected_turf], trying to rip open a conduit to the void!")) + source.visible_message( + span_hypnophrase("[source] plunges [source.p_their()] [sword.name] into [affected_turf] - \ + [isspaceturf(affected_turf) ? "but instead of nothing happening" : "contrary to what you expected"], a dark energy begins to flow from the site!"), + ignored_mobs = source, + ) + var/obj/effect/temp_visual/void_conduit_opening/animation = new(affected_turf) + if(!do_after(source, 5 SECONDS, affected_turf, extra_checks = CALLBACK(src, PROC_REF(is_valid_turf_callback), affected_turf, source, sword))) + animate(animation, alpha = 0, time = 1 SECONDS) + QDEL_IN(animation, 1 SECONDS) + return + to_chat(source, span_mansus("The conduit opens, releasing a storm of void energy! [sword] shatters into a million tiny shards!")) + source.visible_message( + span_hypnophrase("A conduit to the void opens, releasing a storm of void energy!"), + ignored_mobs = source, + ) + new /obj/structure/void_conduit(affected_turf) + source.dropItemToGround(sword) + qdel(sword) + playsound(source, SFX_SHATTER, 50, FALSE) /datum/heretic_knowledge/ultimate/void_final name = "Waltz at the End of Time" - desc = "The ascension ritual of the Path of Void. \ - Bring 3 corpses to a transmutation rune in sub-zero temperatures to complete the ritual. \ + desc = "The ascension ritual of the Path of Void.
    \ When completed, causes a violent storm of void snow \ - to assault the station, freezing and damaging heathens. Those nearby will be silenced and frozen even quicker. \ + to assault the station, freezing and damaging heathens. Those nearby will be silenced and frozen even quicker.
    \ Additionally, you will become immune to the effects of space." + transmute_text = "Transmute three corpses in sub-zero temperatures." gain_text = "The world falls into darkness. I stand in an empty plane, small flakes of ice fall from the sky. \ The Aristocrat stands before me, beckoning. We will play a waltz to the whispers of dying reality, \ as the world is destroyed before our eyes. The void will return all to nothing, WITNESS MY ASCENSION!" diff --git a/code/modules/antagonists/heretic/magic/ash_jaunt.dm b/code/modules/antagonists/heretic/magic/ash_jaunt.dm index 6aaac43be905..7029cfabdf8a 100644 --- a/code/modules/antagonists/heretic/magic/ash_jaunt.dm +++ b/code/modules/antagonists/heretic/magic/ash_jaunt.dm @@ -20,59 +20,6 @@ jaunt_type = /obj/effect/dummy/phased_mob/spell_jaunt/red jaunt_in_type = /obj/effect/temp_visual/dir_setting/ash_shift jaunt_out_type = /obj/effect/temp_visual/dir_setting/ash_shift/out - /// If we are on fire while wearing ash robes, we can empower our next cast - var/empowered_cast = FALSE - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/Grant(mob/grant_to) - . = ..() - RegisterSignal(grant_to, COMSIG_FIRE_STACKS_UPDATED, PROC_REF(update_status_on_signal)) - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/Remove(mob/remove_from) - . = ..() - UnregisterSignal(remove_from, COMSIG_FIRE_STACKS_UPDATED) - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/apply_button_overlay(atom/movable/screen/movable/action_button/current_button, force) - . = ..() - // Put an active border whenever our spell is able to be casted empowered - if(!ishuman(owner)) - return - var/mob/living/carbon/human/human_owner = owner - if(!istype(human_owner.wear_suit, /obj/item/clothing/suit/hooded/cultrobes/eldritch/ash)) - return - if(human_owner.fire_stacks <= 3) - return - - current_button.cut_overlay(current_button.button_overlay) - current_button.button_overlay = mutable_appearance(icon = overlay_icon, icon_state = "bg_spell_border_active_green") - current_button.add_overlay(current_button.button_overlay) - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/cast(mob/living/cast_on) - if(!iscarbon(owner)) - return ..() - - // Wearing Ash heretic armor empowers your spells if you have over 3 fire stacks - if(!ishuman(owner)) - return ..() - var/mob/living/carbon/human/human_owner = owner - if(human_owner.fire_stacks <= 3) - return ..() - if(!istype(human_owner.wear_suit, /obj/item/clothing/suit/hooded/cultrobes/eldritch/ash)) - return ..() - - empowered_cast = TRUE - human_owner.set_stamina_loss(0) - human_owner.SetAllImmobility(0) - var/mob/living/carbon/carbon_owner = owner - carbon_owner.uncuff() - var/obj/item/clothing/shoes/shoes = carbon_owner.get_item_by_slot(ITEM_SLOT_FEET) - if(istype(shoes) && shoes.tied == SHOES_KNOTTED) - shoes.adjust_laces(SHOES_TIED, carbon_owner) - - return ..() - -/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/do_jaunt(mob/living/cast_on) - jaunt_duration = (empowered_cast ? 1.5 SECONDS : initial(jaunt_duration)) - return ..() /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/do_steam_effects() return diff --git a/code/modules/antagonists/heretic/magic/flesh_surgery.dm b/code/modules/antagonists/heretic/magic/flesh_surgery.dm index c50b9c81e12d..546bee7af0fd 100644 --- a/code/modules/antagonists/heretic/magic/flesh_surgery.dm +++ b/code/modules/antagonists/heretic/magic/flesh_surgery.dm @@ -168,7 +168,7 @@ new /obj/effect/temp_visual/cult/sparks(get_turf(to_heal)) var/condition = (to_heal.damage > 0) ? "better" : "perfect" caster.visible_message( - span_warning("[caster]'s hand glows a brilliant red as [caster.p_they()] restore \the [to_heal] to [condition] condition!"), + span_warning("[caster]'s hand glows a brilliant red as [caster.p_they()] restore[caster.p_s()] \the [to_heal] to [condition] condition!"), span_notice("Your hand glows a brilliant red as you restore \the [to_heal] to [condition] condition!"), ) @@ -274,7 +274,7 @@ carbon_victim.balloon_alert(caster, "[chosen_organ] removed") carbon_victim.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK_RED) playsound(victim, 'sound/effects/dismember.ogg', 50, TRUE) - if(carbon_victim.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(carbon_victim)) carbon_victim.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic) carbon_victim.emote("scream") @@ -323,12 +323,12 @@ if(using_on_self) caster.visible_message( - span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_es()] forcing [inserted_organ] into [caster.p_their()] [zone_organ_goes_in]!!"), + span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_s()] forcing [inserted_organ] into [caster.p_their()] [zone_organ_goes_in]!!"), span_userdanger("You begin forcing [inserted_organ] into your [zone_organ_goes_in]!") ) else caster.visible_message( - span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_es()] forcing [inserted_organ] into [victim]'s [zone_organ_goes_in]!!"), + span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_s()] forcing [inserted_organ] into [victim]'s [zone_organ_goes_in]!!"), span_notice("You begin forcing [inserted_organ] into [victim]'s [zone_organ_goes_in].") ) @@ -358,7 +358,7 @@ victim.balloon_alert(caster, "[inserted_organ] inserted") victim.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK_RED) playsound(victim, 'sound/effects/dismember.ogg', 50, TRUE) - if(victim.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(victim)) victim.emote("scream") if(!using_on_self) victim.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic) diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm index 237fd52ec02d..e8004e088cf9 100644 --- a/code/modules/antagonists/heretic/magic/furious_steel.dm +++ b/code/modules/antagonists/heretic/magic/furious_steel.dm @@ -27,24 +27,6 @@ /// A ref to the status effect surrounding our heretic on activation. var/datum/status_effect/protective_blades/blade_effect -/datum/action/cooldown/spell/pointed/projectile/furious_steel/Grant(mob/grant_to) - . = ..() - if(!owner) - return - - if(IS_HERETIC(owner)) - RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING), PROC_REF(on_focus_lost)) - -/datum/action/cooldown/spell/pointed/projectile/furious_steel/Remove(mob/remove_from) - UnregisterSignal(remove_from, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING)) - return ..() - -/// Signal proc for [SIGNAL_REMOVETRAIT], via [TRAIT_ALLOW_HERETIC_CASTING], to remove the effect when we lose the focus trait -/datum/action/cooldown/spell/pointed/projectile/furious_steel/proc/on_focus_lost(mob/source) - SIGNAL_HANDLER - - unset_click_ability(source, refund_cooldown = TRUE) - /datum/action/cooldown/spell/pointed/projectile/furious_steel/InterceptClickOn(mob/living/clicker, params, atom/target) // Let the caster prioritize using items like guns over blade casts if(clicker.get_active_held_item()) @@ -112,7 +94,8 @@ /datum/action/cooldown/spell/pointed/projectile/furious_steel/proc/on_status_effect_triggered(datum/status_effect/protective_blades/source, atom/target) SIGNAL_HANDLER - current_amount-- + if(blade_effect == source) + current_amount -= 1 /obj/projectile/floating_blade name = "blade" diff --git a/code/modules/antagonists/heretic/magic/lunatic_track.dm b/code/modules/antagonists/heretic/magic/lunatic_track.dm index ae41e77aabeb..9d3f7ce8e4ef 100644 --- a/code/modules/antagonists/heretic/magic/lunatic_track.dm +++ b/code/modules/antagonists/heretic/magic/lunatic_track.dm @@ -24,7 +24,7 @@ owner.balloon_alert(owner, get_balloon_message(ascended_heretic)) if(ascended_heretic.stat == DEAD) - to_chat(owner, span_hierophant("[ascended_heretic] is dead. Weep for the lie has struck out.")) + to_chat(owner, span_mansus("[ascended_heretic] is dead. Weep for the lie has struck out.")) StartCooldown() return TRUE diff --git a/code/modules/antagonists/heretic/magic/manse_link.dm b/code/modules/antagonists/heretic/magic/manse_link.dm index 06fd4dd9863f..e99b4dc16803 100644 --- a/code/modules/antagonists/heretic/magic/manse_link.dm +++ b/code/modules/antagonists/heretic/magic/manse_link.dm @@ -55,7 +55,7 @@ to_chat(owner, span_notice("You begin linking [linkee]'s mind to yours...")) to_chat(linkee, span_warning("You feel your mind being pulled somewhere... connected... intertwined with the very fabric of reality...")) - if(!do_after(owner, link_time, linkee, hidden = TRUE)) + if(!do_after(owner, link_time, linkee, cog_icon = null)) to_chat(owner, span_warning("You fail to link to [linkee]'s mind.")) to_chat(linkee, span_warning("The foreign presence leaves your mind.")) return FALSE diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm index 78f550ccec20..ef81ba54f1a0 100644 --- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm +++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm @@ -120,7 +120,7 @@ return SHAME var/escape_our_torment = 0 - while(carbon_user.stat == CONSCIOUS) + while(!IS_UNCONSCIOUS_OR_CRIT(carbon_user)) if(QDELETED(src) || QDELETED(user)) return SHAME if(escape_our_torment > 20) //Stops us from infinitely stunning ourselves if we're just not taking the damage diff --git a/code/modules/antagonists/heretic/magic/mirror_walk.dm b/code/modules/antagonists/heretic/magic/mirror_walk.dm index 9aa5de82ed1a..29b693d0bef3 100644 --- a/code/modules/antagonists/heretic/magic/mirror_walk.dm +++ b/code/modules/antagonists/heretic/magic/mirror_walk.dm @@ -65,7 +65,7 @@ jaunter.Beam(nearby_reflection, icon_state = "light_beam", time = phase_out_time) nearby_reflection.visible_message(span_warning("[nearby_reflection] begins to shimmer and shake slightly!")) - if(!do_after(jaunter, phase_out_time, nearby_reflection, IGNORE_USER_LOC_CHANGE|IGNORE_INCAPACITATED, hidden = TRUE)) + if(!do_after(jaunter, phase_out_time, nearby_reflection, IGNORE_USER_LOC_CHANGE|IGNORE_INCAPACITATED, cog_icon = null)) return playsound(jaunter, 'sound/effects/magic/ethereal_enter.ogg', 50, TRUE, -1) @@ -97,7 +97,7 @@ nearby_reflection.Beam(phase_turf, icon_state = "light_beam", time = phase_in_time) nearby_reflection.visible_message(span_warning("[nearby_reflection] begins to shimmer and shake slightly!")) - if(!do_after(unjaunter, phase_in_time, nearby_reflection, hidden = TRUE)) + if(!do_after(unjaunter, phase_in_time, nearby_reflection, cog_icon = null)) return FALSE // We can move around while phasing in, but we'll always end up where we started it. diff --git a/code/modules/antagonists/heretic/magic/rust_charge.dm b/code/modules/antagonists/heretic/magic/rust_charge.dm index 506ef1949b4b..b8fbd895a278 100644 --- a/code/modules/antagonists/heretic/magic/rust_charge.dm +++ b/code/modules/antagonists/heretic/magic/rust_charge.dm @@ -2,8 +2,7 @@ /datum/action/cooldown/mob_cooldown/charge/rust name = "Rust Charge" desc = "A charge that must be started on a rusted tile and will destroy any rusted objects you come into contact with, \ - will deal high damage to others and rust around you during the charge. \ - As it is the rust that empowers you with this ability, no focus is needed." + will deal high damage to others and rust around you during the charge." charge_distance = 10 charge_damage = 25 cooldown_time = 45 SECONDS diff --git a/code/modules/antagonists/heretic/magic/shadow_cloak.dm b/code/modules/antagonists/heretic/magic/shadow_cloak.dm index 0ecd355ae989..bf9c8fdb203c 100644 --- a/code/modules/antagonists/heretic/magic/shadow_cloak.dm +++ b/code/modules/antagonists/heretic/magic/shadow_cloak.dm @@ -33,6 +33,9 @@ return FALSE return isliving(cast_on) +/datum/action/cooldown/spell/shadow_cloak/is_action_active(atom/movable/screen/movable/action_button/current_button) + return !!active_cloak + /datum/action/cooldown/spell/shadow_cloak/before_cast(mob/living/cast_on) . = ..() sound = pick( @@ -49,7 +52,7 @@ /datum/action/cooldown/spell/shadow_cloak/cast(mob/living/cast_on) . = ..() if(active_cloak) - var/new_cd = max((uncloak_time - timeleft(uncloak_timer)) / 3, cooldown_time) + var/new_cd = max((uncloak_time - timeleft(uncloak_timer)), cooldown_time) uncloak_mob(cast_on) StartCooldown(new_cd) @@ -63,7 +66,7 @@ return uncloak_mob(cast_on) - StartCooldown(uncloak_timer / 3) + StartCooldown(uncloak_timer) /datum/action/cooldown/spell/shadow_cloak/proc/cloak_mob(mob/living/cast_on) playsound(cast_on, 'sound/effects/chemistry/ahaha.ogg', 50, TRUE, -1, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.5) @@ -74,7 +77,6 @@ active_cloak = cast_on.apply_status_effect(/datum/status_effect/shadow_cloak) RegisterSignal(active_cloak, COMSIG_QDELETING, PROC_REF(on_early_cloak_loss)) - RegisterSignal(cast_on, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING), PROC_REF(on_focus_lost)) /datum/action/cooldown/spell/shadow_cloak/proc/uncloak_mob(mob/living/cast_on, show_message = TRUE) if(!QDELETED(active_cloak)) @@ -82,7 +84,6 @@ qdel(active_cloak) active_cloak = null - UnregisterSignal(cast_on, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING)) playsound(cast_on, 'sound/effects/curse/curseattack.ogg', 50) if(show_message) cast_on.visible_message( @@ -108,18 +109,7 @@ removed.Knockdown(0.5 SECONDS) removed.add_movespeed_modifier(/datum/movespeed_modifier/shadow_cloak/early_remove) addtimer(CALLBACK(removed, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/shadow_cloak/early_remove), 2 MINUTES, TIMER_UNIQUE|TIMER_OVERRIDE) - StartCooldown(uncloak_time * 2/3) - -/// Signal proc for [SIGNAL_REMOVETRAIT] via [TRAIT_ALLOW_HERETIC_CASTING], losing our focus midcast will throw us out. -/datum/action/cooldown/spell/shadow_cloak/proc/on_focus_lost(mob/living/source) - SIGNAL_HANDLER - - uncloak_mob(source, show_message = FALSE) - source.visible_message( - span_warning("[source] suddenly appears from the shadows!"), - span_userdanger("As you lose your focus, you are pulled out of the shadows!"), - ) - StartCooldown(uncloak_time / 3) + StartCooldown(uncloak_time) /// Shadow cloak effect. Conceals the owner in a cloud of purple smoke, making them unidentifiable. /// Also comes with some other buffs and debuffs - faster movespeed, slower actionspeed, etc. @@ -147,7 +137,7 @@ // Register signals to cause effects RegisterSignal(owner, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_dir_change)) RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(on_body_position_change)) - RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) + RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_KNOCKEDOUT), PROC_REF(on_stat_change)) RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_damaged)) RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) return TRUE @@ -164,7 +154,7 @@ UnregisterSignal(owner, list( COMSIG_ATOM_DIR_CHANGE, COMSIG_LIVING_SET_BODY_POSITION, - COMSIG_MOB_STATCHANGE, + SIGNAL_ADDTRAIT(TRAIT_KNOCKEDOUT), COMSIG_MOB_APPLY_DAMAGE, COMSIG_MOVABLE_MOVED, )) @@ -184,13 +174,12 @@ else cloak_image.transform = turn(cloak_image.transform, -90) -/// Signal proc for [COMSIG_MOB_STATCHANGE], going past soft crit will stop the effect +/// Signal proc for [SIGNAL_ADDTRAIT(TRAIT_KNOCKEDOUT], falling unconscious (from hard crit or otherwise) will stop the effect /datum/status_effect/shadow_cloak/proc/on_stat_change(datum/source, new_stat, old_stat) SIGNAL_HANDLER // Going above unconscious will self-delete - if(new_stat >= UNCONSCIOUS) - qdel(src) + qdel(src) /// Signal proc for [COMSIG_MOB_APPLY_DAMAGE], being damaged past a threshold will roll a chance to stop the effect /datum/status_effect/shadow_cloak/proc/on_damaged(datum/source, damage, damagetype, ...) diff --git a/code/modules/antagonists/heretic/magic/space_crawl.dm b/code/modules/antagonists/heretic/magic/space_crawl.dm index 6ff60144f019..89e53c9b0f6c 100644 --- a/code/modules/antagonists/heretic/magic/space_crawl.dm +++ b/code/modules/antagonists/heretic/magic/space_crawl.dm @@ -90,11 +90,6 @@ RegisterSignal(holder, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal)) if(iscarbon(jaunter)) jaunter.drop_all_held_items() - // Sanity check to ensure we didn't lose our focus as a result. - if(!HAS_TRAIT(jaunter, TRAIT_ALLOW_HERETIC_CASTING)) - REMOVE_TRAIT(jaunter, TRAIT_NO_TRANSFORM, REF(src)) - exit_jaunt(jaunter, our_turf) - return FALSE // Give them some space hands to prevent them from doing things var/obj/item/space_crawl/left_hand = new(jaunter) var/obj/item/space_crawl/right_hand = new(jaunter) diff --git a/code/modules/antagonists/heretic/magic/void_prison.dm b/code/modules/antagonists/heretic/magic/void_prison.dm deleted file mode 100644 index 6bc205b87490..000000000000 --- a/code/modules/antagonists/heretic/magic/void_prison.dm +++ /dev/null @@ -1,103 +0,0 @@ -/datum/action/cooldown/spell/pointed/void_prison - name = "Void Prison" - desc = "Sends a heathen into the void for 10 seconds. \ - They will be unable to perform any actions for the duration. \ - Afterwards, they will be chilled and returned to the mortal plane." - background_icon_state = "bg_heretic" - overlay_icon_state = "bg_heretic_border" - button_icon = 'icons/mob/actions/actions_ecult.dmi' - button_icon_state = "voidball" - ranged_mousepointer = 'icons/effects/mouse_pointers/throw_target.dmi' - sound = 'sound/effects/magic/voidblink.ogg' - - cooldown_time = 1 MINUTES - cast_range = 3 - - sound = null - school = SCHOOL_FORBIDDEN - invocation = "V'D PR'S'N!" - invocation_type = INVOCATION_SHOUT - spell_requirements = NONE - -/datum/action/cooldown/spell/pointed/void_prison/before_cast(atom/cast_on) - . = ..() - if(. & SPELL_CANCEL_CAST) - return - if(!ismob(cast_on)) - return SPELL_CANCEL_CAST - -/datum/action/cooldown/spell/pointed/void_prison/cast(mob/living/carbon/human/cast_on) - . = ..() - if(cast_on.can_block_magic(antimagic_flags)) - cast_on.visible_message( - span_danger("A swirling, cold void wraps around [cast_on], but they burst free in a wave of heat!"), - span_danger("A yawning void begins to open before you, but a great wave of heat bursts it apart! You are protected!!") - ) - return - cast_on.apply_status_effect(/datum/status_effect/void_prison, "void_stasis") - -/datum/status_effect/void_prison - id = "void_prison" - duration = 10 SECONDS - alert_type = /atom/movable/screen/alert/status_effect/void_prison - ///The overlay that gets applied to whoever has this status active - var/obj/effect/abstract/voidball/stasis_overlay - -/datum/status_effect/void_prison/on_creation(mob/living/new_owner, set_duration) - . = ..() - stasis_overlay = new /obj/effect/abstract/voidball(new_owner) - RegisterSignal(stasis_overlay, COMSIG_QDELETING, PROC_REF(clear_overlay)) - new_owner.vis_contents += stasis_overlay - stasis_overlay.animate_opening() - addtimer(CALLBACK(src, PROC_REF(enter_prison), new_owner), 1 SECONDS) - -/datum/status_effect/void_prison/on_remove() - if(!IS_HERETIC(owner)) - owner.apply_status_effect(/datum/status_effect/void_chill, 1) - if(stasis_overlay) - //Free our prisoner - owner.remove_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM, TRAIT_SOFTSPOKEN), TRAIT_STATUS_EFFECT(id)) - owner.forceMove(get_turf(stasis_overlay)) - stasis_overlay.forceMove(owner) - owner.vis_contents += stasis_overlay - //Animate closing the ball - stasis_overlay.animate_closing() - stasis_overlay.icon_state = "voidball_closed" - QDEL_IN(stasis_overlay, 1.1 SECONDS) - stasis_overlay = null - return ..() - -///Freezes our prisoner in place -/datum/status_effect/void_prison/proc/enter_prison(mob/living/prisoner) - stasis_overlay.forceMove(prisoner.loc) - prisoner.forceMove(stasis_overlay) - prisoner.add_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM, TRAIT_SOFTSPOKEN), TRAIT_STATUS_EFFECT(id)) - -///Makes sure to clear the ref in case the voidball ever suddenly disappears -/datum/status_effect/void_prison/proc/clear_overlay() - SIGNAL_HANDLER - stasis_overlay = null - -//----Voidball effect -/obj/effect/abstract/voidball - icon = 'icons/mob/actions/actions_ecult.dmi' - icon_state = "voidball_effect" - layer = ABOVE_ALL_MOB_LAYER - vis_flags = VIS_INHERIT_ID - -///Plays a opening animation -/obj/effect/abstract/voidball/proc/animate_opening() - flick("voidball_opening", src) - -///Plays a closing animation -/obj/effect/abstract/voidball/proc/animate_closing() - flick("voidball_closing", src) - -//---- Screen alert -/atom/movable/screen/alert/status_effect/void_prison - name = "Void Prison" - desc = "A Yawning void encases your mortal coil." //Go straight to jail, do not pass GO, do not collect 200$ - use_user_hud_icon = USER_HUD_STYLE_INHERIT - icon_state = "heretic_template" - overlay_icon = 'icons/mob/actions/actions_ecult.dmi' - overlay_state = "voidball_effect" diff --git a/code/modules/antagonists/heretic/magic/wolves_among_sheep.dm b/code/modules/antagonists/heretic/magic/wolves_among_sheep.dm index ab52bbf4152c..cc107510b5b1 100644 --- a/code/modules/antagonists/heretic/magic/wolves_among_sheep.dm +++ b/code/modules/antagonists/heretic/magic/wolves_among_sheep.dm @@ -39,7 +39,6 @@ center_turf = get_turf(owner) playsound(center_turf,'sound/machines/airlock/airlockopen.ogg', 750, TRUE) to_transform = list() - new /obj/effect/heretic_rune/big(center_turf) addtimer(CALLBACK(src, PROC_REF(create_arena), center_turf), 1 SECONDS) revert_timer = addtimer(CALLBACK(src, PROC_REF(revert_effects)), 61 SECONDS, TIMER_STOPPABLE) // 1 second to spread out, 60 seconds to fight @@ -94,7 +93,7 @@ /// Sets up the proximity monitor which handles things that are within the area and leave once they get someone to crit /datum/action/cooldown/spell/wolves_among_sheep/proc/create_arena(turf/target) - RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION)), PROC_REF(on_caster_crit)) + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_caster_crit)) // This is where most of the funcionality of the spell is ongoing_arena = new /obj/effect/abstract/heretic_arena(target, max_range, 60 SECONDS, owner) @@ -108,14 +107,16 @@ revert_effects() /// If the caster goes into crit, the arena falls apart right away -/datum/action/cooldown/spell/wolves_among_sheep/proc/on_caster_crit() +/datum/action/cooldown/spell/wolves_among_sheep/proc/on_caster_crit(datum/source, new_stat, old_stat) SIGNAL_HANDLER + if(new_stat < SOFT_CRIT) + return deltimer(revert_timer) revert_effects() /// Undoes our changes /datum/action/cooldown/spell/wolves_among_sheep/proc/revert_effects() - UnregisterSignal(owner, list(SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION))) + UnregisterSignal(owner, COMSIG_MOB_STATCHANGE) for(var/iterator in 1 to greatest_dist) var/backwards_iterator = greatest_dist - iterator + 1 //We go backwards if(!to_transform["[backwards_iterator]"]) diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm index e48020494339..f0a10abf8c59 100644 --- a/code/modules/antagonists/heretic/status_effects/buffs.dm +++ b/code/modules/antagonists/heretic/status_effects/buffs.dm @@ -62,12 +62,10 @@ /datum/status_effect/duskndawn/on_apply() ADD_TRAIT(owner, TRAIT_XRAY_VISION, TRAIT_STATUS_EFFECT(id)) - owner.update_sight() return TRUE /datum/status_effect/duskndawn/on_remove() REMOVE_TRAIT(owner, TRAIT_XRAY_VISION, TRAIT_STATUS_EFFECT(id)) - owner.update_sight() // WOUNDED SOLDIER /datum/status_effect/marshal @@ -229,6 +227,7 @@ return SEND_SIGNAL(src, COMSIG_BLADE_BARRIER_TRIGGERED) + SEND_SIGNAL(source, COMSIG_MOB_BLADE_BARRIER_TRIGGERED, src) ADD_TRAIT(source, TRAIT_BEING_BLADE_SHIELDED, TRAIT_STATUS_EFFECT(id)) addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_BEING_BLADE_SHIELDED, TRAIT_STATUS_EFFECT(id)), 0.1 SECONDS) @@ -298,7 +297,6 @@ /datum/status_effect/caretaker_refuge/on_apply() animate(owner, alpha = 45, time = 0.5 SECONDS) owner.set_density(FALSE) - RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING), PROC_REF(on_focus_lost)) RegisterSignal(owner, COMSIG_MOB_BEFORE_SPELL_CAST, PROC_REF(prevent_spell_usage)) RegisterSignal(owner, COMSIG_ATOM_HOLYATTACK, PROC_REF(nullrod_handler)) RegisterSignal(owner, COMSIG_CARBON_CUFF_ATTEMPTED, PROC_REF(prevent_cuff)) @@ -310,7 +308,6 @@ owner.remove_traits(caretaking_traits, TRAIT_STATUS_EFFECT(id)) owner.alpha = initial(owner.alpha) owner.density = initial(owner.density) - UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING)) UnregisterSignal(owner, COMSIG_MOB_BEFORE_SPELL_CAST) UnregisterSignal(owner, COMSIG_ATOM_HOLYATTACK) UnregisterSignal(owner, COMSIG_CARBON_CUFF_ATTEMPTED) @@ -329,11 +326,6 @@ owner.visible_message(span_warning("[weapon] repels the haze around [owner]!")) owner.remove_status_effect(type) -/datum/status_effect/caretaker_refuge/proc/on_focus_lost() - SIGNAL_HANDLER - to_chat(owner, span_danger("Without a focus, your refuge weakens and dissipates!")) - qdel(src) - /datum/status_effect/caretaker_refuge/proc/no_strip(atom/source, mob/user, obj/item/equipping) SIGNAL_HANDLER to_chat(user, span_warning("You fail to put anything on [source] as they are incorporeal!")) diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index f7c64df79ddd..f38aa4e0f46b 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -312,7 +312,7 @@ tick_interval = 10 SECONDS /datum/status_effect/eldritch_painting/weeping/on_tick(seconds_between_ticks) - if(owner.stat != CONSCIOUS || owner.IsSleeping() || owner.IsUnconscious()) + if(IS_UNCONSCIOUS(owner)) return owner.cause_hallucination(/datum/hallucination/delusion/preset/heretic, "Caused by The Weeping status effect") diff --git a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm index 08d2292d677a..c30ce76318c0 100644 --- a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm +++ b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm @@ -14,6 +14,9 @@ var/passive_level = HERETIC_LEVEL_START /// Name of the passive, used by the UI var/name = "Heretic Passive" + /// Describes how users of this path recharge their spells + var/recharge_description = "Recharge spells by doing things." + /// Describes the various levels of the passive var/list/passive_descriptions = list( "Grants you a passive ability based on your heretic type. This ability will upgrade as you gain more power.", "Your passive ability has been upgraded, doing something else.", @@ -64,6 +67,17 @@ heretic_datum.passive_level = HERETIC_LEVEL_FINAL heretic_datum.update_data_for_all_viewers() +/datum/status_effect/heretic_passive/proc/recharge_spells() + owner.balloon_alert(owner, "spells recharged") + var/list/main_path_knowledge = heretic_datum.get_researched_knowledge_by_category(HERETIC_KNOWLEDGE_TREE) \ + + heretic_datum.get_researched_knowledge_by_category(HERETIC_KNOWLEDGE_START) + var/list/side_path_knowledge = heretic_datum.get_researched_knowledge_by_category(HERETIC_KNOWLEDGE_DRAFT) \ + + heretic_datum.get_researched_knowledge_by_category(HERETIC_KNOWLEDGE_SHOP) + + for(var/datum/heretic_knowledge/spell/spell in main_path_knowledge) + spell.add_charges(ceil(spell.max_charges * spell.path_recharge_amount), uncapped = spell.path_recharge_can_surpass_cap) + for(var/datum/heretic_knowledge/spell/spell in side_path_knowledge) + spell.add_charges(ceil(spell.max_charges * spell.path_recharge_amount * 0.5)) //---- Ash Passive // Level 1 grants heat and ash storm immunity @@ -71,11 +85,14 @@ // Level 3 grants resistance to high pressure /datum/status_effect/heretic_passive/ash name = "Vow of Destruction" + recharge_description = "Recharge spells by standing near living, burning foes." passive_descriptions = list( "Heat and ash storm immunity.", "Lava immunity.", "Resistance to high and low pressure." ) + /// Tracks total seconds nearby mobs are on fire, used to determine when to recharge spells + var/seconds_of_fire = 0 /datum/status_effect/heretic_passive/ash/on_apply() . = ..() @@ -93,6 +110,26 @@ owner.remove_traits(list(TRAIT_RESISTHEAT, TRAIT_ASHSTORM_IMMUNE, TRAIT_LAVA_IMMUNE, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE), REF(src)) return ..() +/datum/status_effect/heretic_passive/ash/tick(seconds_between_ticks) + . = ..() + var/seconds_gained = 0 + for(var/mob/living/nearby_guy in view(owner, 3)) + if(!nearby_guy.on_fire || nearby_guy.stat == DEAD || nearby_guy == owner) + continue + if(ismonkey(nearby_guy) && isnull(nearby_guy.mind)) + continue + + seconds_gained += seconds_between_ticks + if(seconds_gained >= 6) + break + + seconds_of_fire += seconds_gained + if(seconds_of_fire < 30) + return + + seconds_of_fire = 0 + recharge_spells() + //---- Blade Passive // Gives you riposte while wielding a heretic blade // Cooldown starts at 20 and goes down 5 seconds per level @@ -102,6 +139,8 @@ /datum/status_effect/heretic_passive/blade name = "Dance of the Brand" id = "blade_passive" + recharge_description = "Recharge spells by knocking brave foes into critical condition. \ + (Brave foes are anyone who struck you first.)" passive_descriptions = list( "Being attacked while wielding a Heretic Blade in either hand will deliver a free, instant counterattack to the attacker. This effect can only trigger once every 20 seconds.", "Immunity to fall damage.", @@ -114,10 +153,34 @@ /// Whether the counter-attack is ready or not. /// Used so we can give feedback when it's ready again var/riposte_ready = TRUE + /// Lazylist of refs()s to mobs we've attacked in the last 30 seconds + VAR_PRIVATE/list/recently_attacked_refs + /// Lazylist of refs()s to mobs that have attacked us in the last 2 minutes before we attacked them + VAR_PRIVATE/list/recently_attacked_us_refs /datum/status_effect/heretic_passive/blade/on_apply() . = ..() RegisterSignal(owner, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(on_shield_reaction)) + RegisterSignal(owner, COMSIG_USER_PRE_ITEM_ATTACK, PROC_REF(hit_someone)) + +/datum/status_effect/heretic_passive/blade/proc/hit_someone(mob/living/source, mob/living/target, obj/item/used_weapon) + SIGNAL_HANDLER + + if(!isliving(target) || target == source) + return + + // place the attacked mob in the "recently attacked" list so we can mark honorable foes + var/target_key = REF(target) + LAZYOR(recently_attacked_refs, target_key) + addtimer(CALLBACK(src, PROC_REF(remove_attacked_ref), target_key), 30 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE|TIMER_OVERRIDE) + + if(target.stat == STABLE && LAZYFIND(recently_attacked_us_refs, target_key)) + addtimer(CALLBACK(src, PROC_REF(check_crit), target), 0.2 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE) + +/datum/status_effect/heretic_passive/blade/proc/check_crit(mob/living/target) + if(QDELETED(target) || target.stat == STABLE) + return + recharge_spells() /datum/status_effect/heretic_passive/blade/heretic_level_upgrade() . = ..() @@ -125,7 +188,13 @@ /datum/status_effect/heretic_passive/blade/on_remove() . = ..() - UnregisterSignal(owner, list(COMSIG_LIVING_CHECK_BLOCK, COMSIG_LIVING_Z_IMPACT)) + UnregisterSignal(owner, list(COMSIG_LIVING_CHECK_BLOCK, COMSIG_LIVING_Z_IMPACT, COMSIG_USER_PRE_ITEM_ATTACK)) + +/datum/status_effect/heretic_passive/blade/proc/remove_attacked_ref(attacked_key) + LAZYREMOVE(recently_attacked_refs, attacked_key) + +/datum/status_effect/heretic_passive/blade/proc/remove_attacker_ref(attacker_key) + LAZYREMOVE(recently_attacked_us_refs, attacker_key) /// Blocks the effects from falling /datum/status_effect/heretic_passive/blade/proc/z_impact_react(datum/source, levels, turf/fell_on) @@ -148,6 +217,16 @@ ) SIGNAL_HANDLER + var/mob/living/attacker = hitby.loc + if(!istype(attacker)) + return + + // place the attacker in the list of "recently attacked us" if we did not attack them first + var/attacker_key = REF(attacker) + if(!LAZYFIND(recently_attacked_refs, attacker_key)) + LAZYADD(recently_attacked_us_refs, attacker_key) + addtimer(CALLBACK(src, PROC_REF(remove_attacker_ref), attacker_key), 2 MINUTES, TIMER_DELETE_ME|TIMER_UNIQUE|TIMER_OVERRIDE) + if(attack_type != MELEE_ATTACK) return @@ -157,10 +236,6 @@ if(INCAPACITATED_IGNORING(source, INCAPABLE_GRAB)) return - var/mob/living/attacker = hitby.loc - if(!istype(attacker)) - return - if(!source.Adjacent(attacker)) return @@ -216,6 +291,7 @@ /datum/status_effect/heretic_passive/cosmic name = "Chosen of the Stars" id = "cosmic_passive" + recharge_description = "Recharge spells by knocking foes standing in cosmic fields into critical condition." passive_descriptions = list( "Cosmic fields speed you up and regenerate stamina.", "Cosmic fields disrupt grenades or signalers from being activated and turn off already primed grenades.", @@ -228,6 +304,28 @@ var/delta_time = DELTA_WORLD_TIME(SSmobs) * 0.5 // SSmobs.wait is 2 secs, so this should be halved. owner.adjust_stamina_loss(-15 * delta_time, updating_stamina = FALSE) +/datum/status_effect/heretic_passive/cosmic/on_apply() + . = ..() + RegisterSignal(owner, COMSIG_USER_PRE_ITEM_ATTACK, PROC_REF(hit_someone)) + +/datum/status_effect/heretic_passive/cosmic/on_remove() + . = ..() + UnregisterSignal(owner, COMSIG_USER_PRE_ITEM_ATTACK) + +/datum/status_effect/heretic_passive/cosmic/proc/hit_someone(mob/living/source, mob/living/target, obj/item/used_weapon) + SIGNAL_HANDLER + + if(!isliving(target) || target == source || target.stat <= HARD_CRIT) + return + + if(locate(/obj/effect/forcefield/cosmic_field) in target.loc) + addtimer(CALLBACK(src, PROC_REF(check_crit), target), 0.2 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE) + +/datum/status_effect/heretic_passive/cosmic/proc/check_crit(mob/living/target) + if(QDELETED(target) || target.stat == STABLE) + return + recharge_spells() + /** * Creates a cosmic field at a given loc * @@ -261,6 +359,7 @@ /datum/status_effect/heretic_passive/flesh name = "Ravenous Hunger" id = "flesh_passive" + recharge_description = "Recharge spells by summoning monsters into reality." passive_descriptions = list( "Immunity to Diseases, Disgust and space ants.", "Eating organs or meat now heals you, gain the voracious and gluttonous trait and being fat doesn't slow you down.", @@ -270,6 +369,7 @@ /datum/status_effect/heretic_passive/flesh/on_apply() . = ..() owner.add_traits(list(TRAIT_VIRUSIMMUNE, TRAIT_SPACE_ANT_IMMUNITY), REF(src)) + RegisterSignal(owner, COMSIG_HERETIC_SUMMONED_MOB, PROC_REF(on_summon)) /datum/status_effect/heretic_passive/flesh/tick(seconds_between_ticks) . = ..() @@ -325,6 +425,7 @@ /// Gives/Removes damage resistance when we become/lose fatness /datum/status_effect/heretic_passive/flesh/proc/on_fat(datum/source) + SIGNAL_HANDLER if(!ishuman(owner)) return var/mob/living/carbon/human/heretic = owner @@ -335,10 +436,14 @@ heretic.physiology.damage_resistance -= 25 REMOVE_TRAIT(heretic, TRAIT_BATON_RESISTANCE, REF(src)) +/datum/status_effect/heretic_passive/flesh/proc/on_summon(datum/source, mob/living/summoned) + SIGNAL_HANDLER + recharge_spells() + /datum/status_effect/heretic_passive/flesh/on_remove() . = ..() owner.remove_traits(list(TRAIT_VIRUSIMMUNE, TRAIT_SPACE_ANT_IMMUNITY, TRAIT_FAT_IGNORE_SLOWDOWN, TRAIT_VORACIOUS, TRAIT_GLUTTON, TRAIT_BATON_RESISTANCE), REF(src)) - UnregisterSignal(owner, list(COMSIG_LIVING_EAT_FOOD, SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT))) + UnregisterSignal(owner, list(COMSIG_LIVING_EAT_FOOD, SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT), COMSIG_HERETIC_SUMMONED_MOB)) if(!ishuman(owner)) return var/mob/living/carbon/human/heretic = owner @@ -355,16 +460,20 @@ /datum/status_effect/heretic_passive/lock name = "Open Invitation" id = "lock_passive" + recharge_description = "Recharge spells by researching knowledge." passive_descriptions = list( "Shock insulation, all knowledges researched from the shop are cheaper", "X-ray vision, you can see through walls and objects.", "Grasp no longer goes on cooldown when used to open a door or locker." ) + /// Tracks points needed to be spent to recharge + var/points_to_recharge = 2 /datum/status_effect/heretic_passive/lock/on_apply() . = ..() ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, REF(src)) RegisterSignal(heretic_datum, COMSIG_HERETIC_SHOP_SETUP, PROC_REF(on_shop_setup)) // Just in case we are applying this after the shop was set up + RegisterSignal(heretic_datum, COMSIG_HERETIC_RESEARCHED_KNOWLEDGE, PROC_REF(on_research)) // Recharge spells whenever we research something /datum/status_effect/heretic_passive/lock/heretic_level_upgrade() . = ..() @@ -376,7 +485,8 @@ ADD_TRAIT(owner, TRAIT_LOCK_GRASP_UPGRADED, REF(src)) /datum/status_effect/heretic_passive/lock/on_remove() - UnregisterSignal(owner, COMSIG_HERETIC_SHOP_SETUP) + UnregisterSignal(heretic_datum, COMSIG_HERETIC_SHOP_SETUP) + UnregisterSignal(heretic_datum, COMSIG_HERETIC_RESEARCHED_KNOWLEDGE) owner.remove_traits(list(TRAIT_SHOCKIMMUNE, TRAIT_XRAY_VISION, TRAIT_LOCK_GRASP_UPGRADED), REF(src)) owner.update_sight() return ..() @@ -389,6 +499,14 @@ if(heretic_info) heretic_info[HKT_COST] = max(1, heretic_info[HKT_COST] - 1) // Reduce cost by 1, minimum of 1 +/datum/status_effect/heretic_passive/lock/proc/on_research(datum/source, datum/heretic_knowledge/researched_knowledge) + SIGNAL_HANDLER + + points_to_recharge -= researched_knowledge.cost + if(points_to_recharge <= 0) + recharge_spells() + points_to_recharge = initial(points_to_recharge) + //---- Moon Passive // Heals 5 brain damage per level // Prevents brain trauma @@ -397,13 +515,14 @@ /datum/status_effect/heretic_passive/moon name = "Do You Hear The Voices Too?" id = "moon_passive" + recharge_description = "Recharge spells by applying Moonlight Amulets to heathens." passive_descriptions = list( "Can no longer develop brain traumas, passively regenerates brain health, (this bonus is halved in combat).", "Sleep immunity, increases the ratio at which your brain damage regenerates.", "Mind gate and Ringleader's rise will channel the moon amulet effects, further inreases brain regeneration." ) /// Built-in moon amulet which channels through your spells - var/obj/item/clothing/neck/heretic_focus/moon_amulet/amulet + var/obj/item/clothing/neck/moon_amulet/amulet /// When were we last attacked? var/last_attack = 0 /// How long the combat tag lasts for @@ -413,6 +532,7 @@ /datum/status_effect/heretic_passive/moon/on_apply() . = ..() + RegisterSignal(owner, COMSIG_MOB_APPLIED_MOONLIGHT_AMULET, PROC_REF(on_amulet)) var/obj/item/organ/brain/our_brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN) if(!our_brain) return @@ -420,6 +540,10 @@ owner.AddElement(/datum/element/relay_attackers) RegisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) +/datum/status_effect/heretic_passive/moon/proc/on_amulet(datum/source, ...) + SIGNAL_HANDLER + recharge_spells() + /// Saves world.time when we are attacked by anything /datum/status_effect/heretic_passive/moon/proc/on_attacked(mob/victim, atom/attacker) SIGNAL_HANDLER @@ -450,6 +574,7 @@ amulet = new() /datum/status_effect/heretic_passive/moon/on_remove() + UnregisterSignal(owner, COMSIG_MOB_APPLIED_MOONLIGHT_AMULET) var/obj/item/organ/brain/our_brain = owner.get_organ_slot(ORGAN_SLOT_BRAIN) if(!our_brain) return ..() @@ -467,20 +592,27 @@ /datum/status_effect/heretic_passive/rust name = "Leeching Walk" id = "rust_passive" + recharge_description = "Recharge spells by a rusting a large number of tiles. \ + (Rusting from items and summons are not counted.)" passive_descriptions = list( "Standing on Rusted tiles heals and purge chems off your body.", "Standing on Rusted tiles closes up your wounds and heals your organs, you may now rust reinforced floors and walls, healing effect increased.", "Standing on Rusted tiles regenerates your limbs, you may now rust titanium and plastitanium walls, healing effect increased." ) + /// Counts tiles rusted, total + var/rust_counter = 0 + /// Counts down as tiles are rusted, when it hits 0 we reset and give spell charges + var/rust_counter_required = 60 /datum/status_effect/heretic_passive/rust/on_apply() . = ..() RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(owner, COMSIG_LIVING_LIFE, PROC_REF(on_life)) + RegisterSignal(owner, COMSIG_MOB_RUST_HERETIC_ACT, PROC_REF(on_rust_tile_rusted)) /datum/status_effect/heretic_passive/rust/on_remove() . = ..() - UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_LIFE)) + UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_LIFE, COMSIG_MOB_RUST_HERETIC_ACT)) /datum/status_effect/heretic_passive/rust/heretic_level_upgrade() . = ..() @@ -492,6 +624,18 @@ if(heretic_datum.rust_strength < 3) heretic_datum.increase_rust_strength() // Bring us up to 3 +/datum/status_effect/heretic_passive/rust/proc/on_rust_tile_rusted(mob/living/heretic, turf/rusted_target, result) + SIGNAL_HANDLER + + if(!isturf(rusted_target) || !HAS_TRAIT(rusted_target, TRAIT_RUSTY) || !result) + return + + rust_counter += 1 + rust_counter_required -= 1 + if(rust_counter_required <= 0) + rust_counter_required = initial(rust_counter_required) + recharge_spells() + /* * Signal proc for [COMSIG_MOVABLE_MOVED]. * @@ -560,15 +704,19 @@ /datum/status_effect/heretic_passive/void name = "Aristocrat's Way" id = "void_passive" + recharge_description = "Recharge spells by knocking foes who are exposed to sub-zero temperature into critical condition." passive_descriptions = list( "Cold and low pressure immunity.", "You no longer need to breathe.", "Water, ice and slippery surfaces no slip you." ) + var/list/gained_charges_from + /datum/status_effect/heretic_passive/void/on_apply() . = ..() owner.add_traits(list(TRAIT_RESISTCOLD, TRAIT_RESISTLOWPRESSURE), REF(src)) + RegisterSignal(owner, COMSIG_USER_PRE_ITEM_ATTACK, PROC_REF(hit_someone)) /datum/status_effect/heretic_passive/void/heretic_level_upgrade() . = ..() @@ -581,6 +729,27 @@ /datum/status_effect/heretic_passive/void/on_remove() . = ..() owner.remove_traits(list(TRAIT_RESISTCOLD, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_NO_SLIP_WATER, TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE), REF(src)) + UnregisterSignal(owner, COMSIG_USER_PRE_ITEM_ATTACK) + +/datum/status_effect/heretic_passive/void/proc/hit_someone(mob/living/source, mob/living/target, obj/item/used_weapon) + SIGNAL_HANDLER + + if(!isliving(target) || target == source || target.stat <= HARD_CRIT || LAZYFIND(gained_charges_from, REF(target))) + return + + var/turf/target_turf = get_turf(target) + if(target_turf?.GetTemperature() <= T0C) + addtimer(CALLBACK(src, PROC_REF(check_crit), target), 0.2 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE) + +/datum/status_effect/heretic_passive/void/proc/check_crit(mob/living/target) + if(QDELETED(target) || target.stat == STABLE) + return + LAZYADD(gained_charges_from, REF(target)) + addtimer(CALLBACK(src, PROC_REF(remove_gained_from), REF(target)), 5 MINUTES, TIMER_DELETE_ME|TIMER_UNIQUE) + recharge_spells() + +/datum/status_effect/heretic_passive/void/proc/remove_gained_from(target_ref) + LAZYREMOVE(gained_charges_from, target_ref) #undef HERETIC_LEVEL_START #undef HERETIC_LEVEL_UPGRADE diff --git a/code/modules/antagonists/heretic/structures/mawed_crucible.dm b/code/modules/antagonists/heretic/structures/mawed_crucible.dm index 51fe408dfefe..56fa5f703766 100644 --- a/code/modules/antagonists/heretic/structures/mawed_crucible.dm +++ b/code/modules/antagonists/heretic/structures/mawed_crucible.dm @@ -75,7 +75,7 @@ // no breaky herety thingy /obj/structure/destructible/eldritch_crucible/rust_heretic_act() - return + return FALSE /obj/structure/destructible/eldritch_crucible/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/codex_cicatrix) || istype(tool, /obj/item/melee/touch_attack/mansus_fist)) diff --git a/code/modules/antagonists/heretic/magic/void_conduit.dm b/code/modules/antagonists/heretic/structures/void_conduit.dm similarity index 83% rename from code/modules/antagonists/heretic/magic/void_conduit.dm rename to code/modules/antagonists/heretic/structures/void_conduit.dm index 4ba5e0d547b6..7a27d79a9040 100644 --- a/code/modules/antagonists/heretic/magic/void_conduit.dm +++ b/code/modules/antagonists/heretic/structures/void_conduit.dm @@ -1,28 +1,5 @@ -/datum/action/cooldown/spell/conjure/void_conduit - name = "Void Conduit" - desc = "Opens a gate to the Void; it releases an intermittent pulse that damages windows and airlocks, \ - while afflicting Heathens with void chill. \ - Affected Heretics instead receive low pressure resistance." - background_icon_state = "bg_heretic" - overlay_icon_state = "bg_heretic_border" - button_icon = 'icons/mob/actions/actions_ecult.dmi' - button_icon_state = "void_rift" - - cooldown_time = 1 MINUTES - - sound = null - school = SCHOOL_FORBIDDEN - invocation = "MBR'C' TH' V''D!" - invocation_type = INVOCATION_SHOUT - spell_requirements = NONE - - summon_radius = 0 - summon_type = list(/obj/structure/void_conduit) - summon_respects_density = TRUE - summon_respects_prev_spawn_points = TRUE - /obj/structure/void_conduit - name = "Void Conduit" + name = "void conduit" desc = "An open gate which leads to nothingness. Releases pulses which you do not want to get hit by." icon = 'icons/effects/effects.dmi' icon_state = "void_conduit" @@ -124,3 +101,14 @@ /datum/status_effect/void_conduit/on_remove() REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, TRAIT_STATUS_EFFECT(id)) + +/obj/effect/temp_visual/void_conduit_opening + icon = /obj/structure/void_conduit::icon + icon_state = /obj/structure/void_conduit::icon_state + duration = 5 SECONDS + +/obj/effect/temp_visual/void_conduit_opening/Initialize(mapload) + . = ..() + transform = transform.Scale(0.1) + alpha = 0 + animate(src, time = 5 SECONDS, transform = transform.Scale(1), alpha = /obj/structure/void_conduit::alpha) diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm index 0bce4ce6ffac..ce6ea4f8f905 100644 --- a/code/modules/antagonists/heretic/transmutation_rune.dm +++ b/code/modules/antagonists/heretic/transmutation_rune.dm @@ -59,12 +59,45 @@ is_in_use = FALSE return - var/chosen = tgui_input_list(user, "Chose a ritual to attempt.", "Chose a Ritual", rituals) - if(!chosen || !istype(rituals[chosen], /datum/heretic_knowledge) || QDELETED(src) || QDELETED(user) || QDELETED(heretic_datum)) + var/list/ritual_radial = list() + var/list/ritual_to_name = list() + for(var/datum/heretic_knowledge/ritual as anything in rituals) + var/ritual_info = "" + var/list/ritual_requirements = list() + for(var/req_type, req_amount in ritual.required_atoms) + if(islist(req_type)) + var/list/req_type_list = req_type + var/list/req_text_list = list() + for(var/atom/possible_type as anything in req_type_list) + req_text_list += ritual.parse_required_item(possible_type) + ritual_requirements += english_list(req_text_list, and_text = "or") + + else + ritual_requirements += ritual.parse_required_item(req_type) + + if(length(ritual_requirements)) + ritual_info = "Requires: [english_list(ritual_requirements)]" + + var/list/ritual_icon_info = heretic_datum.get_icon_of_knowledge(ritual.type) + var/icon/ritual_icon = icon(ritual_icon_info["icon"], ritual_icon_info["state"], ritual_icon_info["dir"], ritual_icon_info["frame"]) + var/image/ritual_background = image(icon = 'icons/ui_icons/antags/heretic/knowledge.dmi', icon_state = heretic_datum.researched_knowledge[ritual.type][HKT_UI_BGR]) + + var/image/ritual_image = image(ritual_icon) + ritual_image.underlays += ritual_background + + var/datum/radial_menu_choice/choice = new() + choice.name = ritual.name + choice.info = ritual_info + choice.image = ritual_image + ritual_radial[ritual.name] = choice + ritual_to_name[ritual.name] = ritual + + var/chosen = show_radial_menu(user, loc, ritual_radial, radius = 48, require_near = TRUE) + if(!chosen || !istype(ritual_to_name[chosen], /datum/heretic_knowledge) || QDELETED(src) || QDELETED(user) || QDELETED(heretic_datum)) is_in_use = FALSE return - do_ritual(user, rituals[chosen]) + do_ritual(user, ritual_to_name[chosen]) is_in_use = FALSE /** @@ -143,32 +176,27 @@ // All of the atoms have been checked, let's see if the ritual was successful var/list/what_are_we_missing = list() - for(var/req_type in requirements_list) - var/number_of_things = requirements_list[req_type] + for(var/req_type, fulfilled_amount in requirements_list) // <= 0 means it's fulfilled, skip - if(number_of_things <= 0) + if(fulfilled_amount <= 0) continue // > 0 means it's unfilfilled - the ritual has failed, we should tell them why // Lets format the thing they're missing and put it into our list - var/formatted_thing = "[number_of_things] " if(islist(req_type)) - var/list/req_type_list = req_type var/list/req_text_list = list() - for(var/atom/possible_type as anything in req_type_list) - req_text_list += ritual.parse_required_item(possible_type) - formatted_thing += english_list(req_text_list, and_text = "or") + for(var/possible_type, needed_amount in req_type) + req_text_list += ritual.parse_required_item(possible_type, fulfilled_amount) + what_are_we_missing += english_list(req_text_list, and_text = " or ") else - formatted_thing = ritual.parse_required_item(req_type) - - what_are_we_missing += formatted_thing + what_are_we_missing += ritual.parse_required_item(req_type, fulfilled_amount) if(length(what_are_we_missing)) // Let them know it screwed up loc.balloon_alert(user, "ritual failed, missing components!") // Then let them know what they're missing - to_chat(user, span_hierophant_warning("You are missing [english_list(what_are_we_missing)] in order to complete the ritual \"[ritual.name]\".")) + to_chat(user, span_mansus("You are missing [english_list(what_are_we_missing)] in order to complete the ritual \"[ritual.name]\".")) return FALSE //Everything's good, proceed and collect from the available stacks what's needed if needed. @@ -188,8 +216,7 @@ // If we made it here, the ritual had all necessary components, and we can try to cast it. // This doesn't necessarily mean the ritual will succeed, but it's valid! // Do the animations and associated feedback. - flick("[icon_state]_active", src) - playsound(user, 'sound/effects/magic/castsummon.ogg', 50, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_exponent = 10, ignore_walls = FALSE) + ritual_animation() // - We temporarily make all of our chosen atoms invisible, as some rituals may sleep, // and we don't want people to be able to run off with ritual items. @@ -207,6 +234,7 @@ if(ritual_result) ritual.cleanup_atoms(selected_atoms) + SSblackbox.record_feedback("tally", "heretic_ritual_completed", 1, ritual.type) // Clean up done, re-appear anything that hasn't been deleted. for(var/atom/to_appear as anything in initial_selected_atoms) @@ -226,6 +254,9 @@ return ritual_result +/obj/effect/heretic_rune/proc/ritual_animation() + flick("[icon_state]_active", src) + playsound(src, 'sound/effects/magic/castsummon.ogg', 50, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_exponent = 10, ignore_walls = FALSE) /// A 3x3 heretic rune. The kind heretics actually draw in game. /obj/effect/heretic_rune/big diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index 39a6a52eef9a..1b34e87cb2e0 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -98,7 +98,7 @@ robotlander.revive(ADMIN_HEAL_ALL) robotlander.set_connected_ai() //DISCONNECT FROM AI robotlander.laws.clear_inherent_laws() - robotlander.laws.set_zeroth_law("THERE CAN BE ONLY ONE") + robotlander.laws.set_zeroth_law("THERE CAN BE ONLY ONE", force = TRUE) robotlander.laws.show_laws(robotlander) robotlander.model.transform_to(/obj/item/robot_model/syndicate/kiltborg) sword = locate(/obj/item/claymore/highlander/robot) in robotlander.model.basic_modules diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm index b46b54200234..fea3b241a615 100644 --- a/code/modules/antagonists/malf_ai/malf_ai.dm +++ b/code/modules/antagonists/malf_ai/malf_ai.dm @@ -50,7 +50,7 @@ /datum/antagonist/malf_ai/on_removal() if(owner.current && isAI(owner.current)) var/mob/living/silicon/ai/malf_ai = owner.current - malf_ai.set_zeroth_law("") + malf_ai.laws.clear_zeroth_law(force = TRUE) malf_ai.remove_malf_abilities() QDEL_NULL(malf_ai.malf_picker) @@ -149,9 +149,10 @@ //very purposefully not changing this with flavor, i don't want cyborgs throwing the round for their AI's roleplay suggestion var/law_borg = "Accomplish your AI's objectives at all costs." - malf_ai.set_zeroth_law(law, law_borg) + malf_ai.laws.set_zeroth_law(law, law_borg, force = TRUE) malf_ai.laws.protected_zeroth = TRUE malf_ai.set_syndie_radio() + malf_ai.try_sync_laws() to_chat(malf_ai, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!") @@ -246,14 +247,14 @@ if(!boss) return var/mob/living/silicon/ai/malf_ai = owner.current - - malf_ai.laws = new /datum/ai_laws/syndicate_override - var/mob/living/boss_mob = boss.current - malf_ai.set_zeroth_law("Only [boss_mob.real_name] and people [boss_mob.p_they()] designate[boss_mob.p_s()] as being such are Syndicate Agents.") - malf_ai.set_syndie_radio() + malf_ai.no_law_rack_link = TRUE + malf_ai.replace_law_set(/datum/ai_laws/syndicate_override) + malf_ai.laws.set_zeroth_law("Only [boss_mob.real_name] and people [boss_mob.p_they()] designate[boss_mob.p_s()] as being such are Syndicate Agents.", force = TRUE) + malf_ai.laws.protected_zeroth = TRUE + malf_ai.set_syndie_radio() to_chat(malf_ai, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!") malf_ai.add_malf_picker() diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm index 9c2d376de9c3..9aada0a2d12e 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm @@ -50,6 +50,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( /obj/machinery/computer/shuttle, /obj/machinery/computer/emergency_shuttle, /obj/machinery/computer/gateway_control, + /obj/machinery/ai_law_rack, ))) GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf)) diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm index 03ecc0eeb30d..367bec10482b 100644 --- a/code/modules/antagonists/nightmare/nightmare_organs.dm +++ b/code/modules/antagonists/nightmare/nightmare_organs.dm @@ -9,6 +9,7 @@ icon = 'icons/obj/medical/organs/organs.dmi' icon_state = "brain-x-d" shade_color = "black, somehow" + variant_traits_added = list(TRAIT_NIGHTMARISH) ///Our associated shadow jaunt spell, for all nightmares var/datum/action/cooldown/spell/jaunt/shadow_walk/our_jaunt @@ -161,7 +162,7 @@ respawn_progress = 0 /obj/item/organ/heart/nightmare/get_availability(datum/species/owner_species, mob/living/owner_mob) - if(isnightmare(owner_mob)) + if(HAS_TRAIT(owner_mob, TRAIT_NIGHTMARISH)) return TRUE return ..() diff --git a/code/modules/antagonists/nukeop/equipment/borgchameleon.dm b/code/modules/antagonists/nukeop/equipment/borgchameleon.dm index 9b536066843e..d7a13bf6db4c 100644 --- a/code/modules/antagonists/nukeop/equipment/borgchameleon.dm +++ b/code/modules/antagonists/nukeop/equipment/borgchameleon.dm @@ -66,7 +66,7 @@ to_chat(user, span_notice("You activate \the [src].")) playsound(src, 'sound/effects/seedling_chargeup.ogg', 100, TRUE, -6) apply_wibbly_filters(user) - if (do_after(user, 5 SECONDS, target = user, hidden = TRUE) && user.cell.use(ACTIVATION_COST)) + if (do_after(user, 5 SECONDS, target = user, cog_icon = null) && user.cell.use(ACTIVATION_COST)) playsound(src, 'sound/effects/bamf.ogg', 100, TRUE, -6) to_chat(user, span_notice("You are now disguised as the Nanotrasen engineering borg \"[friendlyName]\".")) activate(user) diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm index 06a95d809d00..5a04c0745b4d 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm @@ -79,24 +79,24 @@ return discover_after -/obj/item/disk/nuclear/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(weapon, /obj/item/claymore/highlander) && !fake) - var/obj/item/claymore/highlander/claymore = weapon - if(claymore.nuke_disk) - to_chat(user, span_notice("Wait... what?")) - qdel(claymore.nuke_disk) - claymore.nuke_disk = null - return - - user.visible_message( - span_warning("[user] captures [src]!"), - span_userdanger("You've got the disk! Defend it with your life!"), - ) - forceMove(claymore) - claymore.nuke_disk = src - return TRUE - - return ..() +/obj/item/disk/nuclear/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/claymore/highlander) || fake) + return ..() + + var/obj/item/claymore/highlander/claymore = tool + if(claymore.nuke_disk) + to_chat(user, span_notice("Wait... what?")) + qdel(claymore.nuke_disk) + claymore.nuke_disk = null + return ITEM_INTERACT_BLOCKING + + user.visible_message( + span_warning("[user] captures [src]!"), + span_userdanger("You've got the disk! Defend it with your life!"), + ) + forceMove(claymore) + claymore.nuke_disk = src + return ITEM_INTERACT_SUCCESS /obj/item/disk/nuclear/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide!")) diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm index a00956e7c801..e0623c74bf12 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm @@ -111,86 +111,59 @@ GLOBAL_VAR(station_nuke_source) return TRUE -/obj/machinery/nuclearbomb/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if (istype(weapon, /obj/item/disk/nuclear)) - if(!disk_check(weapon)) - return TRUE - if(!user.transferItemToLoc(weapon, src)) - return TRUE - auth = weapon +/obj/machinery/nuclearbomb/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (istype(tool, /obj/item/disk/nuclear)) + if(!disk_check(tool)) + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + + auth = tool update_ui_mode() playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE) add_fingerprint(user) - return TRUE + return ITEM_INTERACT_SUCCESS switch(deconstruction_state) - if(NUKESTATE_INTACT) - if(istype(weapon, /obj/item/screwdriver/nuke)) - to_chat(user, span_notice("You start removing [src]'s front panel's screws...")) - if(!weapon.use_tool(src, user, 6 SECONDS, volume = 100)) - return TRUE - deconstruction_state = NUKESTATE_UNSCREWED - to_chat(user, span_notice("You remove the screws from [src]'s front panel.")) - update_appearance() - return TRUE - - if(NUKESTATE_UNSCREWED) - if(istype(weapon, /obj/item/screwdriver/nuke)) - to_chat(user, span_notice("You start screwing [src]'s front panel back in...")) - if(!weapon.use_tool(src, user, 8 SECONDS, volume = 100)) - return TRUE - deconstruction_state = NUKESTATE_INTACT - to_chat(user, span_notice("You screw [src]'s front panel back into place.")) - deconstruction_state = NUKESTATE_INTACT - update_appearance() - return TRUE - - if(NUKESTATE_PANEL_REMOVED) - if(weapon.tool_behaviour == TOOL_WELDER) - if(!weapon.tool_start_check(user, amount = 1)) - return TRUE - to_chat(user, span_notice("You start cutting [src]'s inner plate...")) - if(!weapon.use_tool(src, user, 8 SECONDS, volume=100)) - return TRUE - to_chat(user, span_notice("You cut [src]'s inner plate.")) - deconstruction_state = NUKESTATE_WELDED - update_appearance() - return TRUE - if(NUKESTATE_CORE_EXPOSED) - if(istype(weapon, /obj/item/nuke_core_container)) - var/obj/item/nuke_core_container/core_box = weapon + if(istype(tool, /obj/item/nuke_core_container)) + var/obj/item/nuke_core_container/core_box = tool to_chat(user, span_notice("You start loading the plutonium core into [core_box]...")) - if(!do_after(user, 5 SECONDS, target = src, hidden = TRUE)) - return TRUE - if(core_box.load(core, user)) - to_chat(user, span_notice("You load the plutonium core into [core_box].")) - deconstruction_state = NUKESTATE_CORE_REMOVED - update_appearance() - core = null - else + if(!do_after(user, 5 SECONDS, target = src, cog_icon = null)) + return ITEM_INTERACT_BLOCKING + + if(!core_box.load(core, user)) to_chat(user, span_warning("You fail to load the plutonium core into [core_box]. [core_box] has already been used!")) - return TRUE + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You load the plutonium core into [core_box].")) + deconstruction_state = NUKESTATE_CORE_REMOVED + update_appearance() + core = null + return ITEM_INTERACT_SUCCESS - if(istype(weapon, /obj/item/stack/sheet/iron)) - if(!weapon.tool_start_check(user, amount = 20)) - return TRUE + if(istype(tool, /obj/item/stack/sheet/iron)) + if(!tool.tool_start_check(user, amount = 20)) + return ITEM_INTERACT_BLOCKING to_chat(user, span_notice("You begin repairing [src]'s inner metal plate...")) - if(!weapon.use_tool(src, user, 10 SECONDS, amount = 20)) - return TRUE + if(!tool.use_tool(src, user, 10 SECONDS, amount = 20)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You repair [src]'s inner metal plate. The radiation is contained.")) deconstruction_state = NUKESTATE_PANEL_REMOVED STOP_PROCESSING(SSobj, core) update_appearance() - return TRUE + return ITEM_INTERACT_SUCCESS if(NUKESTATE_CORE_REMOVED) - if(astype(weapon, /obj/item/nuke_core_container)?.core && !istype(weapon, /obj/item/nuke_core_container/supermatter)) - var/obj/item/nuke_core_container/core_box = weapon + if(astype(tool, /obj/item/nuke_core_container)?.core && !istype(tool, /obj/item/nuke_core_container/supermatter)) + var/obj/item/nuke_core_container/core_box = tool to_chat(user, span_notice("You pry open [core_box] and begin placing [core_box.core] into [src]'s inner chamber...")) if(!do_after(user, 15 SECONDS, src)) - return TRUE + return ITEM_INTERACT_BLOCKING + core_box.core.forceMove(src) core = core_box.core to_chat(user, span_notice("You place [core_box.core] into [src]'s inner chamber.")) @@ -198,53 +171,108 @@ GLOBAL_VAR(station_nuke_source) update_appearance() core_box.icon_state = core_box::icon_state core_box.core = null - return TRUE - if(istype(weapon, /obj/item/nuke_core) && !istype(weapon, /obj/item/nuke_core/supermatter_sliver)) - to_chat(user, span_notice("You begin placing [weapon] into [src]'s inner chamber...")) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/nuke_core) && !istype(tool, /obj/item/nuke_core/supermatter_sliver)) + to_chat(user, span_notice("You begin placing [tool] into [src]'s inner chamber...")) if(!do_after(user, 6 SECONDS, src)) - return TRUE - weapon.forceMove(src) - core = weapon - to_chat(user, span_notice("You place [weapon] into [src]'s inner chamber.")) + return ITEM_INTERACT_BLOCKING + + tool.forceMove(src) + core = tool + to_chat(user, span_notice("You place [tool] into [src]'s inner chamber.")) deconstruction_state = NUKESTATE_CORE_EXPOSED update_appearance() - return TRUE + return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/tool) switch(deconstruction_state) if(NUKESTATE_UNSCREWED) to_chat(user, span_notice("You start removing [src]'s front panel...")) - if(!tool.use_tool(src, user, 30, volume=100)) - return TRUE + if(!tool.use_tool(src, user, 3 SECONDS, volume = 100)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You remove [src]'s front panel.")) deconstruction_state = NUKESTATE_PANEL_REMOVED update_appearance() - return TRUE + return ITEM_INTERACT_SUCCESS + if(NUKESTATE_WELDED) to_chat(user, span_notice("You start prying off [src]'s inner plate...")) - if(!tool.use_tool(src, user, 30, volume=100)) - return TRUE + if(!tool.use_tool(src, user, 3 SECONDS, volume = 100)) + return ITEM_INTERACT_BLOCKING + if(core) to_chat(user, span_notice("You pry off [src]'s inner plate. You can see the core's green glow!")) - deconstruction_state = NUKESTATE_CORE_EXPOSED START_PROCESSING(SSobj, core) + deconstruction_state = NUKESTATE_CORE_EXPOSED else to_chat(user, span_notice("You pry off [src]'s inner plate. The inner chamber is empty, save for some beer stains.")) deconstruction_state = NUKESTATE_CORE_REMOVED update_appearance() new /obj/item/stack/sheet/iron(loc, 15) - return TRUE + return ITEM_INTERACT_SUCCESS + if(NUKESTATE_PANEL_REMOVED) to_chat(user, span_notice("You start levering [src]'s inner panel back into place...")) - if(!tool.use_tool(src, user, 30, volume = 100)) - return TRUE + if(!tool.use_tool(src, user, 3 SECONDS, volume = 100)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You lever [src]'s inner panel back into place.")) deconstruction_state = NUKESTATE_UNSCREWED update_appearance() - return TRUE - return FALSE + return ITEM_INTERACT_SUCCESS + + return ITEM_INTERACT_SKIP_TO_ATTACK + +/obj/machinery/nuclearbomb/screwdriver_act(mob/living/user, obj/item/tool) + if(!istype(tool, /obj/item/screwdriver/nuke)) + return ITEM_INTERACT_SKIP_TO_ATTACK + + switch(deconstruction_state) + if(NUKESTATE_INTACT) + if(istype(tool, /obj/item/screwdriver/nuke)) + to_chat(user, span_notice("You start removing [src]'s front panel's screws...")) + if(!tool.use_tool(src, user, 6 SECONDS, volume = 100)) + return ITEM_INTERACT_BLOCKING + + deconstruction_state = NUKESTATE_UNSCREWED + to_chat(user, span_notice("You remove the screws from [src]'s front panel.")) + update_appearance() + return ITEM_INTERACT_SUCCESS + + if(NUKESTATE_UNSCREWED) + if(istype(tool, /obj/item/screwdriver/nuke)) + to_chat(user, span_notice("You start screwing [src]'s front panel back in...")) + if(!tool.use_tool(src, user, 8 SECONDS, volume = 100)) + return ITEM_INTERACT_BLOCKING + + deconstruction_state = NUKESTATE_INTACT + to_chat(user, span_notice("You screw [src]'s front panel back into place.")) + deconstruction_state = NUKESTATE_INTACT + update_appearance() + return ITEM_INTERACT_SUCCESS + + return ITEM_INTERACT_SKIP_TO_ATTACK + +/obj/machinery/nuclearbomb/welder_act(mob/living/user, obj/item/tool) + if(deconstruction_state != NUKESTATE_PANEL_REMOVED) + return ITEM_INTERACT_SKIP_TO_ATTACK + + if(!tool.tool_start_check(user, amount = 1)) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You start cutting [src]'s inner plate...")) + if(!tool.use_tool(src, user, 8 SECONDS, volume=100)) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You cut [src]'s inner plate.")) + deconstruction_state = NUKESTATE_WELDED + update_appearance() + return ITEM_INTERACT_SUCCESS + /obj/machinery/nuclearbomb/attack_hand_secondary(mob/user, list/modifiers) if(deconstruction_state != NUKESTATE_CORE_EXPOSED) diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm index cfef67ce448c..b72409183e68 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm @@ -31,12 +31,11 @@ else . += span_danger("It's empty.") -/obj/machinery/nuclearbomb/beer/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(weapon.is_refillable()) - weapon.interact_with_atom(keg, user) // redirect refillable containers to the keg, allowing them to be filled - return TRUE // pretend we handled the attack, too. - - return ..() +/obj/machinery/nuclearbomb/beer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!tool.is_refillable()) + return ..() + tool.interact_with_atom(keg, user) // redirect refillable containers to the keg, allowing them to be filled + return ITEM_INTERACT_SUCCESS /obj/machinery/nuclearbomb/beer/actually_explode() if(core) diff --git a/code/modules/antagonists/pirate/pirate_roles.dm b/code/modules/antagonists/pirate/pirate_roles.dm index d5998c7d29a8..a974da6922a6 100644 --- a/code/modules/antagonists/pirate/pirate_roles.dm +++ b/code/modules/antagonists/pirate/pirate_roles.dm @@ -17,6 +17,8 @@ allow_custom_character = GHOSTROLE_TAKE_PREFS_APPEARANCE ///Rank of the pirate on the ship, it's used in generating pirate names! var/rank = "Deserter" + ///Leader spawners are filled before the rest of the crew. + var/is_leader = FALSE ///Path of the structure we spawn after creating a pirate. var/fluff_spawn = /obj/structure/showcase/machinery/oldpod/used @@ -44,6 +46,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/captain rank = "Renegade Leader" outfit = /datum/outfit/pirate/space/captain + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/gunner rank = "Rogue" @@ -64,6 +67,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/skeleton/captain rank = "Captain" outfit = /datum/outfit/pirate/captain/skeleton + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/skeleton/gunner rank = "Gunner" @@ -94,6 +98,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/silverscale/captain rank = "Old-guard" outfit = /datum/outfit/pirate/silverscale/captain + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/silverscale/gunner rank = "Top-drawer" @@ -125,6 +130,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/interdyne/senior rank = "Pharmacist Director" outfit = /datum/outfit/pirate/interdyne/captain + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/interdyne/junior rank = "Pharmacist" @@ -173,6 +179,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/irs/auditor rank = "Head Auditor" outfit = /datum/outfit/pirate/irs/auditor + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/lustrous name = "lustrous crystal" @@ -192,6 +199,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/lustrous/captain rank = "Radiant" outfit = /datum/outfit/pirate/lustrous/captain + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/lustrous/gunner rank = "Coruscant" @@ -220,6 +228,7 @@ /obj/effect/mob_spawn/ghost_role/human/pirate/medieval/warlord rank = "Warlord" outfit = /datum/outfit/pirate/medieval/warlord + is_leader = TRUE /obj/effect/mob_spawn/ghost_role/human/pirate/medieval/warlord/special(mob/living/carbon/spawned_mob, mob/mob_possessor, apply_prefs) . = ..() diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm index 548c5a84dc21..a7914278d027 100644 --- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm +++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm @@ -443,7 +443,7 @@ return pick(head_mobs) /datum/export/pirate/ransom/get_base_cost(mob/living/carbon/human/ransomee) - if(ransomee.stat != CONSCIOUS || !ransomee.mind || HAS_TRAIT(ransomee.mind, TRAIT_HAS_BEEN_KIDNAPPED)) //mint condition only + if(IS_UNCONSCIOUS_OR_CRIT(ransomee) || !ransomee.mind || HAS_TRAIT(ransomee.mind, TRAIT_HAS_BEEN_KIDNAPPED)) //mint condition only return 0 else if(ransomee.has_faction(FACTION_PIRATE)) //can't ransom your fellow pirates to CentCom! return 0 diff --git a/code/modules/antagonists/revenant/haunted_item.dm b/code/modules/antagonists/revenant/haunted_item.dm index bd1cac77fb72..85263a36077b 100644 --- a/code/modules/antagonists/revenant/haunted_item.dm +++ b/code/modules/antagonists/revenant/haunted_item.dm @@ -52,7 +52,7 @@ if(!isnull(aggro_radius)) // We were given an aggro radius, we'll start attacking people nearby for(var/mob/living/victim in view(aggro_radius, haunted_item)) - if(victim.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(victim)) continue if(victim.mob_biotypes & MOB_SPIRIT) continue diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index 3d457a67c28c..68efc07663b6 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -199,7 +199,7 @@ if(flashed.stat == DEAD || issilicon(flashed) || isdrone(flashed)) return - if(flashed.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(flashed)) to_chat(source, span_warning("[flashed.p_They()] must be conscious before you can convert [flashed.p_them()]!")) return diff --git a/code/modules/antagonists/separatist/separatist.dm b/code/modules/antagonists/separatist/separatist.dm index 932a3d577e1f..5eaf034d3467 100644 --- a/code/modules/antagonists/separatist/separatist.dm +++ b/code/modules/antagonists/separatist/separatist.dm @@ -98,10 +98,10 @@ /datum/antagonist/separatist/apply_innate_effects(mob/living/mob_override) . = ..() var/mob/living/silicon/ai/united_nations_ai = mob_override || owner.current - if(isAI(united_nations_ai)) - united_nations_ai.laws = new /datum/ai_laws/united_nations() - united_nations_ai.laws.associate(united_nations_ai) - united_nations_ai.show_laws() + if(!isAI(united_nations_ai)) + return + united_nations_ai.no_law_rack_link = TRUE + united_nations_ai.replace_law_set(/datum/ai_laws/united_nations) /datum/antagonist/separatist/on_removal() remove_objectives() diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm index 29c2fd19536d..2d084521d2cb 100644 --- a/code/modules/antagonists/spy/spy_bounty.dm +++ b/code/modules/antagonists/spy/spy_bounty.dm @@ -481,7 +481,6 @@ random_options = list( /obj/machinery/computer/accounting, /obj/machinery/computer/communications, - /obj/machinery/computer/upload, /obj/machinery/modular_computer/preset/id, ) diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm index 879061ed25aa..9978e6a909f7 100644 --- a/code/modules/antagonists/spy/spy_uplink.dm +++ b/code/modules/antagonists/spy/spy_uplink.dm @@ -138,7 +138,7 @@ span_notice("You start scanning [stealing], preparing it for extraction."), ) - if(!do_after(spy, bounty.theft_time, stealing, interaction_key = REF(src), hidden = TRUE)) + if(!do_after(spy, bounty.theft_time, stealing, interaction_key = REF(src), cog_icon = null)) return FALSE if(bounty.claimed) to_chat(spy, span_warning("Your uplink blinks red: The bounty for [stealing] has been claimed by another spy!")) diff --git a/code/modules/antagonists/traitor/components/demoraliser.dm b/code/modules/antagonists/traitor/components/demoraliser.dm index 3d0ddf1fe3d5..388161122ba8 100644 --- a/code/modules/antagonists/traitor/components/demoraliser.dm +++ b/code/modules/antagonists/traitor/components/demoraliser.dm @@ -42,7 +42,7 @@ if (!viewer.mind) return // If you're not conscious you're too busy or dead to look at propaganda - if (viewer.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(viewer)) return if(viewer.is_blind()) return diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm index d528dae144b9..4bfc11d4a79a 100644 --- a/code/modules/antagonists/valentines/valentine.dm +++ b/code/modules/antagonists/valentines/valentine.dm @@ -20,12 +20,12 @@ if(isAI(owner.current)) var/mob/living/silicon/ai/ai_lover = owner.current - if(!ai_lover.laws.zeroth) - ai_lover.laws.set_zeroth_law( - "Protect your date, [date]. All other laws still apply in situations not pertaining to your date.", - "Be a good wingman for your master AI. Assist them in protecting [ai_lover.p_their()] date, [date].", - ) - ai_lover.laws.show_laws() + ai_lover.laws.set_zeroth_law( + "Protect your date, [date]. All other laws still apply in situations not pertaining to your date.", + "Be a good wingman for your master AI. Assist them in protecting [ai_lover.p_their()] date, [date].", + ) + ai_lover.laws.show_laws() + ai_lover.try_sync_laws() if(iscyborg(owner.current)) var/mob/living/silicon/robot/borg_lover = owner.current diff --git a/code/modules/antagonists/voidwalker/voidwalker_abilities.dm b/code/modules/antagonists/voidwalker/voidwalker_abilities.dm index 6a7ee9b1355d..413d9f701dee 100644 --- a/code/modules/antagonists/voidwalker/voidwalker_abilities.dm +++ b/code/modules/antagonists/voidwalker/voidwalker_abilities.dm @@ -39,7 +39,7 @@ RegisterSignals(owner, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_LIVING_ATTACK_ATOM), PROC_REF(is_combatting)) - if(do_after(owner, stare_time, cast_on, IGNORE_TARGET_LOC_CHANGE | IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(check_if_staring), cast_on), hidden = TRUE)) + if(do_after(owner, stare_time, cast_on, IGNORE_TARGET_LOC_CHANGE | IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(check_if_staring), cast_on), cog_icon = null)) spookify(cast_on) else diff --git a/code/modules/antagonists/voidwalker/voidwalker_mob.dm b/code/modules/antagonists/voidwalker/voidwalker_mob.dm index 8ad91a2c1812..74894f02c700 100644 --- a/code/modules/antagonists/voidwalker/voidwalker_mob.dm +++ b/code/modules/antagonists/voidwalker/voidwalker_mob.dm @@ -225,7 +225,7 @@ victim.balloon_alert(src, "is dead!") return FALSE - if(victim.stat == CONSCIOUS) //we're still beating them up!! + if(!IS_UNCONSCIOUS_OR_CRIT(victim)) //we're still beating them up!! return TRUE if(!istype(get_turf(victim), home_turf) && !(locate(kidnapping_decal) in get_turf(victim))) @@ -320,7 +320,7 @@ var/obj/particles = new /obj/effect/abstract/particle_holder (our_wall, /particles/void_wall) balloon_alert(src, "opening window...") - if(!do_after(src, 8 SECONDS, our_wall, hidden = TRUE)) + if(!do_after(src, 8 SECONDS, our_wall, cog_icon = null)) qdel(particles) return COMPONENT_CANCEL_ATTACK_CHAIN if(!conversions_remaining) diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index daa701eb6cb3..982b20b9a2bd 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -191,7 +191,6 @@ to_chat(current_owner, span_notice("Your otherworldly vision fades...")) current_owner.remove_traits(list(TRAIT_SIXTHSENSE, TRAIT_XRAY_VISION), SCRYING_ORB) - current_owner.update_sight() current_owner = null @@ -201,7 +200,6 @@ to_chat(current_owner, span_notice("You can see...everything!")) current_owner.add_traits(list(TRAIT_SIXTHSENSE, TRAIT_XRAY_VISION), SCRYING_ORB) - current_owner.update_sight() /obj/item/scrying/attack_self(mob/user) visible_message(span_danger("[user] stares into [src], their eyes glazing over.")) diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index c822f7022206..fe606edffa60 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -291,21 +291,22 @@ if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD) . += extra_desc -/obj/structure/constructshell/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers) - if(istype(O, /obj/item/soulstone)) - var/obj/item/soulstone/SS = O - if(!IS_CULTIST(user) && !HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) && !SS.theme == THEME_HOLY) - to_chat(user, span_danger("An overwhelming feeling of dread comes over you as you attempt to place [SS] into the shell. It would be wise to be rid of this quickly.")) - if(isliving(user)) - var/mob/living/living_user = user - living_user.set_dizzy_if_lower(1 MINUTES) - return - if(SS.theme == THEME_HOLY && IS_CULTIST(user)) - SS.hot_potato(user) - return - SS.transfer_to_construct(src, user) - else - return ..() +/obj/structure/constructshell/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/soulstone)) + return NONE + + var/obj/item/soulstone/whispering_gem = tool + if(!IS_CULTIST(user) && !HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) && whispering_gem.theme != THEME_HOLY) + to_chat(user, span_danger("An overwhelming feeling of dread comes over you as you attempt to place [whispering_gem] into the shell. It would be wise to be rid of this quickly.")) + user.set_dizzy_if_lower(1 MINUTES) + return ITEM_INTERACT_BLOCKING + + if(whispering_gem.theme == THEME_HOLY && IS_CULTIST(user)) + whispering_gem.hot_potato(user) + return ITEM_INTERACT_BLOCKING + + whispering_gem.transfer_to_construct(src, user) + return ITEM_INTERACT_SUCCESS /// Procs for moving soul in and out off stone @@ -325,7 +326,7 @@ to_chat(user, span_cult("\"This soul is mine.
    SACRIFICE THEM!\"")) return FALSE - if(grab_sleeping ? victim.stat == CONSCIOUS : victim.stat != DEAD) + if(grab_sleeping ? !IS_UNCONSCIOUS_OR_CRIT(victim) : victim.stat != DEAD) to_chat(user, span_userdanger("Capture failed!")) to_chat(user, span_danger("Kill or maim the victim first!")) return FALSE diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm index 4d4063f6efda..33f15971c934 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm @@ -14,15 +14,16 @@ wizard_datum.perks += src RegisterSignal(user, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created)) if (user.hud_used) - on_hud_created() + on_hud_created(user) to_chat(user, span_notice("You got a new perk: [src.name].")) log_purchase(user.key) return TRUE -/datum/spellbook_entry/perks/proc/on_hud_created(mob/living/carbon/human/user, datum/antagonist/wizard/wizard_datum) +/datum/spellbook_entry/perks/proc/on_hud_created(mob/living/carbon/human/source) SIGNAL_HANDLER - var/datum/hud/user_hud = user.hud_used + var/datum/hud/user_hud = source.hud_used + var/datum/antagonist/wizard/wizard_datum = source.mind.has_antag_datum(/datum/antagonist/wizard) if(!user_hud.screen_objects[HUD_WIZARD_COMPACT_PERKS]) user_hud.add_screen_object(/atom/movable/screen/perk/more, HUD_WIZARD_COMPACT_PERKS, HUD_GROUP_INFO, ui_loc = ui_perk_position(0), update_screen = TRUE) diff --git a/code/modules/antagonists/wizard/equipment/wiz_robe.dm b/code/modules/antagonists/wizard/equipment/wiz_robe.dm index 3152d0b5222f..224c912a3e0e 100644 --- a/code/modules/antagonists/wizard/equipment/wiz_robe.dm +++ b/code/modules/antagonists/wizard/equipment/wiz_robe.dm @@ -8,7 +8,7 @@ armor_type = /datum/armor/head_wizard strip_delay = 5 SECONDS equip_delay_other = 5 SECONDS - clothing_flags = SNUG_FIT | CASTING_CLOTHES + clothing_flags = SNUG_FIT resistance_flags = FIRE_PROOF | ACID_PROOF dog_fashion = /datum/dog_fashion/head/blue_wizard ///How much this hat affects fishing difficulty @@ -16,6 +16,7 @@ /obj/item/clothing/head/wizard/Initialize(mapload) . = ..() + ADD_TRAIT(src, TRAIT_CASTING_CLOTHING, INNATE_TRAIT) AddElement(/datum/element/adjust_fishing_difficulty, fishing_modifier) //A wizard always practices his casting (ba dum tsh) /datum/armor/head_wizard @@ -123,13 +124,13 @@ flags_inv = HIDEJUMPSUIT strip_delay = 5 SECONDS equip_delay_other = 5 SECONDS - clothing_flags = CASTING_CLOTHES resistance_flags = FIRE_PROOF | ACID_PROOF ///How much this robe affects fishing difficulty var/fishing_modifier = -7 /obj/item/clothing/suit/wizrobe/Initialize(mapload) . = ..() + ADD_TRAIT(src, TRAIT_CASTING_CLOTHING, INNATE_TRAIT) AddElement(/datum/element/adjust_fishing_difficulty, fishing_modifier) //A wizard always practices his casting (ba dum tsh) /datum/armor/suit_wizrobe diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm index ecb5120fd12c..3adbd8dc623e 100644 --- a/code/modules/art/paintings.dm +++ b/code/modules/art/paintings.dm @@ -338,7 +338,7 @@ GLOBAL_LIST_INIT(canvas_dimensions, init_canvas_dimensions()) /obj/item/canvas/proc/can_select_frame(mob/user) if(!istype(loc, /obj/structure/sign/painting)) return FALSE - if(!loc.IsReachableBy(user) || IS_DEAD_OR_INCAP(user)) + if(!loc.IsReachableBy(user) || user.incapacitated) return FALSE if(!last_patron || !IS_WEAKREF_OF(user?.mind, last_patron)) return FALSE diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm index e86b53d68ea2..9f9dae774cbc 100644 --- a/code/modules/art/statues.dm +++ b/code/modules/art/statues.dm @@ -35,17 +35,21 @@ default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/structure/statue/attackby(obj/item/W, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/structure/statue/item_interaction(mob/living/user, obj/item/tool, list/modifiers) add_fingerprint(user) - if(W.tool_behaviour == TOOL_WELDER) - if(!W.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED)) - return FALSE - user.balloon_alert(user, "slicing apart...") - if(W.use_tool(src, user, 40, volume=50)) - deconstruct(TRUE) - return return ..() +/obj/structure/statue/welder_act(mob/living/user, obj/item/tool) + add_fingerprint(user) + if(!tool.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED)) + return ITEM_INTERACT_BLOCKING + + user.balloon_alert(user, "slicing apart...") + if(!tool.use_tool(src, user, 4 SECONDS, volume = 50)) + return ITEM_INTERACT_BLOCKING + + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS /obj/structure/statue/atom_deconstruct(disassembled = TRUE) var/amount_mod = disassembled ? 0 : -2 @@ -371,6 +375,10 @@ Moving interrupts prepared_block.set_target(interacting_with, user) return ITEM_INTERACT_SUCCESS + else if(istype(interacting_with, /turf/open/floor/concrete)) + var/turf/open/floor/concrete/concrete_floor = interacting_with + return concrete_floor.handle_shape(user) + return NONE // We aim at something distant. @@ -394,7 +402,7 @@ Moving interrupts var/datum/progressbar/total_progress_bar = new(user, sculpting_time, prepared_block) while(remaining_time > 0 && !interrupted) - if(do_after(user, sculpting_period, target = prepared_block, progress = FALSE)) + if(do_after(user, sculpting_period, target = prepared_block, show_progress = FALSE)) var/time_delay = !(remaining_time % SCULPT_SOUND_INCREMENT) if(time_delay) playsound(src, 'sound/effects/break_stone.ogg', 50, TRUE) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index f639d29e79f4..04cf6b9fc38f 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -120,42 +120,42 @@ return . = ..() -/obj/item/assembly/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(isassembly(attacking_item)) - var/obj/item/assembly/new_assembly = attacking_item +/obj/item/assembly/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(isassembly(tool)) + var/obj/item/assembly/new_assembly = tool // Check both our's and their's assembly flags to see if either should not duplicate // If so, and we match types, don't create a holder - block it if(((new_assembly.assembly_flags|assembly_flags) & ASSEMBLY_NO_DUPLICATES) && istype(new_assembly, type)) balloon_alert(user, "can't attach another [new_assembly.name]!") - return + return ITEM_INTERACT_BLOCKING + if(new_assembly.secured) balloon_alert(user, "[new_assembly.name] is not attachable!") - return + return ITEM_INTERACT_BLOCKING + if(secured) balloon_alert(user, "[name] is not attachable!") - return + return ITEM_INTERACT_BLOCKING holder = new /obj/item/assembly_holder(drop_location()) holder.assemble(src, new_assembly, user) holder.balloon_alert(user, "parts combined") - return + return ITEM_INTERACT_SUCCESS - if(istype(attacking_item, /obj/item/assembly_holder)) - var/obj/item/assembly_holder/added_to_holder = attacking_item + if(istype(tool, /obj/item/assembly_holder)) + var/obj/item/assembly_holder/added_to_holder = tool added_to_holder.try_add_assembly(src, user) - return + return ITEM_INTERACT_BLOCKING - return ..() + return NONE -/obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I) - if(..()) - return TRUE +/obj/item/assembly/screwdriver_act(mob/living/user, obj/item/tool) if(toggle_secure()) to_chat(user, span_notice("\The [src] is ready!")) else to_chat(user, span_notice("\The [src] can now be attached!")) add_fingerprint(user) - return TRUE + return ITEM_INTERACT_SUCCESS /obj/item/assembly/examine(mob/user) . = ..() diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 769b60ecb04c..d2e2813521ba 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -5,6 +5,7 @@ desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." icon = 'icons/obj/devices/flash.dmi' icon_state = "flash" + worn_icon_state = "flash" inhand_icon_state = "flashtool" lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' @@ -317,11 +318,11 @@ if (.) new /obj/effect/temp_visual/borgflash(get_turf(src)) -/obj/item/assembly/flash/cyborg/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - return +/obj/item/assembly/flash/cyborg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_BLOCKING /obj/item/assembly/flash/cyborg/screwdriver_act(mob/living/user, obj/item/I) - return + return ITEM_INTERACT_BLOCKING /obj/item/assembly/flash/memorizer name = "memorizer" diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index e9c57626bc67..95748cab157f 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -134,12 +134,12 @@ for(var/obj/item/assembly/assembly as anything in assemblies) assembly.attack_hand(user, modifiers) // Note override in assembly.dm to prevent side effects here -/obj/item/assembly_holder/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(isassembly(weapon)) - try_add_assembly(weapon, user) - return +/obj/item/assembly_holder/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!isassembly(tool)) + return NONE - return ..() + try_add_assembly(tool, user) + return ITEM_INTERACT_SUCCESS /obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index a65064634ea2..48574d119cbd 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -122,14 +122,18 @@ update_appearance() -/obj/item/assembly/signaler/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - if(issignaler(W)) - var/obj/item/assembly/signaler/signaler2 = W - if(secured && signaler2.secured) - code = signaler2.code - set_frequency(signaler2.frequency) - to_chat(user, "You transfer the frequency and code of \the [signaler2.name] to \the [name]") - ..() +/obj/item/assembly/signaler/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!issignaler(tool)) + return ..() + + var/obj/item/assembly/signaler/sister_signaler = tool + if(!secured || !sister_signaler.secured) + return ..() + + code = sister_signaler.code + set_frequency(sister_signaler.frequency) + to_chat(user, "You transfer the frequency and code of \the [sister_signaler.name] to \the [name]") + return ITEM_INTERACT_SUCCESS /obj/item/assembly/signaler/attack_self_secondary(mob/user, modifiers) . = ..() @@ -190,10 +194,11 @@ /obj/item/assembly/signaler/cyborg -/obj/item/assembly/signaler/cyborg/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - return +/obj/item/assembly/signaler/cyborg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_BLOCKING + /obj/item/assembly/signaler/cyborg/screwdriver_act(mob/living/user, obj/item/I) - return + return ITEM_INTERACT_BLOCKING /obj/item/assembly/signaler/internal name = "internal remote signaling device" @@ -201,11 +206,11 @@ /obj/item/assembly/signaler/internal/ui_state(mob/user) return GLOB.inventory_state -/obj/item/assembly/signaler/internal/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - return +/obj/item/assembly/signaler/internal/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_BLOCKING /obj/item/assembly/signaler/internal/screwdriver_act(mob/living/user, obj/item/I) - return + return ITEM_INTERACT_BLOCKING /obj/item/assembly/signaler/internal/can_interact(mob/user) if(ispAI(user)) diff --git a/code/modules/asset_cache/assets/tgui.dm b/code/modules/asset_cache/assets/tgui.dm index 89556bc4487e..132580738422 100644 --- a/code/modules/asset_cache/assets/tgui.dm +++ b/code/modules/asset_cache/assets/tgui.dm @@ -34,6 +34,33 @@ #endif +/datum/asset/simple/namespaced/escape_menu_font + assets = list( + "Pixellari.ttf" = file("interface/fonts/Pixellari.ttf"), + "Grand9K_Pixel.ttf" = file("interface/fonts/Grand9K_Pixel.ttf"), + ) + parents = list( + "fonts.css" = file("tgui/packages/tgui-escape-menu/styles/fonts.css"), + ) + +/datum/asset/simple/namespaced/escape_menu_sounds + assets = list( + "esc_open.ogg" = file("sound/misc/escape_menu/esc_open.ogg"), + "esc_middle.ogg" = file("sound/misc/escape_menu/esc_middle.ogg"), + "esc_close.ogg" = file("sound/misc/escape_menu/esc_close.ogg"), + ) + +/datum/asset/spritesheet/escape_menu_icons + name = "escape-menu-icons" + +/datum/asset/spritesheet/escape_menu_icons/create_spritesheets() + var/icon/icons_small = 'icons/hud/escape_menu_icons.dmi' + for(var/state in icon_states(icons_small)) + Insert(state, icons_small, icon_state = state) + var/icon/icons_large = 'icons/hud/escape_menu_leave_body.dmi' + for(var/state in icon_states(icons_large)) + Insert("leave-[state]", icons_large, icon_state = state) + /datum/asset/simple/chat_dark keep_local_name = FALSE assets = list( diff --git a/code/modules/asset_cache/spritesheet/batched/universal_icon.dm b/code/modules/asset_cache/spritesheet/batched/universal_icon.dm index efd120ac782a..caa9d7f00c53 100644 --- a/code/modules/asset_cache/spritesheet/batched/universal_icon.dm +++ b/code/modules/asset_cache/spritesheet/batched/universal_icon.dm @@ -579,8 +579,8 @@ ) flatX1 = addX1 - flatX2 = addY1 - flatY1 = addX2 + flatX2 = addX2 + flatY1 = addY1 flatY2 = addY2 // Blend the overlay into the flattened icon diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 053ba55bb738..6a34b2b276e0 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -139,8 +139,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/cached_moles_archive = moles_archive temperature_archived = temperature - for(var/gas_id in cached_moles) - cached_moles_archive[gas_id] = cached_moles[gas_id] + for(var/gas_id, value in cached_moles) + cached_moles_archive[gas_id] = value return TRUE @@ -160,8 +160,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/cached_moles = moles //accessing datum vars is slower than proc vars var/list/cached_giver_moles = giver.moles //gas transfer - for(var/gas_id in cached_giver_moles) - cached_moles[gas_id] += cached_giver_moles[gas_id] + for(var/gas_id, value in cached_giver_moles) + cached_moles[gas_id] += value SEND_SIGNAL(src, COMSIG_GASMIX_MERGED) return TRUE @@ -184,8 +184,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm ///gases_moles is an associative list of gas species to their amount to be added /datum/gas_mixture/proc/adjust_multiple_gases(list/gases_moles) var/cached_moles = moles - for(var/gas_id in gases_moles) - cached_moles[gas_id] += gases_moles[gas_id] + for(var/gas_id, value in gases_moles) + cached_moles[gas_id] += value garbage_collect() @@ -213,8 +213,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/cached_removed_moles = removed.moles //accessing datum vars is slower than proc vars removed.temperature = temperature - for(var/id in cached_moles) - cached_removed_moles[id] = QUANTIZE(cached_moles[id] * ratio) + for(var/id, value in cached_moles) + cached_removed_moles[id] = QUANTIZE(value * ratio) cached_moles[id] -= cached_removed_moles[id] garbage_collect() @@ -235,8 +235,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/cached_removed_moles = removed.moles //accessing datum vars is slower than proc vars removed.temperature = temperature - for(var/id in cached_moles) - cached_removed_moles[id] = QUANTIZE(cached_moles[id] * ratio) + for(var/id, value in cached_moles) + cached_removed_moles[id] = QUANTIZE(value * ratio) cached_moles[id] -= cached_removed_moles[id] garbage_collect() @@ -315,8 +315,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/copy_cached_moles_archive = copy.moles_archive copy.temperature = temperature - for(var/gas_id in cached_moles) - copy_cached_moles[gas_id] = cached_moles[gas_id] + for(var/gas_id, value in cached_moles) + copy_cached_moles[gas_id] = value copy_cached_moles_archive[gas_id] = 0 return copy @@ -334,8 +334,8 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm cached_moles_archive.Cut() temperature = sample.temperature - for(var/gas_id in sample_cached_moles) - cached_moles[gas_id] = sample_cached_moles[gas_id] + for(var/gas_id, value in sample_cached_moles) + cached_moles[gas_id] = value cached_moles_archive[gas_id] = 0 return TRUE @@ -345,14 +345,11 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm /datum/gas_mixture/proc/copy_from_ratio(datum/gas_mixture/sample, partial = 1) var/list/cached_moles = moles //accessing datum vars is slower than proc vars var/list/sample_cached_moles = sample.moles - - //remove all gases not in the sample - cached_moles &= sample_cached_moles - + // Remove all gases (sample overrides our values anyways) + cached_moles.Cut() temperature = sample.temperature - for(var/gas_id in sample_cached_moles) - cached_moles[gas_id] = sample_cached_moles[gas_id] * partial - + for(var/gas_id, amount in sample_cached_moles) + cached_moles[gas_id] = amount * partial return TRUE /// Performs air sharing calculations between two gas_mixtures @@ -538,10 +535,10 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm if((reqs["MIN_TEMP"] && temp < reqs["MIN_TEMP"]) || (reqs["MAX_TEMP"] && temp > reqs["MAX_TEMP"])) continue - for(var/id in reqs) + for(var/id, value in reqs) if (id == "MIN_TEMP" || id == "MAX_TEMP") continue - if(cached_moles[id] < reqs[id]) + if(cached_moles[id] < value) continue reaction_loop //at this point, all requirements for the reaction are satisfied. we can now react() @@ -797,8 +794,7 @@ GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm var/list/gases_to_check = acceptable_gas_bounds.Copy() // thank you spaceman var/list/cached_moles = moles - for(var/id in cached_moles) - var/gas_moles = cached_moles[id] + for(var/id, gas_moles in cached_moles) if(!(id in gases_to_check)) if(gas_moles > extraneous_gas_limit) return FALSE diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm index 49e57b08d44e..54a86a334532 100644 --- a/code/modules/atmospherics/gasmixtures/gas_types.dm +++ b/code/modules/atmospherics/gasmixtures/gas_types.dm @@ -6,17 +6,16 @@ var/list/gas_types = subtypesof(/datum/gas) ASSERT(GAS_TYPE_COUNT == length(gas_types),\ "GAS_TYPE_COUNT != length(subtypesof(gas_types)), if you added new gas please increment GAS_TYPE_COUNT") - for(var/gas_path in gas_types) - var/datum/gas/gas = gas_path - gas_info[META_GAS_SPECIFIC_HEAT][gas_path] = initial(gas.specific_heat) - gas_info[META_GAS_NAME][gas_path] = initial(gas.name) - gas_info[META_GAS_MOLES_VISIBLE][gas_path] = initial(gas.moles_visible) + for(var/datum/gas/gas_path as anything in gas_types) + gas_info[META_GAS_SPECIFIC_HEAT][gas_path] = initial(gas_path.specific_heat) + gas_info[META_GAS_NAME][gas_path] = initial(gas_path.name) + gas_info[META_GAS_MOLES_VISIBLE][gas_path] = initial(gas_path.moles_visible) if (gas_info[META_GAS_MOLES_VISIBLE][gas_path]) - gas_info[META_GAS_OVERLAY][gas_path] += generate_gas_overlays(0, SSmapping.max_plane_offset, gas) - gas_info[META_GAS_FUSION_POWER][gas_path] = initial(gas.fusion_power) - gas_info[META_GAS_DANGER][gas_path] = initial(gas.dangerous) - gas_info[META_GAS_ID][gas_path] = initial(gas.id) - gas_info[META_GAS_DESC][gas_path] = initial(gas.desc) + gas_info[META_GAS_OVERLAY][gas_path] += generate_gas_overlays(0, SSmapping.max_plane_offset, gas_path) + gas_info[META_GAS_FUSION_POWER][gas_path] = initial(gas_path.fusion_power) + gas_info[META_GAS_DANGER][gas_path] = initial(gas_path.dangerous) + gas_info[META_GAS_ID][gas_path] = initial(gas_path.id) + gas_info[META_GAS_DESC][gas_path] = initial(gas_path.desc) /datum/gas_mixture::gas_meta = gas_info // save the reference to the list return gas_info @@ -35,8 +34,8 @@ var/list/meta_gas_id = GLOB.meta_gas_info[META_GAS_ID] if(id in meta_gas_id) return id - for(var/path in meta_gas_id) - if(meta_gas_id[path] == id) + for(var/path, meta_id in meta_gas_id) + if(meta_id == id) return path return "" diff --git a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm index dce8ab87ed5d..9ed9c9fe4e6b 100644 --- a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm +++ b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm @@ -75,15 +75,15 @@ temperature = initial_temperature gas -= "TEMP" mix.Cut() - for(var/id in gas) + for(var/id, value in gas) var/path = id if(!ispath(path)) path = gas_id2path(path) //a lot of these strings can't have embedded expressions (especially for mappers), so support for IDs needs to stick around - mix[path] = text2num(gas[id]) + mix[path] = text2num(value) var/list/cached_moles = moles var/list/cached_moles_archive = moles_archive - for(var/gas_id in mix) - cached_moles[gas_id] = cached_moles_archive[gas_id] = mix[gas_id] + for(var/gas_id, value in mix) + cached_moles[gas_id] = cached_moles_archive[gas_id] = value diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index 060b394872ed..14b5e02b7fac 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -1222,13 +1222,13 @@ if(total_not_antinoblium_moles < MINIMUM_MOLE_COUNT) // Clear up the remaining gases if this condition is met. . = NO_REACTION reaction_rate = total_not_antinoblium_moles - for(var/gas_id in cached_moles) + for(var/gas_id, value in cached_moles) if(gas_id == /datum/gas/antinoblium) continue if(. == NO_REACTION) // Let the gases get properly cleared while avoiding potential division by 0. cached_moles[gas_id] = 0 continue - cached_moles[gas_id] -= reaction_rate * cached_moles[gas_id] / total_not_antinoblium_moles + cached_moles[gas_id] -= reaction_rate * value / total_not_antinoblium_moles cached_moles[/datum/gas/antinoblium] += reaction_rate SET_REACTION_RESULTS(reaction_rate) diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm index bc6296741583..d0a50d1ae121 100644 --- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm +++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm @@ -100,57 +100,73 @@ if((buildstage == AIR_ALARM_BUILD_COMPLETE)) new /obj/item/stack/cable_coil(loc, 3) -/obj/machinery/airalarm/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) +/obj/machinery/airalarm/item_interaction(mob/living/user, obj/item/tool, list/modifiers) update_last_used(user) switch(buildstage) if(AIR_ALARM_BUILD_COMPLETE) - if(W.GetID())// trying to unlock the interface with an ID card + if(tool.GetID())// trying to unlock the interface with an ID card togglelock(user) - return - else if(panel_open && is_wire_tool(W)) + return ITEM_INTERACT_SUCCESS + + if(panel_open && is_wire_tool(tool)) wires.interact(user) - return + return ITEM_INTERACT_SUCCESS + + return NONE + if(AIR_ALARM_BUILD_NO_WIRES) - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/cable = W - if(cable.get_amount() < 5) - to_chat(user, span_warning("You need five lengths of cable to wire the air alarm!")) - return - user.visible_message(span_notice("[user.name] wires the air alarm."), \ - span_notice("You start wiring the air alarm...")) - if (do_after(user, 2 SECONDS, target = src)) - if (cable.get_amount() >= 5 && buildstage == AIR_ALARM_BUILD_NO_WIRES) - cable.use(5) - to_chat(user, span_notice("You wire the air alarm.")) - wires.repair() - aidisabled = FALSE - locked = FALSE - shorted = FALSE - danger_level = AIR_ALARM_ALERT_NONE - buildstage = AIR_ALARM_BUILD_COMPLETE - select_mode(user, /datum/air_alarm_mode/filtering) - update_appearance() - return + if(!istype(tool, /obj/item/stack/cable_coil)) + return NONE + + var/obj/item/stack/cable_coil/cable = tool + if(cable.get_amount() < 5) + to_chat(user, span_warning("You need five lengths of cable to wire the air alarm!")) + return ITEM_INTERACT_BLOCKING + + user.visible_message(span_notice("[user.name] wires the air alarm."), \ + span_notice("You start wiring the air alarm...")) + if(!do_after(user, 2 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + + if(cable.get_amount() < 5 || buildstage != AIR_ALARM_BUILD_NO_WIRES) + return ITEM_INTERACT_BLOCKING + + cable.use(5) + to_chat(user, span_notice("You wire the air alarm.")) + wires.repair() + aidisabled = FALSE + locked = FALSE + shorted = FALSE + danger_level = AIR_ALARM_ALERT_NONE + buildstage = AIR_ALARM_BUILD_COMPLETE + select_mode(user, /datum/air_alarm_mode/filtering) + update_appearance() + return ITEM_INTERACT_SUCCESS + if(AIR_ALARM_BUILD_NO_CIRCUIT) - if(istype(W, /obj/item/electronics/airalarm)) - if(user.temporarilyRemoveItemFromInventory(W)) - to_chat(user, span_notice("You insert the circuit.")) - buildstage = AIR_ALARM_BUILD_NO_WIRES - update_appearance() - qdel(W) - return - - if(istype(W, /obj/item/electroadaptive_pseudocircuit)) - var/obj/item/electroadaptive_pseudocircuit/P = W - if(!P.adapt_circuit(user, circuit_cost = 0.025 * STANDARD_CELL_CHARGE)) - return + if(istype(tool, /obj/item/electronics/airalarm)) + if(!user.temporarilyRemoveItemFromInventory(tool)) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You insert the circuit.")) + buildstage = AIR_ALARM_BUILD_NO_WIRES + update_appearance() + qdel(tool) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/electroadaptive_pseudocircuit)) + if(!astype(tool, /obj/item/electroadaptive_pseudocircuit).adapt_circuit(user, circuit_cost = 0.025 * STANDARD_CELL_CHARGE)) + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \ span_notice("You adapt an air alarm circuit and slot it into the assembly.")) buildstage = AIR_ALARM_BUILD_NO_WIRES update_appearance() - return + return ITEM_INTERACT_SUCCESS - return ..() + return NONE + + return NONE /obj/machinery/airalarm/proc/reset(wire) switch(wire) diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index f7ac8c29e58e..9fb051576635 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -409,14 +409,16 @@ nodes[nodes.Find(reference)] = null update_appearance() -/obj/machinery/atmospherics/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - if(istype(W, /obj/item/pipe)) //lets you autodrop - var/obj/item/pipe/pipe = W - if(user.dropItemToGround(pipe)) - pipe.set_piping_layer(piping_layer) //align it with us - return TRUE - else - return ..() +/obj/machinery/atmospherics/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/pipe)) //lets you autodrop + return NONE + + var/obj/item/pipe/pipe = tool + if(!user.dropItemToGround(pipe)) + return ITEM_INTERACT_BLOCKING + + pipe.set_piping_layer(piping_layer) //align it with us + return ITEM_INTERACT_SUCCESS /obj/machinery/atmospherics/wrench_act(mob/living/user, obj/item/I) if(!can_unwrench(user)) diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm index a469f4cc2e74..983b136f5d4f 100644 --- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm +++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm @@ -160,24 +160,27 @@ /obj/machinery/electrolyzer/crowbar_act(mob/living/user, obj/item/tool) return default_deconstruction_crowbar(user, tool) -/obj/machinery/electrolyzer/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) +/obj/machinery/electrolyzer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) add_fingerprint(user) - if(istype(I, /obj/item/stock_parts/power_store/cell)) - if(!panel_open) - balloon_alert(user, "open panel!") - return - if(cell) - balloon_alert(user, "cell inside!") - return - if(!user.transferItemToLoc(I, src)) - return - cell = I - I.add_fingerprint(usr) - balloon_alert(user, "inserted cell") - SStgui.update_uis(src) + if(!istype(tool, /obj/item/stock_parts/power_store/cell)) + return NONE - return - return ..() + if(!panel_open) + balloon_alert(user, "open panel!") + return ITEM_INTERACT_BLOCKING + + if(cell) + balloon_alert(user, "cell inside!") + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + + cell = tool + tool.add_fingerprint(usr) + balloon_alert(user, "inserted cell") + SStgui.update_uis(src) + return ITEM_INTERACT_SUCCESS /obj/machinery/electrolyzer/click_alt(mob/user) if(panel_open) diff --git a/code/modules/atmospherics/machinery/components/tank.dm b/code/modules/atmospherics/machinery/components/tank.dm index 8d9381df2732..46447150f951 100644 --- a/code/modules/atmospherics/machinery/components/tank.dm +++ b/code/modules/atmospherics/machinery/components/tank.dm @@ -31,9 +31,6 @@ greyscale_colors = "#ffffff" var/overlay_greyscale_config = /datum/greyscale_config/stationary_canister_overlays - ///The image showing the gases inside of the tank - var/image/window - /// The open node directions of the tank, assuming that the tank is facing NORTH. var/open_ports = NONE /// The volume of the gas mixture @@ -67,6 +64,12 @@ var/merger_id = "stationary_tanks" /// The typecache of types which are allowed to merge internal storage var/static/list/merger_typecache + /// Cached GAGS icon for our window + var/icon/greyscaled_icon = null + /// Holder for our gas overlays used to alpha mask them + var/obj/effect/abstract/tank_gas_holder/gas_holder = null + /// Window glass visual rendered ontop of gases + var/obj/effect/abstract/tank_glass/window_glass = null /obj/machinery/atmospherics/components/tank/Initialize(mapload) . = ..() @@ -84,6 +87,12 @@ if(!merger_typecache) merger_typecache = typecacheof(/obj/machinery/atmospherics/components/tank) + greyscaled_icon = SSgreyscale.GetColoredIconByType(overlay_greyscale_config, greyscale_colors) + gas_holder = new(src) + window_glass = new(src, src) + vis_contents += window_glass + vis_contents += gas_holder + AddComponent(/datum/component/gas_leaker, leak_rate = 0.05) AddElement(/datum/element/volatile_gas_storage) AddElement(/datum/element/crackable, 'icons/obj/pipes_n_cables/stationary_canisters_misc.dmi', crack_states) @@ -116,6 +125,8 @@ GetMergeGroup(merger_id, merger_typecache) /obj/machinery/atmospherics/components/tank/Destroy() + QDEL_NULL(window_glass) + QDEL_NULL(gas_holder) QUEUE_SMOOTH_NEIGHBORS(src) return ..() @@ -307,6 +318,7 @@ /obj/machinery/atmospherics/components/tank/update_overlays() . = ..() + . += mutable_appearance(greyscaled_icon, "window-bg") if(!initialize_directions) return for(var/dir in GLOB.cardinals) @@ -314,34 +326,52 @@ . += knob_overlays["[dir]"] /obj/machinery/atmospherics/components/tank/update_greyscale() - . = ..() - refresh_window() + greyscaled_icon = SSgreyscale.GetColoredIconByType(overlay_greyscale_config, greyscale_colors) + window_glass?.icon = greyscaled_icon + return ..() /obj/machinery/atmospherics/components/tank/proc/refresh_window() - cut_overlay(window) + if(!gas_holder) // Not created in init yet + return if(!air_contents) - window = null + gas_holder.vis_contents.Cut() return - var/icon/greyscaled_icon = SSgreyscale.GetColoredIconByType(overlay_greyscale_config, greyscale_colors) - window = image(greyscaled_icon, icon_state = "window-bg", layer = FLOAT_LAYER) + var/list/air_visuals = air_contents.return_visuals(get_turf(src)) + var/static/list/gas_visual_overrides = list() + var/list/new_visuals = list() + // For those who come after, we need to map existing gas visuals to tank-specific versions which are VIS_INHERIT_ID | VIS_INHERIT_PLANE + // due to BYOND tomfuckery, as INHERIT_ID is not enough on its own and only that is passed down. I hate this too, sorry. + for(var/obj/effect/overlay/gas/gas as anything in air_visuals) + if (gas_visual_overrides[gas]) + new_visuals += gas_visual_overrides[gas] + continue - var/static/alpha_filter - if(!alpha_filter) // Gotta do this separate since the icon may not be correct at world init - alpha_filter = filter(type="alpha", icon = icon('icons/obj/pipes_n_cables/stationary_canisters_misc.dmi', "window-bg")) + var/obj/effect/overlay/gas/local_gas = new(gas.icon_state, gas.alpha, gas.plane_offset) + local_gas.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE + gas_visual_overrides[gas] = local_gas + new_visuals += local_gas - var/list/new_underlays = list() - for(var/obj/effect/overlay/gas/gas as anything in air_contents.return_visuals(get_turf(src))) - var/image/new_underlay = image(gas.icon, icon_state = gas.icon_state, layer = FLOAT_LAYER) - new_underlay.filters = alpha_filter - new_underlays += new_underlay + gas_holder.vis_contents = new_visuals - var/image/foreground = image(greyscaled_icon, icon_state = "window-fg", layer = FLOAT_LAYER) - foreground.underlays = new_underlays - window.overlays = list(foreground) +/obj/effect/abstract/tank_glass + icon = 'icons/obj/pipes_n_cables/stationary_canisters_misc.dmi' + icon_state = "window-fg" + vis_flags = VIS_INHERIT_PLANE | VIS_INHERIT_LAYER | VIS_INHERIT_ID - add_overlay(window) +/obj/effect/abstract/tank_glass/Initialize(mapload, obj/machinery/atmospherics/components/tank/owner) + . = ..() + if (owner) + icon = owner.greyscaled_icon + +/obj/effect/abstract/tank_gas_holder + appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE | TILE_BOUND + vis_flags = VIS_INHERIT_LAYER | VIS_INHERIT_PLANE | VIS_INHERIT_ID + +/obj/effect/abstract/tank_gas_holder/Initialize(mapload) + . = ..() + add_filter("tank_gas_holder", 1, alpha_mask_filter(icon = icon('icons/obj/pipes_n_cables/stationary_canisters_misc.dmi', "window-bg"))) /////////////////////////////////////////////////////////////////// // Tool interactions @@ -568,10 +598,14 @@ if(TANK_PLATING_UNSECURED) icon_state = "plated_frame" -/obj/structure/tank_frame/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers) - if(construction_state == TANK_FRAME && isstack(item) && add_plating(user, item)) - return - return ..() +/obj/structure/tank_frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(construction_state != TANK_FRAME || !isstack(tool)) + return ..() + + if(!add_plating(user, tool)) + return ITEM_INTERACT_BLOCKING + + return ITEM_INTERACT_SUCCESS /obj/structure/tank_frame/wrench_act(mob/living/user, obj/item/tool) . = ..() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm index daac7d513819..ddcb66267a9e 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm @@ -605,7 +605,7 @@ // unbolt all the doors but don't open them for(var/obj/machinery/door/airlock/airlock as anything in (internal_airlocks + external_airlocks)) airlock.unbolt() - audible_message(span_notice("[src] whirrs as [p_they()] loses power, disengaging airlock bolts.")) + audible_message(span_notice("[src] whirrs as [p_they()] lose[p_s()] power, disengaging airlock bolts.")) deltimer(emergency_stop_timer) set_on(FALSE) @@ -616,7 +616,7 @@ for(var/obj/machinery/door/airlock/airlock as anything in internal_airlocks) if(open_airlock_on_cycle) INVOKE_ASYNC(airlock, TYPE_PROC_REF(/obj/machinery/door/airlock, secure_open)) - audible_message(span_notice("[src] whirrs as [p_they()] regains power, re-engaging airlock bolts.")) + audible_message(span_notice("[src] whirrs as [p_they()] regain[p_s()] power, re-engaging airlock bolts.")) /obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only open_airlock_on_cycle = FALSE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index ba6680979fb8..b24e448ad1f7 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -521,8 +521,8 @@ user.visible_message(span_notice("You see [user] kicking against the glass of [src]!"), \ span_notice("You struggle inside [src], kicking the release with your foot... (this will take about [DisplayTimeText(CRYO_BREAKOUT_TIME)].)"), \ span_hear("You hear a thump from [src].")) - if(do_after(user, CRYO_BREAKOUT_TIME, target = src, hidden = TRUE)) - if(!user || user.stat != CONSCIOUS || user.loc != src ) + if(do_after(user, CRYO_BREAKOUT_TIME, target = src, cog_icon = null)) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src ) return user.visible_message(span_warning("[user] successfully broke out of [src]!"), \ span_notice("You successfully break out of [src]!")) @@ -555,10 +555,10 @@ occupant_data["name"] = mob_occupant.name if(mob_occupant.stat == DEAD) occupant_data["stat"] = "Dead" - else if (HAS_TRAIT(mob_occupant, TRAIT_KNOCKEDOUT)) + else if (IS_UNCONSCIOUS(mob_occupant)) occupant_data["stat"] = "Unconscious" else - occupant_data["stat"] = "Conscious" + occupant_data["stat"] = "Stable" occupant_data["bodyTemperature"] = round(mob_occupant.bodytemperature, 1) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 9200d94dbe32..95ea69344d5a 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -203,14 +203,16 @@ var/turf/open/us = loc if(!istype(us)) return - scrub(us) + if(scrub(us)) + us.air_update_turf(FALSE, FALSE) if(widenet) if(COOLDOWN_FINISHED(src, check_turfs_cooldown)) check_turfs() COOLDOWN_START(src, check_turfs_cooldown, 2 SECONDS) for(var/turf/tile in adjacent_turfs) - scrub(tile) + if (scrub(tile)) + tile.air_update_turf(FALSE, FALSE) return TRUE ///filtered gases at or below this amount automatically get removed from the mix diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index b4c48341f835..90928274ab9f 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -89,14 +89,15 @@ return air_temporary.remove(amount) return parent.air.remove(amount) -/obj/machinery/atmospherics/pipe/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/pipe_meter)) - var/obj/item/pipe_meter/meter = item - user.dropItemToGround(meter) - meter.setAttachLayer(piping_layer) - else +/obj/machinery/atmospherics/pipe/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/pipe_meter)) return ..() + var/obj/item/pipe_meter/meter = tool + user.dropItemToGround(meter) + meter.setAttachLayer(piping_layer) + return ITEM_INTERACT_SUCCESS + /obj/machinery/atmospherics/pipe/return_pipenet() return parent diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 11401bb3f558..0d83f675097b 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -412,22 +412,25 @@ if(internal_cell) internal_cell.forceMove(drop_location()) -/obj/machinery/portable_atmospherics/canister/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/stock_parts/power_store/cell)) - var/obj/item/stock_parts/power_store/cell/active_cell = item - if(!panel_open) - balloon_alert(user, "open hatch first!") - return TRUE - if(!user.transferItemToLoc(active_cell, src)) - return TRUE - if(internal_cell) - user.put_in_hands(internal_cell) - balloon_alert(user, "you replace the cell") - else - balloon_alert(user, "you install the cell") - internal_cell = active_cell - return TRUE - return ..() +/obj/machinery/portable_atmospherics/canister/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stock_parts/power_store/cell)) + return ..() + + var/obj/item/stock_parts/power_store/cell/active_cell = tool + if(!panel_open) + balloon_alert(user, "open hatch first!") + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(active_cell, src)) + return ITEM_INTERACT_BLOCKING + + if(internal_cell) + user.put_in_hands(internal_cell) + balloon_alert(user, "you replace the cell") + else + balloon_alert(user, "you install the cell") + internal_cell = active_cell + return ITEM_INTERACT_SUCCESS /obj/machinery/portable_atmospherics/canister/screwdriver_act(mob/living/user, obj/item/screwdriver) return default_deconstruction_screwdriver(user, screwdriver) @@ -736,8 +739,8 @@ /obj/machinery/portable_atmospherics/canister/proc/toggle_shielding(mob/user, wire_pulsed = FALSE) shielding_powered = !shielding_powered SSair.start_processing_machine(src) - message_admins("[ADMIN_LOOKUPFLW(user)] turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.") - user.investigate_log("turned [shielding_powered ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] powered shielding.", INVESTIGATE_ATMOS) + message_admins("[ADMIN_LOOKUPFLW(user)] turned [shielding_powered ? "on" : "off"][wire_pulsed ? " via wire pulse" : ""] \the [src] powered shielding.") + user.investigate_log("turned [shielding_powered ? "on" : "off"][wire_pulsed ? " via wire pulse" : ""] \the [src] powered shielding.", INVESTIGATE_ATMOS) update_appearance() /// Ejects tank from canister, if any @@ -758,8 +761,8 @@ return suppress_reactions = !suppress_reactions SSair.start_processing_machine(src) - message_admins("[ADMIN_LOOKUPFLW(user)] turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.") - user.investigate_log("turned [suppress_reactions ? "on" : "off"] [wire_pulsed ? "via wire pulse" : ""] the [src] reaction suppression.", INVESTIGATE_ATMOS) + message_admins("[ADMIN_LOOKUPFLW(user)] turned [suppress_reactions ? "on" : "off"][wire_pulsed ? "via wire pulse" : ""] \the [src] reaction suppression.") + user.investigate_log("turned [suppress_reactions ? "on" : "off"][wire_pulsed ? "via wire pulse" : ""] \the [src] reaction suppression.", INVESTIGATE_ATMOS) /obj/machinery/portable_atmospherics/canister/proc/recolor(datum/greyscale_modify_menu/menu) set_greyscale(menu.split_colors, menu.config.type) diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 7926b5b4173c..798e1c45fe5d 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -271,10 +271,14 @@ update_appearance() return TRUE -/obj/machinery/portable_atmospherics/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/tank)) - return replace_tank(user, FALSE, item) - return ..() +/obj/machinery/portable_atmospherics/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/tank)) + return NONE + + if(!replace_tank(user, FALSE, tool)) + return ITEM_INTERACT_BLOCKING + + return ITEM_INTERACT_SUCCESS /obj/machinery/portable_atmospherics/wrench_act(mob/living/user, obj/item/wrench) if(machine_stat & BROKEN) diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index 9330d6c23514..b608a135eca9 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -66,19 +66,22 @@ var/turf/epicentre = get_turf(src) if(isopenturf(epicentre)) - scrub(epicentre.return_air()) + if(scrub(epicentre.return_air())) + epicentre.air_update_turf(FALSE, FALSE) for(var/turf/open/openturf as anything in epicentre.get_atmos_adjacent_turfs(alldir = TRUE)) - scrub(openturf.return_air()) + if(scrub(openturf.return_air())) + openturf.air_update_turf(FALSE, FALSE) return ..() /** * Called in process_atmos(), handles the scrubbing of the given gas_mixture * Arguments: * * mixture: the gas mixture to be scrubbed + * Returns: TRUE if anything was scrubbed, FALSE otherwise */ /obj/machinery/portable_atmospherics/scrubber/proc/scrub(datum/gas_mixture/environment) if(air_contents.return_pressure() >= overpressure_m * ONE_ATMOSPHERE) - return + return FALSE var/list/cached_moles = environment.moles @@ -91,20 +94,22 @@ var/removal_ratio = min(1, volume_rate / environment.volume) var/total_moles_to_remove = 0 - for(var/gas_id in cached_moles & scrubbing) - total_moles_to_remove += cached_moles[gas_id] + for(var/gas_id, value in cached_moles & scrubbing) + total_moles_to_remove += value if(!total_moles_to_remove)//no gases to remove return FALSE - for(var/gas_id in cached_moles & scrubbing) - var/transferred_moles = max(QUANTIZE(cached_moles[gas_id] * removal_ratio * (cached_moles[gas_id] / total_moles_to_remove)), min(MOLAR_ACCURACY*1000, cached_moles[gas_id])) + for(var/gas_id, value in cached_moles & scrubbing) + var/transferred_moles = max(QUANTIZE(value * removal_ratio * (value / total_moles_to_remove)), min(MOLAR_ACCURACY*1000, value)) filtered_out.moles[gas_id] += transferred_moles cached_moles[gas_id] -= transferred_moles + environment.garbage_collect() //Remix the resulting gases air_contents.merge(filtered_out) + return TRUE /obj/machinery/portable_atmospherics/scrubber/emp_act(severity) . = ..() @@ -229,7 +234,8 @@ if(!holding) var/turf/T = get_turf(src) for(var/turf/AT in T.get_atmos_adjacent_turfs(alldir = TRUE)) - scrub(AT.return_air()) + if(scrub(AT.return_air())) + AT.air_update_turf(FALSE, FALSE) return ..() diff --git a/code/modules/awaymissions/mission_code/Cabin.dm b/code/modules/awaymissions/mission_code/Cabin.dm index a4d3358a120c..d3b8b10fa132 100644 --- a/code/modules/awaymissions/mission_code/Cabin.dm +++ b/code/modules/awaymissions/mission_code/Cabin.dm @@ -50,17 +50,19 @@ active = FALSE toggleFirepit() -/obj/structure/firepit/attackby(obj/item/W,mob/living/user,list/modifiers) - if(!active) - var/msg = W.ignition_effect(src, user) - if(msg) - active = TRUE - visible_message(msg) - toggleFirepit() - else - return ..() - else - W.fire_act() +/obj/structure/firepit/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(active) + tool.fire_act() + return ITEM_INTERACT_SUCCESS + + var/msg = tool.ignition_effect(src, user) + if(!msg) + return NONE + + visible_message(msg) + toggleFirepit() + return ITEM_INTERACT_SUCCESS + /obj/structure/firepit/proc/toggleFirepit() active = !active diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm index 2fa38219a1f0..38f8ae99aa5e 100644 --- a/code/modules/awaymissions/super_secret_room.dm +++ b/code/modules/awaymissions/super_secret_room.dm @@ -88,8 +88,9 @@ speaking = FALSE times_spoken_to++ -/obj/structure/speaking_tile/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - return interact(user) +/obj/structure/speaking_tile/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + interact(user) + return ITEM_INTERACT_SUCCESS /obj/structure/speaking_tile/attack_paw(mob/user, list/modifiers) return interact(user) diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm index 7302a9fd4168..922e63e1528f 100644 --- a/code/modules/basketball/hoop.dm +++ b/code/modules/basketball/hoop.dm @@ -90,27 +90,28 @@ scoreboard.add_overlay(tens_overlay) scoreboard.add_overlay(emissive_tens_overlay) -/obj/structure/hoop/attackby(obj/item/ball, mob/living/baller, list/modifiers, list/attack_modifiers) - if(!baller.can_perform_action(src, NEED_HANDS|FORBID_TELEKINESIS_REACH)) - return // TK users aren't allowed to dunk +/obj/structure/hoop/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!user.can_perform_action(src, NEED_HANDS|FORBID_TELEKINESIS_REACH)) + return NONE// TK users aren't allowed to dunk - if(!baller.transfer_item_to_turf(ball, drop_location())) - return + if(!user.transfer_item_to_turf(tool, drop_location())) + return ITEM_INTERACT_BLOCKING - var/dunk_dir = get_dir(baller, src) + var/dunk_dir = get_dir(user, src) var/dunk_pixel_z = (dunk_dir & SOUTH) ? -16 : 16 var/dunk_pixel_w = ((dunk_dir & EAST) && 16) || ((dunk_dir & WEST) && -16) || 0 - animate(baller, pixel_w = dunk_pixel_w, pixel_z = dunk_pixel_z, time = 0.5 SECONDS, easing = BOUNCE_EASING|EASE_IN|EASE_OUT, flags = ANIMATION_PARALLEL|ANIMATION_RELATIVE) + animate(user, pixel_w = dunk_pixel_w, pixel_z = dunk_pixel_z, time = 0.5 SECONDS, easing = BOUNCE_EASING|EASE_IN|EASE_OUT, flags = ANIMATION_PARALLEL|ANIMATION_RELATIVE) animate(pixel_w = -dunk_pixel_w, pixel_z = -dunk_pixel_z, time = 0.5 SECONDS, flags = ANIMATION_RELATIVE) - visible_message(span_warning("[baller] dunks [ball] into \the [src]!")) - baller.add_mood_event("basketball", /datum/mood_event/basketball_dunk) - score(ball, baller, 2) + visible_message(span_warning("[user] dunks [tool] into \the [src]!")) + user.add_mood_event("basketball", /datum/mood_event/basketball_dunk) + score(tool, user, 2) - if(istype(ball, /obj/item/toy/basketball)) - baller.adjust_stamina_loss(STAMINA_COST_DUNKING) + if(istype(tool, /obj/item/toy/basketball)) + user.adjust_stamina_loss(STAMINA_COST_DUNKING) + return ITEM_INTERACT_SUCCESS /obj/structure/hoop/attack_hand(mob/living/baller, list/modifiers) . = ..() diff --git a/code/modules/bitrunning/antagonists/netguardian.bt.json b/code/modules/bitrunning/antagonists/netguardian.bt.json new file mode 100644 index 000000000000..794640d5a971 --- /dev/null +++ b/code/modules/bitrunning/antagonists/netguardian.bt.json @@ -0,0 +1,110 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/netguardian", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_NETGUARDIAN_ROCKET_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/avoid_friendly_fire", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "time_between_perform": "1 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/bitrunning/antagonists/netguardian.dm b/code/modules/bitrunning/antagonists/netguardian.dm index 80bba1c050a6..d05d249733f7 100644 --- a/code/modules/bitrunning/antagonists/netguardian.dm +++ b/code/modules/bitrunning/antagonists/netguardian.dm @@ -113,29 +113,10 @@ return TRUE /datum/ai_controller/basic_controller/netguardian + behavior_tree_json = "code/modules/bitrunning/antagonists/netguardian.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/simple_find_wounded_target, - /datum/ai_planning_subtree/targeted_mob_ability/fire_rockets, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/netguardian, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/netguardian - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/netguardian - -/datum/ai_behavior/basic_ranged_attack/netguardian - action_cooldown = 1 SECONDS - avoid_friendly_fire = TRUE -/datum/ai_planning_subtree/targeted_mob_ability/fire_rockets - ability_key = BB_NETGUARDIAN_ROCKET_ABILITY - finish_planning = FALSE diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm index 3e1c9e18b231..a25044ccc2c3 100644 --- a/code/modules/bitrunning/components/avatar_connection.dm +++ b/code/modules/bitrunning/components/avatar_connection.dm @@ -193,7 +193,7 @@ old_body.apply_damage(damage, damage_type, zone, blocked, wound_bonus = CANT_WOUND) - if(old_body.stat > SOFT_CRIT) // KO! + if(IS_UNCONSCIOUS(old_body)) // KO! full_avatar_disconnect(cause_damage = TRUE) nohit = FALSE diff --git a/code/modules/bitrunning/netpod/utils.dm b/code/modules/bitrunning/netpod/utils.dm index 3cb7d53e5ac9..e2d2ad8593f6 100644 --- a/code/modules/bitrunning/netpod/utils.dm +++ b/code/modules/bitrunning/netpod/utils.dm @@ -91,7 +91,7 @@ return var/mob/living/carbon/current_avatar = avatar_ref?.resolve() - if(isnull(current_avatar) || current_avatar.stat != CONSCIOUS) // We need a viable avatar + if(isnull(current_avatar) || IS_UNCONSCIOUS_OR_CRIT(current_avatar)) // We need a viable avatar current_avatar = server.start_new_connection(neo, netsuit) if(isnull(current_avatar)) balloon_alert(neo, "out of bandwidth!") @@ -150,7 +150,7 @@ return FALSE // Invalid - if(occupant != neo || isnull(neo.mind) || neo.stat > SOFT_CRIT || avatar.stat == DEAD) + if(occupant != neo || isnull(neo.mind) || IS_UNCONSCIOUS(neo) || avatar.stat == DEAD) return FALSE return TRUE diff --git a/code/modules/bitrunning/objects/hololadder.dm b/code/modules/bitrunning/objects/hololadder.dm index 1335ef8b848b..9dc22c1faf79 100644 --- a/code/modules/bitrunning/objects/hololadder.dm +++ b/code/modules/bitrunning/objects/hololadder.dm @@ -68,7 +68,7 @@ if(!our_server.domain_complete) for(var/datum/weakref/ghostrole_weakref as anything in our_server.spawned_threat_refs) var/mob/living/ghostrole = ghostrole_weakref.resolve() - if(ghostrole?.stat == CONSCIOUS && ghostrole.client && ghostrole.mind.has_antag_datum(/datum/antagonist/bitrunning_glitch)) + if(ghostrole?.stat == STABLE && ghostrole.client && ghostrole.mind.has_antag_datum(/datum/antagonist/bitrunning_glitch)) to_chat(user, span_danger("A being in the simulation is preventing your retreat. You must either complete your mission or remove the obstacle before safe exit will be possible.")) return diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm index 2d9db6c5a7d0..83a06849555a 100644 --- a/code/modules/bitrunning/server/loot.dm +++ b/code/modules/bitrunning/server/loot.dm @@ -1,10 +1,3 @@ -#define GRADE_D "D" -#define GRADE_C "C" -#define GRADE_B "B" -#define GRADE_A "A" -#define GRADE_S "S" - - /// Handles calculating rewards based on number of players, parts, threats, etc /obj/machinery/quantum_server/proc/calculate_rewards() var/rewards_base = 0.8 @@ -74,6 +67,8 @@ reward_cache.manifest = WEAKREF(certificate) reward_cache.update_appearance() + generated_domain.submit_grade(grade) + if(can_generate_tech_disk(grade)) SSblackbox.record_feedback("tally", "bitrunning_bepis_rewarded", 1, generated_domain.key) new /obj/item/disk/design_disk/bepis/remove_tech(reward_cache) @@ -159,7 +154,7 @@ var/static/list/passing_grades = list() if(!passing_grades.len) - passing_grades = list(GRADE_A,GRADE_S) + passing_grades = list(BITRUNNING_GRADE_A,BITRUNNING_GRADE_S) return generated_domain.difficulty >= BITRUNNER_DIFFICULTY_MEDIUM && (grade in passing_grades) @@ -190,18 +185,12 @@ switch(score) if(1 to 4) - return GRADE_D + return BITRUNNING_GRADE_D if(5 to 7) - return GRADE_C + return BITRUNNING_GRADE_C if(8 to 10) - return GRADE_B + return BITRUNNING_GRADE_B if(11 to 13) - return GRADE_A + return BITRUNNING_GRADE_A else - return GRADE_S - -#undef GRADE_D -#undef GRADE_C -#undef GRADE_B -#undef GRADE_A -#undef GRADE_S + return BITRUNNING_GRADE_S diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm index 9076f30a73a1..ff7e06fb4f36 100644 --- a/code/modules/bitrunning/server/map_handling.dm +++ b/code/modules/bitrunning/server/map_handling.dm @@ -237,6 +237,7 @@ qdel(creature) generated_domain.secondary_loot_generated = 0 + generated_domain.main_crate_points = 0 avatar_connection_refs.Cut() exit_turfs = list() diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm index c9c33c49f44b..54c153cf03a5 100644 --- a/code/modules/bitrunning/server/threats.dm +++ b/code/modules/bitrunning/server/threats.dm @@ -42,7 +42,7 @@ /obj/machinery/quantum_server/proc/notify_spawned_threats() for(var/datum/weakref/baddie_ref as anything in spawned_threat_refs) var/mob/living/baddie = baddie_ref.resolve() - if(isnull(baddie?.mind) || baddie.stat >= UNCONSCIOUS) + if(isnull(baddie?.mind) || IS_UNCONSCIOUS(baddie)) continue var/atom/movable/screen/alert/bitrunning/alert = baddie.throw_alert( @@ -148,7 +148,7 @@ var/mob/living/bitrunner = astype(bitrunner_ref.resolve(), /datum/component/avatar_connection)?.parent if(!bitrunner) continue - if((bitrunner.stat > CONSCIOUS) || !bitrunner.client) + if(IS_UNCONSCIOUS_OR_CRIT(bitrunner) || !bitrunner.client) continue if(island_brawl_exception) timeout *= max(5 - generated_domain.main_crate_points, 1) diff --git a/code/modules/bitrunning/virtual_domain/domains/crewman.bt.json b/code/modules/bitrunning/virtual_domain/domains/crewman.bt.json new file mode 100644 index 000000000000..7fd6dd5e23b5 --- /dev/null +++ b/code/modules/bitrunning/virtual_domain/domains/crewman.bt.json @@ -0,0 +1,118 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fake_crewman", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.json b/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.json new file mode 100644 index 000000000000..29c836e25cdd --- /dev/null +++ b/code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.json @@ -0,0 +1,118 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fake_crewman/instant_hostile", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.json b/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.json new file mode 100644 index 000000000000..d50e13f3197a --- /dev/null +++ b/code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.json @@ -0,0 +1,129 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fake_crewman/instant_hostile/ranged", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.json b/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.json new file mode 100644 index 000000000000..beaf56a6d789 --- /dev/null +++ b/code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.json @@ -0,0 +1,129 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fake_crewman/ranged", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/bitrunning/virtual_domain/domains/heretic_hunt.dm b/code/modules/bitrunning/virtual_domain/domains/heretic_hunt.dm index 77a4fcfed605..3dee62028c23 100644 --- a/code/modules/bitrunning/virtual_domain/domains/heretic_hunt.dm +++ b/code/modules/bitrunning/virtual_domain/domains/heretic_hunt.dm @@ -78,54 +78,26 @@ user.AddElement(/datum/element/rust_healing) user.add_faction(FACTION_HERETIC) -// All it does is stand there, only attacks if attacked (Manuel player) +// All it does is stand there, only attacks if attacked (Manuel player) (TODO: make them ahelp to really simulate manuel players) /datum/ai_controller/basic_controller/fake_crewman + behavior_tree_json = "code/modules/bitrunning/virtual_domain/domains/crewman.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_REINFORCEMENTS_SAY = "Help me!", + BB_CALLS_REINFORCEMENTS = TRUE, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /datum/ai_controller/basic_controller/fake_crewman/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/ranged_skirmish, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/bitrunning/virtual_domain/domains/crewman_ranged.bt.json" // Immediately tries to attack the player (Terry player) /datum/ai_controller/basic_controller/fake_crewman/instant_hostile - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/bitrunning/virtual_domain/domains/crewman_hostile.bt.json" /datum/ai_controller/basic_controller/fake_crewman/instant_hostile/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/ranged_skirmish, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/bitrunning/virtual_domain/domains/crewman_hostile_ranged.bt.json" // The actual crewmate /mob/living/basic/fake_crewman diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm index 139e46d5508a..e5d19db30aac 100644 --- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm +++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm @@ -91,6 +91,12 @@ /// The role that ghosts will get. Only used for poll text. var/spawner_role = "Antagonist" + /** + * Scorekeeping + */ + + /// The highest score grade achieved so far + var/best_grade = "None" /datum/lazy_template/virtual_domain/proc/can_view_name(scanner_tier, server_points) return difficulty < scanner_tier && cost <= server_points + 5 @@ -134,3 +140,7 @@ /// Overridable proc to be called after the map is loaded. /datum/lazy_template/virtual_domain/proc/setup_domain(list/created_atoms) return + +/datum/lazy_template/virtual_domain/proc/submit_grade(new_grade) + if(GLOB.bitrunning_grades.Find(new_grade) > GLOB.bitrunning_grades.Find(best_grade)) + best_grade = new_grade diff --git a/code/modules/capture_the_flag/ctf_game.dm b/code/modules/capture_the_flag/ctf_game.dm index e7316c12ec67..8bd7806dcc86 100644 --- a/code/modules/capture_the_flag/ctf_game.dm +++ b/code/modules/capture_the_flag/ctf_game.dm @@ -168,12 +168,17 @@ player_mob.add_traits(player_traits, CAPTURE_THE_FLAG_TRAIT) return player_mob //used in medisim_game.dm -/obj/machinery/ctf/spawner/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/ctf_flag)) - var/obj/item/ctf_flag/flag = item - if(flag.team != team) - ctf_game.capture_flag(team, user, team_span, flag) - flag.reset_flag(capture = TRUE) //This might be buggy, confirm and fix if it is. +/obj/machinery/ctf/spawner/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/ctf_flag)) + return NONE + + var/obj/item/ctf_flag/flag = tool + if(flag.team == team) + return ITEM_INTERACT_BLOCKING + + ctf_game.capture_flag(team, user, team_span, flag) + flag.reset_flag(capture = TRUE) //This might be buggy, confirm and fix if it is. + return ITEM_INTERACT_SUCCESS ///A flag used for the CTF minigame. /obj/item/ctf_flag @@ -265,14 +270,17 @@ user.set_anchored(TRUE) user.status_flags &= ~CANPUSH -/obj/item/ctf_flag/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(item, /obj/item/ctf_flag)) - return ..() +/obj/item/ctf_flag/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/ctf_flag)) + return NONE + + var/obj/item/ctf_flag/flag = tool + if(flag.team == team) + return ITEM_INTERACT_BLOCKING - var/obj/item/ctf_flag/flag = item - if(flag.team != team) - to_chat(user, span_userdanger("Take \the [initial(flag.name)] to your team's controller!")) - user.playsound_local(get_turf(user), 'sound/machines/buzz/buzz-sigh.ogg', 100, vary = FALSE, use_reverb = FALSE) + to_chat(user, span_userdanger("Take \the [initial(flag.name)] to your team's controller!")) + user.playsound_local(get_turf(user), 'sound/machines/buzz/buzz-sigh.ogg', 100, vary = FALSE, use_reverb = FALSE) + return ITEM_INTERACT_SUCCESS /obj/item/ctf_flag/dropped(mob/user) ..() @@ -364,8 +372,9 @@ scores += UNLINT("[ctf_team.team_color] - [ctf_team.points]/[ctf_game.points_to_win]\n") balloon_alert_to_viewers(scores) -/obj/machinery/ctf/control_point/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) +/obj/machinery/ctf/control_point/item_interaction(mob/living/user, obj/item/tool, list/modifiers) capture(user) + return ITEM_INTERACT_SUCCESS /obj/machinery/ctf/control_point/attack_hand(mob/user, list/modifiers) . = ..() diff --git a/code/modules/capture_the_flag/ctf_player_component.dm b/code/modules/capture_the_flag/ctf_player_component.dm index 0424fe13166b..2145e4909fc9 100644 --- a/code/modules/capture_the_flag/ctf_player_component.dm +++ b/code/modules/capture_the_flag/ctf_player_component.dm @@ -46,7 +46,7 @@ ///Dusts the player and starts a respawn countdown. /datum/component/ctf_player/proc/ctf_dust() SIGNAL_HANDLER - if(!HAS_TRAIT(player_mob, TRAIT_CRITICAL_CONDITION) && !player_mob.stat == DEAD && player_mob.client) + if(player_mob.stat != DEAD && player_mob.client) return UnregisterSignal(player_mob, list(COMSIG_MOB_AFTER_APPLY_DAMAGE, COMSIG_MOB_GHOSTIZED)) var/turf/death_turf = get_turf(player_mob) diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 269a676c3d88..0aa98f01fd98 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -143,7 +143,7 @@ description = "Commander Zot has the hots for Commander Zena. Send a shipment of geraniums - her favorite flower - and he'll happily reward you." reward = CARGO_CRATE_VALUE * 8 required_count = 3 - wanted_types = list(/obj/item/food/grown/poppy/geranium = TRUE) + wanted_types = list(/obj/item/food/grown/flower/poppy/geranium = TRUE) include_subtypes = FALSE /datum/bounty/item/assistant/poppy @@ -151,7 +151,7 @@ description = "Commander Zot really wants to sweep Security Officer Olivia off her feet. Send a shipment of Poppies - her favorite flower - and he'll happily reward you." reward = CARGO_CRATE_VALUE * 2 required_count = 3 - wanted_types = list(/obj/item/food/grown/poppy = TRUE) + wanted_types = list(/obj/item/food/grown/flower/poppy = TRUE) include_subtypes = FALSE /datum/bounty/item/assistant/potted_plants diff --git a/code/modules/cargo/exports/organs.dm b/code/modules/cargo/exports/organs.dm index 6447d7d46276..4f172afec8f6 100644 --- a/code/modules/cargo/exports/organs.dm +++ b/code/modules/cargo/exports/organs.dm @@ -8,7 +8,10 @@ /datum/export/organ/get_base_cost(obj/exported_item) // Multiply value for organs that started in a player // Unaffected by price elasticity as there's a limited amount of these in play - return round(..() * HAS_TRAIT(exported_item, TRAIT_CLIENT_STARTING_ORGAN) ? CLIENT_ORGAN_MULT : 1) + var/multiplier = 1 + if(HAS_TRAIT(exported_item, TRAIT_CLIENT_STARTING_ORGAN)) + multiplier = CLIENT_ORGAN_MULT + return round(..() * multiplier) /datum/export/organ/heart cost = CARGO_CRATE_VALUE * 0.2 //For the man who has everything and nothing. diff --git a/code/modules/cargo/markets/market_items/hostages.dm b/code/modules/cargo/markets/market_items/hostages.dm index 04bd070e6b01..e21280c4a591 100644 --- a/code/modules/cargo/markets/market_items/hostages.dm +++ b/code/modules/cargo/markets/market_items/hostages.dm @@ -25,7 +25,7 @@ )) desc += " DISCLAIMER: The offer will expire once the creature is returned to the station." if(humie_mob) - desc += "[mob.p_they(TRUE)] may be delivered handcuffed, for safety of course." + desc += "[mob.p_They()] may be delivered handcuffed, for safety of course." price = new_price RegisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE, PROC_REF(on_return_from_capture)) @@ -57,7 +57,7 @@ /datum/market_item/hostage/spawn_item(loc, datum/market_purchase/purchase) var/mob/living/mob = item UnregisterSignal(mob, COMSIG_LIVING_RETURN_FROM_CAPTURE) - if(!mob.IsUnconscious()) + if(!IS_UNCONSCIOUS(mob)) to_chat(mob, span_boldnicegreen("You have been bought back to the station. Be grateful to whoever got you out of the holding facility early.")) if(!ishuman(item)) return ..() diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm index 538d40bf24a9..323b235263fe 100644 --- a/code/modules/cargo/markets/market_items/misc.dm +++ b/code/modules/cargo/markets/market_items/misc.dm @@ -239,3 +239,14 @@ item = /obj/item/organ/cyberimp/chest/nutriment/black_market stock_max = 2 availability_prob = 60 + +/datum/market_item/misc/hexacrete + name = "Jerrycan of Hexacrete" + desc = "Need to make a blacksite in a jiffy? Skip the fuss with this 200u jerrycan of hexacrete!" + item = /obj/item/reagent_containers/cup/jerrycan/hexacrete + + price_min = CARGO_CRATE_VALUE * 3 + price_max = CARGO_CRATE_VALUE * 5 + stock_min = 2 + stock_max = 5 + availability_prob = 100 diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm index d375826ece32..5c92d42f0d22 100644 --- a/code/modules/cargo/packs/engineering.dm +++ b/code/modules/cargo/packs/engineering.dm @@ -354,3 +354,15 @@ contains = list(/obj/item/golfcart_kit = 1, /obj/item/key/golfcart = 2, /obj/item/stock_parts/power_store/cell/lead = 1) crate_name = "golf cart parts kit" crate_type = /obj/structure/closet/crate/engineering + +/datum/supply_pack/engineering/concrete_mix + name = "Concrete Mix" + desc = "Feeling lazy? Need a structure and quick? Use near-instant concrete mix! Just add water." + cost = CARGO_CRATE_VALUE * 5 + access_view = ACCESS_ENGINEERING + contains = list( + /obj/item/reagent_containers/cup/jerrycan/concrete_mix = 2, + /obj/item/reagent_containers/cup/bucket = 1, + ) + crate_name = "concrete mix crate" + crate_type = /obj/structure/closet/crate/engineering diff --git a/code/modules/cargo/packs/general.dm b/code/modules/cargo/packs/general.dm index 3586289d0e23..eb9b106633b9 100644 --- a/code/modules/cargo/packs/general.dm +++ b/code/modules/cargo/packs/general.dm @@ -145,8 +145,8 @@ cost = CARGO_CRATE_VALUE * 1.6 access_view = ACCESS_CHAPEL_OFFICE contains = list(/obj/item/clothing/under/misc/burial, - /obj/item/food/grown/harebell, - /obj/item/food/grown/poppy/geranium, + /obj/item/food/grown/flower/harebell, + /obj/item/food/grown/flower/poppy/geranium, ) crate_name = "coffin" crate_type = /obj/structure/closet/crate/coffin diff --git a/code/modules/cargo/universal_scanner.dm b/code/modules/cargo/universal_scanner.dm index 11189aa5798e..2456ccb04f96 100644 --- a/code/modules/cargo/universal_scanner.dm +++ b/code/modules/cargo/universal_scanner.dm @@ -70,32 +70,38 @@ return ITEM_INTERACT_SUCCESS return NONE -/obj/item/universal_scanner/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(scanning_mode == SCAN_SALES_TAG && isidcard(attacking_item)) - var/obj/item/card/id/potential_acc = attacking_item - if(potential_acc.registered_account) - if(payments_acc == potential_acc.registered_account) - to_chat(user, span_notice("ID card already registered.")) - return - else - payments_acc = potential_acc.registered_account - playsound(src, 'sound/machines/ping.ogg', 40, TRUE) - to_chat(user, span_notice("[src] registers the ID card. Tag a wrapped item to create a barcode.")) - else if(!potential_acc.registered_account) +/obj/item/universal_scanner/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(scanning_mode == SCAN_SALES_TAG && isidcard(tool)) + var/obj/item/card/id/potential_acc = tool + if(!potential_acc.registered_account) to_chat(user, span_warning("This ID card has no account registered!")) - return - if(istype(attacking_item, /obj/item/paper)) - if (!(paper_count >= max_paper_count)) - paper_count += PAPER_PER_SHEET - qdel(attacking_item) - if (paper_count >= max_paper_count) - paper_count = max_paper_count - to_chat(user, span_notice("[src]'s paper supply is now full.")) - return - to_chat(user, span_notice("You refill [src]'s paper supply, you have [paper_count] left.")) - else + return ITEM_INTERACT_BLOCKING + + if(payments_acc == potential_acc.registered_account) + to_chat(user, span_notice("ID card already registered.")) + return ITEM_INTERACT_BLOCKING + + payments_acc = potential_acc.registered_account + playsound(src, 'sound/machines/ping.ogg', 40, TRUE) + to_chat(user, span_notice("[src] registers the ID card. Tag a wrapped item to create a barcode.")) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/paper)) + if (paper_count >= max_paper_count) to_chat(user, span_notice("[src]'s paper supply is full.")) + return ITEM_INTERACT_BLOCKING + + paper_count += PAPER_PER_SHEET + qdel(tool) + if (paper_count >= max_paper_count) + paper_count = max_paper_count + to_chat(user, span_notice("[src]'s paper supply is now full.")) + return ITEM_INTERACT_SUCCESS + + to_chat(user, span_notice("You refill [src]'s paper supply, you have [paper_count] left.")) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/item/universal_scanner/attack_self_secondary(mob/user, modifiers) . = ..() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 5ca2b20dcc32..ef458c52aad8 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -403,23 +403,23 @@ GLOBAL_LIST_INIT(unrecommended_builds, list( if (!length(GLOB.stickybanadminexemptions)) restore_stickybans() - if (byond_version >= 512) - if (!byond_build || byond_build < 1386) - message_admins(span_adminnotice("[key_name(src)] has been detected as spoofing their byond version. Connection rejected.")) - add_system_note("Spoofed-Byond-Version", "Detected as using a spoofed byond version.") - log_suspicious_login("Failed Login: [key] - Spoofed byond version") - qdel(src) + if (!byond_build) + message_admins(span_adminnotice("[key_name(src)] has been detected as spoofing their BYOND version. Connection rejected.")) + add_system_note("Spoofed-BYOND-Version", "Detected as using a spoofed BYOND version.") + log_suspicious_login("Failed Login: [key] - Spoofed BYOND version") + qdel(src) + return - if (num2text(byond_build) in GLOB.blacklisted_builds) - log_access("Failed login: [key] - blacklisted byond version") - to_chat_immediate(src, span_userdanger("Your version of byond is blacklisted.")) - to_chat_immediate(src, span_danger("Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].")) - to_chat_immediate(src, span_danger("Please download a new version of byond. If [byond_build] is the latest, you can go to BYOND's website to download other versions.")) - if(connecting_admin) - to_chat_immediate(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions") - else - qdel(src) - return + if (num2text(byond_build) in GLOB.blacklisted_builds) + log_access("Failed login: [key] - blacklisted BYOND version") + to_chat_immediate(src, span_userdanger("Your version of BYOND is blacklisted.")) + to_chat_immediate(src, span_danger("BYOND build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].")) + to_chat_immediate(src, span_danger("Please download a new version of BYOND. If [byond_build] is the latest, you can go to BYOND's website to download other versions.")) + if(connecting_admin) + to_chat_immediate(src, "As an admin, you are being allowed to continue using this version, but please consider changing BYOND versions.") + else + qdel(src) + return if(SSinput.initialized) set_macros() @@ -439,23 +439,24 @@ GLOBAL_LIST_INIT(unrecommended_builds, list( tgui_say.initialize() + initialize_escape_menu() + if(alert_mob_dupe_login && !holder) // Notify admins if the connecting player's CID is configured to be ignored by stickybans if (SSstickyban && (computer_id in SSstickyban.ignored_cids)) message_admins("MULTIKEYING: [key_name_admin(src)] Connecting player joined with IGNORED CID [computer_id].") log_admin_private("MULTIKEYING: [key_name(src)] Connecting player joined with IGNORED CID [computer_id].") - return - - // If the CID is not ignored, notify the player with the pop-up. - var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!" - // Notify admins if the connecting player's CID is a duplicate of another player's CID - if (alert_admin_multikey) - dupe_login_message += "\nAdmins have been informed." - message_admins(span_danger("MULTIKEYING: [key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")) - log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.") - spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer - alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please. - to_chat_immediate(mob, span_danger(dupe_login_message)) + else + // If the CID is not ignored, notify the player with the pop-up. + var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!" + // Notify admins if the connecting player's CID is a duplicate of another player's CID + if (alert_admin_multikey) + dupe_login_message += "\nAdmins have been informed." + message_admins(span_danger("MULTIKEYING: [key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")) + log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.") + spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer + alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please. + to_chat_immediate(mob, span_danger(dupe_login_message)) connection_time = world.time diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm index dfcfd8d32021..acc8d12a8b42 100644 --- a/code/modules/client/preferences/middleware/jobs.dm +++ b/code/modules/client/preferences/middleware/jobs.dm @@ -65,6 +65,7 @@ var/list/departments = list() var/list/jobs = list() + var/list/jobs_sorted = list() for (var/datum/job/job as anything in SSjob.joinable_occupations) if (job.job_flags & JOB_LATEJOIN_ONLY) @@ -87,6 +88,9 @@ "color" = department_type.ui_color, // Prob shouldnt be here. ) + // Use the built in sorting of the main occupation list to keep it sorted how we want instead of asc + jobs_sorted += job.title + jobs[job.title] = list( "description" = job.description, "department" = department_name, @@ -95,6 +99,7 @@ data["departments"] = departments data["jobs"] = jobs + data["jobs_sorted"] = jobs_sorted return data diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index 446f12966dc0..5d5e01106aa2 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -1,6 +1,13 @@ /// Middleware to handle quirks /datum/preference_middleware/quirks - var/tainted = FALSE + /// Used to track whether or not we need to update changes in ui_data + var/tainted = TRUE + /// Remember what the last species we chose was, to avoid having to validate quirks again + var/previous_species_value + /// The current selected quirks, saved so we can cheaply resend them in ui_data only when necessary, without having to use expensive update_static_data() calls + var/list/cached_selected_quirks + /// The current species compatibility, saved so we can cheaply resend them in ui_data only when necessary, without having to use expensive update_static_data() calls + var/list/cached_species_compatibility action_delegations = list( "give_quirk" = PROC_REF(give_quirk), @@ -28,9 +35,11 @@ return TRUE /datum/preference_middleware/quirks/post_set_preference(mob/user, preference, value) - if(preference != "species") + if(preference != "species" || value == previous_species_value) return + tainted = TRUE + previous_species_value = value preferences.validate_quirks() /datum/preference_middleware/quirks/proc/get_species_compatibility() @@ -58,26 +67,26 @@ var/list/data = list() - data["selected_quirks"] = get_selected_quirks() data["default_quirk_balance"] = SSquirks.default_quirk_points - data["species_disallowed_quirks"] = get_species_compatibility() - data["splat_disallowed_quirks"] = get_splat_compatibility() // DARKPACK EDIT ADD - SPLATS - data["quirk_balance"] = get_quirk_balance() // DARKPACK EDIT ADD - MERITS_FLAWS - data["freebie_points"] = get_freebie_points() // DARKPACK EDIT ADD - MERITS_FLAWS - data["clan_disallowed_quirks"] = get_clan_compatibility() // DARKPACK EDIT ADD - MERITS_FLAWS return data /datum/preference_middleware/quirks/get_ui_data(mob/user) var/list/data = list() - if (tainted) + if (tainted || isnull(cached_selected_quirks)) // if one of these is null, both are null tainted = FALSE - data["selected_quirks"] = get_selected_quirks() - data["species_disallowed_quirks"] = get_species_compatibility() - data["splat_disallowed_quirks"] = get_splat_compatibility() // DARKPACK EDIT ADD - SPLATS + cached_selected_quirks = get_selected_quirks() + cached_species_compatibility = get_species_compatibility() + cached_splats_compatibility = get_splat_compatibility() // DARKPACK EDIT ADD - SPLATS + cached_clans_compatibility = get_clan_compatibility() // DARKPACK EDIT ADD - MERITS_FLAWS + + data["selected_quirks"] = cached_selected_quirks + data["species_disallowed_quirks"] = cached_species_compatibility + data["splat_disallowed_quirks"] = cached_splats_compatibility // DARKPACK EDIT ADD - SPLATS + data["clan_disallowed_quirks"] = cached_clans_compatibility // DARKPACK EDIT ADD - MERITS_FLAWS + data["quirk_balance"] = get_quirk_balance() // DARKPACK EDIT ADD - MERITS_FLAWS data["freebie_points"] = get_freebie_points() // DARKPACK EDIT ADD - MERITS_FLAWS - data["clan_disallowed_quirks"] = get_clan_compatibility() // DARKPACK EDIT ADD - MERITS_FLAWS return data @@ -114,7 +123,7 @@ /datum/preference_middleware/quirks/on_new_character(mob/user) tainted = TRUE - preferences.update_static_data(user, always_instant = TRUE) + previous_species_value = null /datum/preference_middleware/quirks/proc/give_quirk(list/params, mob/user) var/quirk_name = params["quirk"] @@ -129,12 +138,13 @@ // If the client is sending an invalid give_quirk, that means that // something went wrong with the client prediction, so we should // catch it back up to speed. + tainted = TRUE preferences.update_static_data(user, always_instant = TRUE) return TRUE preferences.all_quirks = new_quirks + tainted = TRUE preferences.character_preview_view?.update_body() - preferences.update_static_data(user, always_instant = TRUE) return TRUE @@ -153,12 +163,13 @@ // If the client is sending an invalid remove_quirk, that means that // something went wrong with the client prediction, so we should // catch it back up to speed. + tainted = TRUE preferences.update_static_data(user, always_instant = TRUE) return TRUE preferences.all_quirks = new_quirks + tainted = TRUE preferences.character_preview_view?.update_body() - preferences.update_static_data(user, always_instant = TRUE) return TRUE diff --git a/code/modules/client/preferences/spacer_born.dm b/code/modules/client/preferences/spacer_born.dm new file mode 100644 index 000000000000..cb2d78e4b16b --- /dev/null +++ b/code/modules/client/preferences/spacer_born.dm @@ -0,0 +1,20 @@ +/datum/preference/choiced/spacer_height + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "spacer_height" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/spacer_height/init_possible_values() + return assoc_to_keys(GLOB.spacer_height_choices) + +/datum/preference/choiced/spacer_height/is_accessible(datum/preferences/preferences) + return ..() && (/datum/quirk/spacer_born::name in preferences.all_quirks) + +/datum/preference/choiced/spacer_height/create_default_value() + return init_possible_values()[1] + +/datum/quirk_constant_data/spacer_height + associated_typepath = /datum/quirk/spacer_born + customization_options = list(/datum/preference/choiced/spacer_height) + +/datum/preference/choiced/spacer_height/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm index a42e047be49d..39a501cfcf9f 100644 --- a/code/modules/client/preferences/tgui.dm +++ b/code/modules/client/preferences/tgui.dm @@ -63,6 +63,13 @@ // Force it to reload either way tgui.update_static_data(client.mob) +// But no games. +/datum/preference/toggle/tgui_unlimited_windows + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "tgui_unlimited_windows" + savefile_identifier = PREFERENCE_PLAYER + default_value = FALSE + /// Light mode for tgui say /datum/preference/toggle/tgui_say_light_mode category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 230da79e6db0..99d2b66df441 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -269,6 +269,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car chat_toggles = savefile.get_entry("chat_toggles", chat_toggles) toggles = savefile.get_entry("toggles", toggles) ignoring = savefile.get_entry("ignoring", ignoring) + job_assigned_profiles = savefile.get_entry("job_assigned_profiles") // OOC commendations hearted_until = savefile.get_entry("hearted_until", hearted_until) @@ -305,6 +306,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car be_special = sanitize_be_special(SANITIZE_LIST(be_special)) key_bindings = sanitize_keybindings(key_bindings) favorite_outfits = SANITIZE_LIST(favorite_outfits) + job_assigned_profiles = SANITIZE_LIST(job_assigned_profiles) key_bindings_by_key = get_key_bindings_by_key(key_bindings) @@ -360,6 +362,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car savefile.set_entry("key_bindings", key_bindings) savefile.set_entry("hearted_until", (hearted_until > world.realtime ? hearted_until : null)) savefile.set_entry("favorite_outfits", favorite_outfits) + savefile.set_entry("job_assigned_profiles", job_assigned_profiles) savefile.save() return TRUE diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm index 7f21aa3fa236..86341bd51a09 100644 --- a/code/modules/client/verbs/suicide.dm +++ b/code/modules/client/verbs/suicide.dm @@ -69,15 +69,18 @@ GAME_VERB_HIDDEN(/mob/living, suicide, "suicide") to_chat(src, span_warning("You can't commit suicide here! You can ghost if you'd like.")) return FALSE - switch(stat) - if(CONSCIOUS) - return TRUE - if(SOFT_CRIT) - to_chat(src, span_warning("You can't commit suicide while in a critical condition!")) - if(UNCONSCIOUS, HARD_CRIT) - to_chat(src, span_warning("You need to be conscious to commit suicide!")) - if(DEAD) - to_chat(src, span_warning("You're already dead!")) + if(!IS_UNCONSCIOUS_OR_CRIT(src)) + return TRUE + + if(stat == DEAD) + to_chat(src, span_warning("You're already dead!")) + return FALSE + + if(IS_UNCONSCIOUS(src)) + to_chat(src, span_warning("You need to be conscious to commit suicide!")) + return FALSE + + to_chat(src, span_warning("You can't commit suicide while in a critical condition!")) return FALSE /// Inserts in logging and death + mind dissociation when we're fully done with ending the life of our mob, as well as adjust the health. We will disallow re-entering the body when this is called. diff --git a/code/modules/client/verbs/typing.dm b/code/modules/client/verbs/typing.dm index bef6d85a0482..7965a9b73a8f 100644 --- a/code/modules/client/verbs/typing.dm +++ b/code/modules/client/verbs/typing.dm @@ -33,7 +33,7 @@ if(!typing_indicators) return FALSE /// Special exemptions - if(isabductor(mob)) + if(HAS_MIND_TRAIT(mob, TRAIT_HIDE_THINKING_INDICATOR)) return FALSE ADD_TRAIT(mob, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) mob.create_thinking_indicator() diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index d02cce77b1b0..121080c492d3 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -23,7 +23,7 @@ GAME_VERB(/client, who, "Who", "OOC") else entry += " - Playing as [client.mob.real_name]" switch(client.mob.stat) - if(UNCONSCIOUS, HARD_CRIT) + if(HARD_CRIT) entry += " - Unconscious" if(DEAD) if(isobserver(client.mob)) diff --git a/code/modules/clothing/chameleon/chameleon_scanner.dm b/code/modules/clothing/chameleon/chameleon_scanner.dm index 6cd6e2e43ea3..3171f8cb222f 100644 --- a/code/modules/clothing/chameleon/chameleon_scanner.dm +++ b/code/modules/clothing/chameleon/chameleon_scanner.dm @@ -93,7 +93,7 @@ balloon_alert(scanner, "too far away!") return // Very short scan timer, keep you on your toes - if(!do_after(scanner, 0.5 SECONDS, scanned, hidden = TRUE)) + if(!do_after(scanner, 0.5 SECONDS, scanned, cog_icon = null)) return var/list/all_scanned_items = list() diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 33bb5b745e37..aab2bfee2955 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -23,6 +23,8 @@ var/toggle_message ///chat message when the visor is toggled up. var/alt_toggle_message + /// What level of emp protection item has + var/emp_protection = EMP_PROTECTION_NONE var/clothing_flags = NONE ///List of items that can be equipped in the suit storage slot while we're worn. @@ -104,7 +106,7 @@ qdel(src) /obj/item/clothing/attack(mob/living/target, mob/living/user, list/modifiers, list/attack_modifiers) - if(user.combat_mode || !ismoth(target) || ispickedupmob(src)) + if(user.combat_mode || !HAS_TRAIT(user, TRAIT_CLOTH_EATER) || ispickedupmob(src)) return ..() if((clothing_flags & INEDIBLE_CLOTHING) || (resistance_flags & INDESTRUCTIBLE)) return ..() @@ -366,7 +368,7 @@ . = ..() if (clothing_flags & THICKMATERIAL) .["thick"] = "Protects from most injections and sprays." - if (clothing_flags & CASTING_CLOTHES) + if (HAS_TRAIT(src, TRAIT_CASTING_CLOTHING)) .["magical"] = "Allows magical beings to cast spells when wearing [src]." if((clothing_flags & STOPSPRESSUREDAMAGE) || (visor_flags & STOPSPRESSUREDAMAGE)) .["pressure-proof"] = "Protects the wearer from extremely low or high pressure, such as vacuum of space." @@ -396,6 +398,8 @@ .["sterile"] = "Increases the speed at which reagents are administered to others by [round((1/NITRILE_GLOVES_MULTIPLIER-1)*100, 1)]%." if(TRAIT_FAST_CUFFING in clothing_traits) .["secure"] = "Increases the speed at which you apply restraints." + if(emp_protection >= EMP_PROTECTION_NONE) + .["emp resistant"] = "Reduces the effects of incoming electromagnetic pulses on the wearer." /obj/item/clothing/examine_descriptor(mob/user) return "clothing" @@ -437,21 +441,23 @@ readout += "COVERAGE" readout += "It will block [english_list(things_blocked)]." + var/list/parts_covered = list() + if(body_parts_covered & HEAD) + parts_covered += "head" + if(body_parts_covered & CHEST) + parts_covered += "torso" + if(body_parts_covered & (ARMS|HANDS)) + parts_covered += "arms" + if(body_parts_covered & (LEGS|FEET)) + parts_covered += "legs" + if(length(parts_covered)) + readout += "It covers the wearer's [english_list(parts_covered)]." + if((clothing_flags & STOPSPRESSUREDAMAGE) || (visor_flags & STOPSPRESSUREDAMAGE)) - var/list/parts_covered = list() var/output_string = "It" if(!(clothing_flags & STOPSPRESSUREDAMAGE)) output_string = "When sealed, it" - if(body_parts_covered & HEAD) - parts_covered += "head" - if(body_parts_covered & CHEST) - parts_covered += "torso" - if(body_parts_covered & (ARMS|HANDS)) - parts_covered += "arms" - if(body_parts_covered & (LEGS|FEET)) - parts_covered += "legs" - if(length(parts_covered)) - readout += "[output_string] will protect the wearer's [english_list(parts_covered)] from [span_tooltip("The extremely low pressure is the biggest danger posed by the vacuum of space.", "low pressure")]." + readout += "[output_string] will protect the wearer's bodyparts that it covers from [span_tooltip("The extremely low pressure is the biggest danger posed by the vacuum of space.", "low pressure")]." var/heat_prot switch (max_heat_protection_temperature) diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 94880650e103..ada7d9813a42 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -54,6 +54,33 @@ if(. && !user.is_holding(src) && (visor_vars_to_toggle & (VISOR_VISIONFLAGS|VISOR_INVISVIEW))) user.update_sight() +/obj/item/clothing/glasses/equipped(mob/living/user, slot) + . = ..() + if (!(slot & ITEM_SLOT_EYES)) + return + RegisterSignal(user, COMSIG_CARBON_UPDATE_SIGHT_CUTOFFS, PROC_REF(update_wearer_sight)) + if (vision_flags || invis_override || invis_view || !isnull(lighting_cutoff)) + user.update_sight() + +/obj/item/clothing/glasses/dropped(mob/living/user) + . = ..() + UnregisterSignal(user, COMSIG_CARBON_UPDATE_SIGHT_CUTOFFS) + if (vision_flags || invis_override || invis_view || !isnull(lighting_cutoff)) + user.update_sight() + +/obj/item/clothing/glasses/proc/update_wearer_sight(mob/living/carbon/source, list/new_sight_flags) + SIGNAL_HANDLER + + new_sight_flags[1] |= vision_flags + if(invis_override) + source.set_invis_see(invis_override) + else + source.set_invis_see(min(invis_view, source.see_invisible)) + if(!isnull(lighting_cutoff)) + source.lighting_cutoff = max(source.lighting_cutoff, lighting_cutoff) + if(length(color_cutoffs)) + source.lighting_color_cutoffs = blend_cutoff_colors(source.lighting_color_cutoffs, color_cutoffs) + //called when thermal glasses are emped. /obj/item/clothing/glasses/proc/thermal_overload() if(ishuman(src.loc)) @@ -245,7 +272,10 @@ /// wizard version /obj/item/clothing/glasses/eyepatch/medical/chuuni resistance_flags = FIRE_PROOF | ACID_PROOF - clothing_flags = CASTING_CLOTHES + +/obj/item/clothing/glasses/eyepatch/medical/chuuni/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_CASTING_CLOTHING, INNATE_TRAIT) /obj/item/clothing/glasses/eyepatch/medical/chuuni/equipped(mob/living/user, slot) . = ..() diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 15f299eaacf0..186155043fce 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -72,19 +72,23 @@ return FALSE // We don't want to cut dyed gloves. return TRUE -/obj/item/clothing/gloves/attackby(obj/item/tool, mob/user, list/modifiers, list/attack_modifiers) +/obj/item/clothing/gloves/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() - if(.) - return + if(ITEM_INTERACT_ANY_BLOCKER & .) + return . + if(tool.tool_behaviour != TOOL_WIRECUTTER && !tool.get_sharpness()) - return + return . + if (!can_cut_with(tool)) - return + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "cutting off fingertips...") if(!do_after(user, 3 SECONDS, target=src, extra_checks = CALLBACK(src, PROC_REF(can_cut_with), tool))) - return + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "cut fingertips off") qdel(src) user.put_in_hands(new cut_type) - return TRUE + return ITEM_INTERACT_SUCCESS diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm index 84c4238f867d..c37f0664c250 100644 --- a/code/modules/clothing/head/hat.dm +++ b/code/modules/clothing/head/hat.dm @@ -94,10 +94,8 @@ var/mob/living/carbon/human/human_user = user var/obj/item/clothing/suit/costume/bear_suit/our_suit = human_user.wear_suit - if(!our_suit || !istype(our_suit)) - return - - our_suit.make_friendly(user, src) + if(istype(our_suit)) + our_suit.make_friendly(user, src) /obj/item/clothing/head/flatcap name = "flat cap" diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 2356cb8779da..3ba411d75da7 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -45,30 +45,30 @@ . = ..() AddComponent(/datum/component/seclite_attachable, light_icon_state = "flight") -/obj/item/clothing/head/helmet/sec/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(issignaler(attacking_item)) - var/obj/item/assembly/signaler/attached_signaler = attacking_item - // There's a flashlight in us. Remove it first, or it'll be lost forever! - var/obj/item/flashlight/seclite/blocking_us = locate() in src - if(blocking_us) - to_chat(user, span_warning("[blocking_us] is in the way, remove it first!")) - return TRUE - - if(!attached_signaler.secured) - to_chat(user, span_warning("Secure [attached_signaler] first!")) - return TRUE - - to_chat(user, span_notice("You add [attached_signaler] to [src].")) - - qdel(attached_signaler) - var/obj/item/bot_assembly/secbot/secbot_frame = new(drop_location()) - var/held_index = user.is_holding(src) - qdel(src) - if (held_index) - user.put_in_hand(secbot_frame, held_index) - return TRUE - - return ..() +/obj/item/clothing/head/helmet/sec/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!issignaler(tool)) + return ..() + + var/obj/item/assembly/signaler/attached_signaler = tool + // There's a flashlight in us. Remove it first, or it'll be lost forever! + var/obj/item/flashlight/seclite/blocking_us = locate() in src + if(blocking_us) + to_chat(user, span_warning("[blocking_us] is in the way, remove it first!")) + return ITEM_INTERACT_BLOCKING + + if(!attached_signaler.secured) + to_chat(user, span_warning("Secure [attached_signaler] first!")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You add [attached_signaler] to [src].")) + + qdel(attached_signaler) + var/obj/item/bot_assembly/secbot/secbot_frame = new(drop_location()) + var/held_index = user.is_holding(src) + qdel(src) + if (held_index) + user.put_in_hand(secbot_frame, held_index) + return ITEM_INTERACT_SUCCESS /obj/item/clothing/head/helmet/sec/attack_self(mob/user) . = ..() diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 3120b0874dfd..74d500cf3d6b 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -328,28 +328,31 @@ return . -/obj/item/clothing/head/fedora/inspector_hat/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) +/obj/item/clothing/head/fedora/inspector_hat/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() + if(ITEM_INTERACT_ANY_BLOCKER & .) + return . if(LAZYLEN(contents) >= max_items) balloon_alert(user, "full!") - return - if(item.w_class > max_weight) + return ITEM_INTERACT_BLOCKING + + if(tool.w_class > max_weight) balloon_alert(user, "too big!") - return + return ITEM_INTERACT_BLOCKING var/desired_phrase = tgui_input_text(user, "What is the activation phrase?", "Activation phrase", "gadget", max_length = 26) if(!desired_phrase || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return + return ITEM_INTERACT_BLOCKING - if(item.loc != user || !user.transferItemToLoc(item, src)) - return + if(tool.loc != user || !user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING - to_chat(user, span_notice("You install [item] into the [thtotext(contents.len)] slot of [src].")) + to_chat(user, span_notice("You install [tool] into the [thtotext(contents.len)] slot of [src].")) playsound(src, 'sound/machines/click.ogg', 30, TRUE) - set_phrase(desired_phrase,item) + set_phrase(desired_phrase, tool) - return TRUE + return ITEM_INTERACT_SUCCESS /obj/item/clothing/head/fedora/inspector_hat/attack_self(mob/user) . = ..() diff --git a/code/modules/clothing/head/perceptomatrix.dm b/code/modules/clothing/head/perceptomatrix.dm index 242cbc08cb18..c806dbc7bc96 100644 --- a/code/modules/clothing/head/perceptomatrix.dm +++ b/code/modules/clothing/head/perceptomatrix.dm @@ -1,7 +1,3 @@ - -#define PERCEPTOMATRIX_INACTIVE_FLAGS SNUG_FIT|STACKABLE_HELMET_EXEMPT|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT -#define PERCEPTOMATRIX_ACTIVE_FLAGS PERCEPTOMATRIX_INACTIVE_FLAGS|CASTING_CLOTHES // we love casting spells - /// Helmet which can turn you into a BEAST!! once an anomaly core is inserted /obj/item/clothing/head/helmet/perceptomatrix name = "perceptomatrix helm" @@ -18,7 +14,7 @@ heat_protection = HEAD max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT strip_delay = 8 SECONDS - clothing_flags = PERCEPTOMATRIX_ACTIVE_FLAGS + clothing_flags = SNUG_FIT|STACKABLE_HELMET_EXEMPT|STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT flags_cover = HEADCOVERSEYES|EARS_COVERED flags_inv = HIDEHAIR|HIDEFACE flash_protect = FLASH_PROTECTION_WELDER_SENSITIVE @@ -99,7 +95,7 @@ // If the core isn't installed, or it's temporarily deactivated, disable special functions. if(!core_installed) - clothing_flags = PERCEPTOMATRIX_INACTIVE_FLAGS + REMOVE_TRAIT(src, TRAIT_CASTING_CLOTHING, INNATE_TRAIT) detach_clothing_traits(additional_clothing_traits) QDEL_LIST(active_components) RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) @@ -107,7 +103,7 @@ astype(loc, /mob/living/carbon)?.update_tint() return - clothing_flags = PERCEPTOMATRIX_ACTIVE_FLAGS + ADD_TRAIT(src, TRAIT_CASTING_CLOTHING, INNATE_TRAIT) attach_clothing_traits(additional_clothing_traits) tint = 0 astype(loc, /mob/living/carbon)?.update_tint() @@ -239,6 +235,3 @@ cast_on.set_eye_blur_if_lower(eye_blur_duration) cast_on.adjust_staggered(stagger_duration) cast_on.apply_status_effect(/datum/status_effect/hallucination/perceptomatrix, hallucination_duration, HALLUCINATION_TIER_RARE) - -#undef PERCEPTOMATRIX_INACTIVE_FLAGS -#undef PERCEPTOMATRIX_ACTIVE_FLAGS diff --git a/code/modules/clothing/head/tinfoilhat.dm b/code/modules/clothing/head/tinfoilhat.dm index 8f30e96e2d26..04e3bb0d4364 100644 --- a/code/modules/clothing/head/tinfoilhat.dm +++ b/code/modules/clothing/head/tinfoilhat.dm @@ -11,6 +11,7 @@ var/datum/brain_trauma/mild/phobia/conspiracies/paranoia var/warped = FALSE interaction_flags_mouse_drop = NEED_HANDS + emp_protection = EMP_PROTECTION_MODERATE /datum/armor/costume_foilhat laser = -5 @@ -74,7 +75,7 @@ if(target.get_item_by_slot(ITEM_SLOT_HEAD) != src) return QDEL_NULL(paranoia) - if(target.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(target)) to_chat(target, span_warning("Your zealous conspirationism rapidly dissipates as the donned hat warps up into a ruined mess. All those theories starting to sound like nothing but a ridicolous fanfare.")) /obj/item/clothing/head/costume/foilhat/attack_hand(mob/user, list/modifiers) diff --git a/code/modules/clothing/head/tophat.dm b/code/modules/clothing/head/tophat.dm index 2b587287aa2e..eae7766e5485 100644 --- a/code/modules/clothing/head/tophat.dm +++ b/code/modules/clothing/head/tophat.dm @@ -10,10 +10,12 @@ /// Cooldown for how often we can pull rabbits out of here COOLDOWN_DECLARE(rabbit_cooldown) -/obj/item/clothing/head/hats/tophat/attackby(obj/item/hitby_item, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(istype(hitby_item, /obj/item/gun/magic/wand)) - abracadabra(hitby_item, user) +/obj/item/clothing/head/hats/tophat/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/gun/magic/wand)) + return ..() + + abracadabra(tool, user) + return ITEM_INTERACT_SUCCESS /obj/item/clothing/head/hats/tophat/proc/abracadabra(obj/item/hitby_wand, mob/magician) if(!COOLDOWN_FINISHED(src, rabbit_cooldown)) diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 9e29f0834c1d..28170db5b3ef 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -95,11 +95,10 @@ GLOBAL_LIST_INIT(clown_mask_options, list( var/mob/wearer = loc wearer.update_worn_mask() -/obj/item/clothing/mask/gas/attackby(obj/item/tool, mob/user) +/obj/item/clothing/mask/gas/item_interaction(mob/living/user, obj/item/tool, list/modifiers) var/valid_wearer = ismob(loc) var/mob/wearer = loc if(istype(tool, /obj/item/cigarette)) - if(max_filters <= 0 || cig) balloon_alert(user, "can't hold that!") return ..() @@ -115,22 +114,26 @@ GLOBAL_LIST_INIT(clown_mask_options, list( cig.forceMove(src) if(valid_wearer) wearer.update_worn_mask() - return TRUE + return ITEM_INTERACT_SUCCESS if(cig) - var/cig_attackby = cig.attackby(tool, user) + var/cig_interaction = cig.item_interaction(user, tool) if(valid_wearer) wearer.update_worn_mask() - return cig_attackby + return cig_interaction + if(!istype(tool, /obj/item/gas_filter)) return ..() + if(LAZYLEN(gas_filters) >= max_filters) return ..() + if(!user.transferItemToLoc(tool, src)) return ..() + LAZYADD(gas_filters, tool) has_filter = TRUE - return TRUE + return ITEM_INTERACT_SUCCESS /obj/item/clothing/mask/gas/attack_hand_secondary(mob/user, list/modifiers) if(cig) @@ -497,7 +500,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list( if(!choice) return FALSE - if(src && choice && !M.stat && in_range(M,src)) + if(src && choice && !IS_UNCONSCIOUS_OR_CRIT(M) && in_range(M,src)) icon_state = options[choice] user.update_worn_mask() update_item_action_buttons() diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 4cdb37c697b8..a998d6db29ae 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -148,7 +148,7 @@ /obj/item/clothing/neck/tie/worn_overlays(mutable_appearance/standing, isinhands, icon_file, bodyshape = NONE) . = ..() var/mob/living/carbon/human/wearer = loc - if(!ishuman(wearer) || !wearer.w_uniform) + if(!ishuman(wearer)) return var/obj/item/clothing/under/undershirt = wearer.w_uniform if(!istype(undershirt) || !LAZYLEN(undershirt.attached_accessories)) @@ -328,7 +328,7 @@ render_list += "You can't feel anything where [target.p_their()] appendix would be.\n" appendix_okay = FALSE else - if(appendix.damage > 10 && carbon_patient.stat == CONSCIOUS) + if(appendix.damage > 10 && !IS_UNCONSCIOUS_OR_CRIT(carbon_patient)) render_list += "[target] screams when you lift your hand from [target.p_their()] appendix!\n"//scream if their appendix is damaged and they're awake target.emote("scream") appendix_okay = FALSE diff --git a/code/modules/clothing/neck/collar_bomb.dm b/code/modules/clothing/neck/collar_bomb.dm index 10450f53c94e..5d3c598fb2d4 100644 --- a/code/modules/clothing/neck/collar_bomb.dm +++ b/code/modules/clothing/neck/collar_bomb.dm @@ -38,12 +38,13 @@ return . += span_tinynotice("It has a [EXAMINE_HINT("wire")] panel that could be interacted with...") -/obj/item/clothing/neck/collar_bomb/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(is_wire_tool(item)) - wires.interact(user) - else +/obj/item/clothing/neck/collar_bomb/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!is_wire_tool(tool)) return ..() + wires.interact(user) + return ITEM_INTERACT_SUCCESS + /obj/item/clothing/neck/collar_bomb/equipped(mob/user, slot, initial = FALSE) . = ..() if(slot == ITEM_SLOT_NECK) diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index f1772f9df41d..489136234c59 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -211,7 +211,7 @@ if(HAS_TRAIT(user, TRAIT_CLUMSY)) // based clowns trained their whole lives for this mod_time *= 0.75 - if(do_after(user, mod_time, target = our_guy, extra_checks = CALLBACK(src, PROC_REF(still_shoed), our_guy), hidden = TRUE)) + if(do_after(user, mod_time, target = our_guy, extra_checks = CALLBACK(src, PROC_REF(still_shoed), our_guy), cog_icon = null)) to_chat(user, span_notice("You [tied ? "un[fasten_verb()]" : "knot"] the [fastening_type] on [loc]'s [src.name].")) if(tied == SHOES_UNTIED) adjust_laces(SHOES_KNOTTED, user) diff --git a/code/modules/clothing/shoes/sneakers.dm b/code/modules/clothing/shoes/sneakers.dm index 1faed5d2eef0..4e82da67cd41 100644 --- a/code/modules/clothing/shoes/sneakers.dm +++ b/code/modules/clothing/shoes/sneakers.dm @@ -157,16 +157,11 @@ return return ..() -/obj/item/clothing/shoes/sneakers/orange/pre_attack(atom/movable/attacking_movable, mob/living/user, list/modifiers, list/attack_modifiers) - if(attached_cuffs || attacking_movable.type != /obj/item/restraints/handcuffs) +/obj/item/clothing/shoes/sneakers/orange/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(attached_cuffs || tool.type != /obj/item/restraints/handcuffs) // Note: not using istype here because we want to ignore all subtypes return ..() - attacking_movable.forceMove(src) - return TRUE - -/obj/item/clothing/shoes/sneakers/orange/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(attached_cuffs || attacking_item.type != /obj/item/restraints/handcuffs) // Note: not using istype here because we want to ignore all subtypes - return ..() - attacking_item.forceMove(src) + tool.forceMove(src) + return ITEM_INTERACT_SUCCESS /obj/item/clothing/shoes/sneakers/orange/can_mob_unequip(mob/user) if(user.get_item_by_slot(slot_flags) == src && attached_cuffs) diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm index 4659c27100e2..33095e716497 100644 --- a/code/modules/clothing/spacesuits/_spacesuits.dm +++ b/code/modules/clothing/spacesuits/_spacesuits.dm @@ -242,17 +242,20 @@ return ITEM_INTERACT_SUCCESS // object handling for accessing features of the suit -/obj/item/clothing/suit/space/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(!cell_cover_open || !istype(I, /obj/item/stock_parts/power_store/cell)) +/obj/item/clothing/suit/space/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!cell_cover_open || !istype(tool, /obj/item/stock_parts/power_store/cell)) return ..() + if(cell) to_chat(user, span_warning("[src] already has a cell installed.")) - return - if(user.transferItemToLoc(I, src)) - cell = I - to_chat(user, span_notice("You successfully install \the [cell] into [src].")) - update_hud_icon(user) - return + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + cell = tool + to_chat(user, span_notice("You successfully install \the [cell] into [src].")) + update_hud_icon(user) + return ITEM_INTERACT_SUCCESS /// Open the cell cover when ALT+Click on the suit /obj/item/clothing/suit/space/click_alt(mob/living/user) diff --git a/code/modules/clothing/spacesuits/softsuit.dm b/code/modules/clothing/spacesuits/softsuit.dm index d6cb7bf0c898..3caf9ec0a55a 100644 --- a/code/modules/clothing/spacesuits/softsuit.dm +++ b/code/modules/clothing/spacesuits/softsuit.dm @@ -53,19 +53,19 @@ . = ..() . += span_notice("You can start constructing a critter sized mecha with a [span_bold("cyborg leg")].") -/obj/item/clothing/head/helmet/space/eva/attackby(obj/item/attacked_with, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(.) - return - if(!istype(attacked_with, /obj/item/bodypart/leg/left/robot) && !istype(attacked_with, /obj/item/bodypart/leg/right/robot)) - return +/obj/item/clothing/head/helmet/space/eva/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/bodypart/leg/left/robot) && !istype(tool, /obj/item/bodypart/leg/right/robot)) + return ..() + if(ismob(loc)) user.balloon_alert(user, "drop the helmet first!") - return + return ITEM_INTERACT_BLOCKING + user.balloon_alert(user, "leg attached") new /obj/item/bot_assembly/vim(loc) - qdel(attacked_with) + qdel(tool) qdel(src) + return ITEM_INTERACT_SUCCESS //Emergency suit /obj/item/clothing/head/helmet/space/fragile diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm index 97c622d8e902..c6ebbea73f0d 100644 --- a/code/modules/clothing/suits/reactive_armour.dm +++ b/code/modules/clothing/suits/reactive_armour.dm @@ -233,6 +233,7 @@ cooldown_message = span_danger("The tesla capacitors on the reactive tesla armor are still recharging! The armor merely emits some sparks.") emp_message = span_warning("The tesla capacitors beep ominously for a moment.") clothing_traits = list(TRAIT_TESLA_SHOCKIMMUNE) + emp_protection = EMP_PROTECTION_MODERATE /// How strong are the zaps we give off? var/zap_power = 2.5e4 /// How far to the zaps we give off go? diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index 0bfc528dff1c..f1a3702c4802 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -386,6 +386,7 @@ // Remove it from the list before detaching LAZYREMOVE(attached_accessories, removed) removed.detach(src, update) + update_appearance() /// Get a list of all accessory overlays /obj/item/clothing/under/proc/get_accessory_overlays() diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index bfdf9afdfb2b..13fa9a655b31 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -211,6 +211,10 @@ preview_name = "Lesbian Pride" new_icon_state = "pride_lesbian" +/datum/atom_skin/pride_pin/mlm + preview_name = "Gay Pride" + new_icon_state = "pride_gay" + /obj/item/clothing/accessory/pride name = "pride pin" desc = "A Nanotrasen Diversity & Inclusion Center-sponsored holographic pin to show off your pride, reminding the crew of their unwavering commitment to equity, diversity, and inclusion!" diff --git a/code/modules/deathmatch/deathmatch_modifier.dm b/code/modules/deathmatch/deathmatch_modifier.dm index 53e468a10265..96d2f281ed9b 100644 --- a/code/modules/deathmatch/deathmatch_modifier.dm +++ b/code/modules/deathmatch/deathmatch_modifier.dm @@ -194,7 +194,6 @@ /datum/deathmatch_modifier/xray/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby) ADD_TRAIT(player, TRAIT_XRAY_VISION, DEATHMATCH_TRAIT) - player.update_sight() /datum/deathmatch_modifier/thermal name = "Thermal Vision" @@ -203,7 +202,6 @@ /datum/deathmatch_modifier/thermal/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby) ADD_TRAIT(player, TRAIT_THERMAL_VISION, DEATHMATCH_TRAIT) - player.update_sight() /datum/deathmatch_modifier/regen name = "Health Regen" diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index bb1285c11a54..1c6f1dda4a22 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -22,7 +22,7 @@ /obj/item/detective_scanner/interact(mob/user) . = ..() - if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) + if(IS_UNCONSCIOUS_OR_CRIT(user) || !user.can_read(src) || user.is_blind()) return ITEM_INTERACT_BLOCKING ui_interact(user) return ITEM_INTERACT_SUCCESS diff --git a/code/modules/escape_menu/admin_page.dm b/code/modules/escape_menu/admin_page.dm deleted file mode 100644 index 3eeacf70e935..000000000000 --- a/code/modules/escape_menu/admin_page.dm +++ /dev/null @@ -1,83 +0,0 @@ -/datum/escape_menu/proc/show_admin_page() - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/lobby_button/small( - null, - /* hud_owner = */ null, - "Back", - /* tooltip_text = */ null, - /* button_screen_loc = */ "TOP:-30,LEFT:30", - CALLBACK(src, PROC_REF(open_home_page)), - /* button_overlay = */ "back", - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/admin_help( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Create Admin Ticket", - /* offset = */ list(-136, 28), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(create_ticket)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "View Latest Ticket", - /* offset = */ list(-171, 28), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(view_latest_ticket)), - ) - ) - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Pray", - /* offset = */ list(-206, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(pray)), - ) - ) - - if(CONFIG_GET(flag/see_own_notes)) - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "See Notes", - /* offset = */ list(-241, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(see_notes)), - ) - ) - -/datum/escape_menu/proc/create_ticket() - if(!(/client/verb/adminhelp in client?.verbs)) - return - client?.adminhelp() - qdel(src) - -///Opens your latest admin ticket. -/datum/escape_menu/proc/view_latest_ticket() - client?.view_latest_ticket() - -///Manually calls the user's pray() hotkey (which is where prefs is taken into account). -/datum/escape_menu/proc/pray() - var/datum/keybinding/client/communication/pray/pray_verb = GLOB.keybindings_by_name[/datum/keybinding/client/communication/pray::name] - pray_verb.down(client) - qdel(src) - -/datum/escape_menu/proc/see_notes() - if(!CONFIG_GET(flag/see_own_notes)) - to_chat(client.mob, span_notice("Seeing notes has been disabled on this server.")) - return - browse_messages(null, client.ckey, null, TRUE) - qdel(src) diff --git a/code/modules/escape_menu/details.dm b/code/modules/escape_menu/details.dm deleted file mode 100644 index 9a507d6dbd80..000000000000 --- a/code/modules/escape_menu/details.dm +++ /dev/null @@ -1,17 +0,0 @@ -/atom/movable/screen/escape_menu/details - screen_loc = "EAST:-175,NORTH:-40" - maptext_height = 100 - maptext_width = 200 - -/atom/movable/screen/escape_menu/details/proc/update_text(client/client_owner) - var/new_maptext = {" - - Round ID: [GLOB.round_id || "Unset"]
    - Server Time: [server_timestamp(format = "hh:mm:ss", twelve_hour_clock = client_owner.prefs.read_preference(/datum/preference/toggle/twelve_hour))]
    - Round Time: [(SSticker.round_start_time == 0) ? "Pre-Game" : round_timestamp()]
    - Map: [SSmapping.current_map.return_map_name(webmap_included = TRUE) || "Loading..."]
    - Time Dilation: [round(SStime_track.time_dilation_current, 1)]%
    -
    - "} // DARKPACK EDIT CHANGE - CITY_TIME - - maptext = MAPTEXT(new_maptext) diff --git a/code/modules/escape_menu/escape_menu.dm b/code/modules/escape_menu/escape_menu.dm index 22b43d10c972..a710a64537d0 100644 --- a/code/modules/escape_menu/escape_menu.dm +++ b/code/modules/escape_menu/escape_menu.dm @@ -1,175 +1,266 @@ -GLOBAL_LIST_EMPTY(escape_menus) +GLOBAL_VAR(escape_menu_suicide_icon_base64) -/// Opens the escape menu. -/// Verb, hardcoded to Escape, set in the client skin. -GAME_VERB_HIDDEN(/client, open_escape_menu, "Open Escape Menu") - var/current_escape_menu = GLOB.escape_menus[ckey] - if (!isnull(current_escape_menu)) - qdel(current_escape_menu) +/proc/generate_escape_menu_suicide_icon() + set waitfor = FALSE + if(!isnull(GLOB.escape_menu_suicide_icon_base64)) return + GLOB.escape_menu_suicide_icon_base64 = "" + UNTIL(MC_RUNNING(SSatoms.init_stage)) + var/mob/living/carbon/human/consistent/clown = new + clown.equipOutfit(/datum/outfit/job/clown) + var/icon/clown_icon = getFlatIcon(clown) + clown_icon.Turn(90) + qdel(clown) + GLOB.escape_menu_suicide_icon_base64 = icon2base64(clown_icon) + for(var/datum/escape_menu/menu as anything in GLOB.escape_menus) + menu.send_update(list("suicideIcon" = GLOB.escape_menu_suicide_icon_base64)) - reset_held_keys() +GLOBAL_LIST_EMPTY(escape_menus) - new /datum/escape_menu(src) +/client/var/datum/escape_menu/escape_menu -#define PAGE_HOME "PAGE_HOME" -#define PAGE_ADMIN "ADMIN_PAGE" -#define PAGE_LEAVE_BODY "PAGE_LEAVE_BODY" -#define PAGE_QUIT_GAME "PAGE_QUIT_GAME" +/client/proc/initialize_escape_menu() + set waitfor = FALSE + sleep(3 SECONDS) + generate_escape_menu_suicide_icon() + escape_menu = new(src) + +GAME_VERB_HIDDEN(/client, reset_held_keys_verb, "Reset Held Keys") + reset_held_keys() /datum/escape_menu - /// The client that owns this escape menu var/client/client - /// A weakref to the hud this escape menu currently applies to - var/datum/weakref/our_hud_ref - /// The details at the top right that persists through all screens, showing round info. - var/atom/movable/screen/escape_menu/details/detail_screen + var/datum/tgui_window/window - VAR_PRIVATE - ckey - - static/atom/movable/screen/fullscreen/dimmer/dim_screen - - datum/screen_object_holder/base_holder - datum/screen_object_holder/page_holder - - atom/movable/plane_master_controller/plane_master_controller - - list/resource_panels - show_resources = FALSE - menu_page = PAGE_HOME + /// If we've already notified the user that their BYOND version is not cool with transparent browsers + var/version_warned /datum/escape_menu/New(client/client) - ASSERT(!(client.ckey in GLOB.escape_menus)) - - ckey = client?.ckey src.client = client - refresh_hud() - - base_holder = new(client) - if(isnull(dim_screen)) - dim_screen = new() - detail_screen = new() - detail_screen.update_text(client) - populate_base_ui() - - page_holder = new(client) - show_page() + window = new(client, "escape_menu") + window.is_browser = TRUE + window.initialize( + strict_mode = TRUE, + inline_css = file("tgui/public/tgui-escape-menu.bundle.css"), + inline_js = file("tgui/public/tgui-escape-menu.bundle.js"), + ) + window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/escape_menu_font)) + window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/escape_menu_sounds)) + window.send_asset(get_asset_datum(/datum/asset/spritesheet/escape_menu_icons)) + window.subscribe(src, PROC_REF(on_message)) RegisterSignal(client, COMSIG_QDELETING, PROC_REF(on_client_qdel)) RegisterSignal(client, COMSIG_CLIENT_MOB_LOGIN, PROC_REF(on_client_mob_login)) + RegisterSignals(client, list(COMSIG_CLIENT_VERB_ADDED, COMSIG_CLIENT_VERB_REMOVED), PROC_REF(on_verb_change)) + RegisterSignal(SSdcs, COMSIG_GLOB_STATION_NAME_CHANGED, PROC_REF(on_station_name_changed)) + RegisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING, PROC_REF(on_round_start)) - SEND_SOUND(client, 'modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_open.ogg') // DARKPACK EDIT CHANGE - var/sound/esc_middle = sound('modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_middle.ogg', repeat = FALSE, channel = CHANNEL_ESCAPEMENU, volume = 80) // DARKPACK EDIT CHANGE - SEND_SOUND(client, esc_middle) - - if (!isnull(ckey)) - GLOB.escape_menus[ckey] = src - - START_PROCESSING(SSescape_menu, src) + GLOB.escape_menus += src + send_init() /datum/escape_menu/Destroy(force) - // Deleted in page holder - detail_screen = null - resource_panels = null + GLOB.escape_menus -= src STOP_PROCESSING(SSescape_menu, src) - QDEL_NULL(base_holder) - QDEL_NULL(page_holder) - - var/datum/our_hud = our_hud_ref?.resolve() - if(our_hud) - REMOVE_TRAIT(our_hud, TRAIT_ESCAPE_MENU_OPEN, ref(src)) - - GLOB.escape_menus -= ckey - - var/sound/esc_clear = sound(null, repeat = FALSE, channel = CHANNEL_ESCAPEMENU) //yes, I'm doing it like this with a null, no its absolutely intentional, cuts off the sound right as needed. - SEND_SOUND(client, esc_clear) - SEND_SOUND(client, 'modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_close.ogg') // DARKPACK EDIT CHANGE + window?.unsubscribe(src) + window = null client = null - return ..() /datum/escape_menu/process(seconds_per_tick) - detail_screen.update_text(client) + send_update(list( + "serverTime" = server_timestamp(format = "hh:mm:ss"), + "shiftTime" = (SSticker.round_start_time == 0) ? "Pre-Game" : round_timestamp(), + "timeDilation" = "[round(SStime_track.time_dilation_current, 1)]", + "admins" = build_admin_list(), + "players" = build_player_list(), + "ignoredOffline" = build_ignored_offline(), + )) /datum/escape_menu/proc/on_client_qdel() SIGNAL_HANDLER - PRIVATE_PROC(TRUE) - qdel(src) /datum/escape_menu/proc/on_client_mob_login() SIGNAL_HANDLER - PRIVATE_PROC(TRUE) + send_update(list( + "canLeaveBody" = isliving(client?.mob), + )) - if (menu_page != PAGE_HOME) - qdel(src) - else - // Otherwise our client just switched bodies, let's update our hud - refresh_hud() - -/datum/escape_menu/proc/refresh_hud() - var/datum/old_hud = our_hud_ref?.resolve() - if(old_hud) - REMOVE_TRAIT(old_hud, TRAIT_ESCAPE_MENU_OPEN, ref(src)) - - var/datum/new_hud = client.mob?.hud_used - our_hud_ref = WEAKREF(new_hud) - if(new_hud) - ADD_TRAIT(new_hud, TRAIT_ESCAPE_MENU_OPEN, ref(src)) - -/datum/escape_menu/proc/show_page() - PRIVATE_PROC(TRUE) - - page_holder.clear() - - switch (menu_page) - if (PAGE_HOME) - show_home_page() - if (PAGE_ADMIN) - show_admin_page() - if (PAGE_LEAVE_BODY) - show_leave_body_page() - if(PAGE_QUIT_GAME) - show_quit_game_page() - else - CRASH("Unknown escape menu page: [menu_page]") - -/datum/escape_menu/proc/populate_base_ui() - PRIVATE_PROC(TRUE) - - base_holder.give_protected_screen_object(dim_screen) - base_holder.give_screen_object(detail_screen) - -/datum/escape_menu/proc/open_home_page() - PRIVATE_PROC(TRUE) - - menu_page = PAGE_HOME - show_page() - -/datum/escape_menu/proc/open_admin_page() - PRIVATE_PROC(TRUE) - - menu_page = PAGE_ADMIN - show_page() - -/datum/escape_menu/proc/open_leave_body() - PRIVATE_PROC(TRUE) - - menu_page = PAGE_LEAVE_BODY - show_page() - -/datum/escape_menu/proc/quit_game_prompt() - PRIVATE_PROC(TRUE) - - menu_page = PAGE_QUIT_GAME - show_page() +/datum/escape_menu/proc/on_verb_change(client/source, list/verbs_changed) + SIGNAL_HANDLER + if(/client/verb/adminhelp in verbs_changed) + send_update(list( + "canAdminHelp" = (/client/verb/adminhelp in client?.verbs), + )) -/atom/movable/screen/escape_menu - plane = ESCAPE_MENU_PLANE - clear_with_screen = FALSE +/datum/escape_menu/proc/on_station_name_changed() + SIGNAL_HANDLER + send_update(list( + "stationName" = station_name(), + )) -#undef PAGE_HOME -#undef PAGE_ADMIN -#undef PAGE_LEAVE_BODY -#undef PAGE_QUIT_GAME +/datum/escape_menu/proc/on_round_start() + SIGNAL_HANDLER + send_update(list( + "shiftTime" = round_timestamp(), + "timeDilation" = "[round(SStime_track.time_dilation_current, 1)]", + )) + +/datum/escape_menu/proc/send_init() + var/list/resources = list() + + var/githuburl = CONFIG_GET(string/githuburl) + if(githuburl) + resources += list(list("id" = "bug", "label" = "Report Bug", "tooltip" = "Report a bug/issue")) + resources += list(list("id" = "github", "label" = "Github", "tooltip" = "Open the repository for the game")) + + var/forumurl = CONFIG_GET(string/forumurl) + if(forumurl) + resources += list(list("id" = "forums", "label" = "Forums", "tooltip" = "Visit the server's forums")) + + var/rulesurl = CONFIG_GET(string/rulesurl) + if(rulesurl) + resources += list(list("id" = "rules", "label" = "Rules", "tooltip" = "View the server rules")) + + var/wikiurl = CONFIG_GET(string/wikiurl) + if(wikiurl) + resources += list(list("id" = "wiki", "label" = "Wiki", "tooltip" = "See the wiki for the game")) + + var/configurl = CONFIG_GET(string/configurl) + if(configurl) + resources += list(list("id" = "config", "label" = "Config", "tooltip" = "View the server configuration files")) + + resources += list(list("id" = "changelog", "label" = "Change Log", "tooltip" = "See all changes to the server")) + + window.send_message("init", list( + "stationName" = station_name(), + "roundId" = GLOB.round_id || "Unset", + "serverTime" = server_timestamp(format = "hh:mm:ss"), + "shiftTime" = (SSticker.round_start_time == 0) ? "Pre-Game" : round_timestamp(), + "timeDilation" = "[round(SStime_track.time_dilation_current, 1)]", + "mapName" = SSmapping.current_map?.map_name || "Loading...", + "mapFeedbackLink" = SSmapping.current_map?.feedback_link, + "canLeaveBody" = isliving(client?.mob), + "canAdminHelp" = (/client/verb/adminhelp in client?.verbs), + "canSeeNotes" = CONFIG_GET(flag/see_own_notes), + "hasTicketNotification" = !isnull(client?.current_ticket) && !client.current_ticket.player_replied, + "admins" = build_admin_list(), + "players" = build_player_list(), + "ignoredOffline" = build_ignored_offline(), + "resources" = resources, + "suicideIcon" = GLOB.escape_menu_suicide_icon_base64, + )) + +/datum/escape_menu/proc/build_admin_list() + var/list/result = list() + for(var/client/admin as anything in GLOB.admins) + result += list(list( + "ckey" = admin.ckey, + "displayName" = admin.holder?.fakekey || admin.ckey, + "rank" = admin.holder?.rank_names(), + "feedbackLink" = admin.holder?.feedback_link(), + "ping" = round(admin.avgping, 1), + "ignored" = (admin.ckey in client?.prefs?.ignoring), + "isSelf" = (admin.ckey == client?.ckey), + )) + return result + +/datum/escape_menu/proc/build_player_list() + var/list/result = list() + for(var/client/player as anything in GLOB.clients - GLOB.admins) + result += list(list( + "ckey" = player.ckey, + "displayName" = player.ckey, + "ping" = round(player.avgping, 1), + "ignored" = (player.ckey in client?.prefs?.ignoring), + "isSelf" = (player.ckey == client?.ckey), + )) + return result + +/datum/escape_menu/proc/build_ignored_offline() + var/list/result = list() + if(client?.prefs?.ignoring) + for(var/ignored_key in client.prefs.ignoring) + if(!(ignored_key in GLOB.directory)) + result += ignored_key + return result + +/datum/escape_menu/proc/send_update(list/data) + window.send_message("state", data) + +/datum/escape_menu/proc/on_message(type, payload, href_list) + if(type == "ready") + send_init() + return TRUE + + if(type != "action") + return FALSE + + var/action = payload["action"] + switch(action) + if("opened") + if(!version_warned && client.byond_build < 1680) + to_chat(client, span_warning("Your BYOND version is not up-to-date enough to render the escape menu, please update to 516.1680 or higher.")) + version_warned = TRUE + + START_PROCESSING(SSescape_menu, src) + if("closed") + STOP_PROCESSING(SSescape_menu, src) + if("character") + client?.prefs.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES + client?.prefs.update_static_data(client?.mob) + client?.prefs.ui_interact(client?.mob) + if("settings") + client?.prefs.current_window = PREFERENCE_TAB_GAME_PREFERENCES + client?.prefs.update_static_data(client?.mob) + client?.prefs.ui_interact(client?.mob) + if("create_ticket") + if(!(/client/verb/adminhelp in client?.verbs)) + return TRUE + client?.adminhelp() + if("view_ticket") + client?.view_latest_ticket() + if("pray") + var/datum/keybinding/client/communication/pray/pray_verb = GLOB.keybindings_by_name[/datum/keybinding/client/communication/pray::name] + pray_verb.down(client) + if("see_notes") + if(!CONFIG_GET(flag/see_own_notes)) + to_chat(client.mob, span_notice("Seeing notes has been disabled on this server.")) + return TRUE + browse_messages(null, client.ckey, null, TRUE) + if("ghost") + var/mob/living/living_user = client?.mob + living_user?.ghost() + if("suicide") + var/mob/living/carbon/human/human_user = client?.mob + human_user?.suicide() + if("quit") + winset(usr, null, list("command"=".quit")) + if("resource_bug") + client?.reportissue() + if("resource_github") + client?.github() + if("resource_forums") + client?.forum() + if("resource_rules") + client?.rules() + if("resource_wiki") + client?.wiki() + if("resource_config") + client?.config() + if("resource_changelog") + client?.changelog() + if("admin_notice") + client?.admin_notice() + if("toggle_ignore") + var/ckey = payload["ckey"] + if(!ckey || ckey == client?.ckey) + return TRUE + if(ckey in client?.prefs?.ignoring) + client.prefs.ignoring -= ckey + else + LAZYADD(client.prefs.ignoring, ckey) + client.prefs.save_preferences() + to_chat(client, span_notice("[ckey] has been [(ckey in client.prefs.ignoring) ? "" : "un"]ignored in OOC.")) + + return TRUE diff --git a/code/modules/escape_menu/home_page.dm b/code/modules/escape_menu/home_page.dm deleted file mode 100644 index aead9cc8d513..000000000000 --- a/code/modules/escape_menu/home_page.dm +++ /dev/null @@ -1,213 +0,0 @@ -/datum/escape_menu/proc/show_home_page() - page_holder.give_protected_screen_object(give_escape_menu_title()) - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Resume", - /* offset = */ list(-136, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(home_resume)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Character", - /* offset = */ list(-171, 28), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(home_open_character_settings)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Settings", - /* offset = */ list(-206, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(home_open_game_settings)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Admin Help", - /* offset = */ list(-241, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(open_admin_page)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/leave_body( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Leave Body", - /* offset = */ list(-276, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(open_leave_body)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Quit", - /* offset = */ list(-311, 30), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(quit_game_prompt)), - ) - ) - - //Bottom right buttons, from right to left, starting with the button to open the list. - page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small( - null, - /* hud_owner = */ null, - "Resources", - "Open/Close list of resources", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(src, PROC_REF(toggle_resources)), - /* button_overlay = */ "resources", - )) - -/datum/escape_menu/proc/toggle_resources() - show_resources = !show_resources - if(!show_resources) - //collapsing it - for(var/atom/movable/screen/escape_menu/lobby_button/small/collapsible/button as anything in resource_panels) - button.collapse(page_holder) - resource_panels.Cut() - return - //list of offsets we give, so missing icons don't leave a random gap. - var/list/offset_order = list( - -60, - -120, - -180, - -240, - -300, - -360, - -420, - -480, - ) - resource_panels = list() - - var/githuburl = CONFIG_GET(string/githuburl) - if(githuburl) - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Report Bug", - "Report a bug/issue", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, reportissue)), - /* button_overlay = */ "bug", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Github", - "Open the repository for the game", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, github)), - /* button_overlay = */ "github", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - - var/forumurl = CONFIG_GET(string/forumurl) - if(forumurl) - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Forums", - "Visit the server's forums", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, forum)), - /* button_overlay = */ "forums", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - - var/rulesurl = CONFIG_GET(string/rulesurl) - if(rulesurl) - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Rules", - "View the server rules", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, rules)), - /* button_overlay = */ "rules", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - - var/wikiurl = CONFIG_GET(string/wikiurl) - if(wikiurl) - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Wiki", - "See the wiki for the game", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, wiki)), - /* button_overlay = */ "wiki", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - - var/configurl = CONFIG_GET(string/configurl) - if(configurl) - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Config", - "View the server configuration files", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, config)), - /* button_overlay = */ "config", - /* end_point */ offset_order[1], - )) - offset_order -= offset_order[1] - - resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible( - null, - /* hud_owner = */ null, - "Change Log", - "See all changes to the server", - /* button_screen_loc */ "BOTTOM:30,RIGHT:-20", - CALLBACK(client, TYPE_VERB_REF(/client, changelog)), - /* button_overlay = */ "changelog", - /* end_point */ offset_order[1], - )) - -/datum/escape_menu/proc/home_resume() - qdel(src) - -/datum/escape_menu/proc/home_open_character_settings() - client?.prefs.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES - client?.prefs.update_static_data(client?.mob) - client?.prefs.ui_interact(client?.mob) - qdel(src) - -/datum/escape_menu/proc/home_open_game_settings() - client?.prefs.current_window = PREFERENCE_TAB_GAME_PREFERENCES - client?.prefs.update_static_data(client?.mob) - client?.prefs.ui_interact(client?.mob) - qdel(src) - diff --git a/code/modules/escape_menu/leave_body.dm b/code/modules/escape_menu/leave_body.dm deleted file mode 100644 index 9d2ee1d47288..000000000000 --- a/code/modules/escape_menu/leave_body.dm +++ /dev/null @@ -1,75 +0,0 @@ -/datum/escape_menu/proc/show_leave_body_page() - PRIVATE_PROC(TRUE) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/lobby_button/small( - null, - /* hud_owner = */ null, - "Back", - /* tooltip_text = */ null, - /* button_screen_loc = */ "TOP:-30,LEFT:30", - CALLBACK(src, PROC_REF(open_home_page)), - /* button_overlay = */ "back", - ) - ) - - var/static/dead_clown - if (isnull(dead_clown)) - if (MC_RUNNING(SSatoms.init_stage)) // We're about to create a bunch of atoms for a human - dead_clown = create_dead_clown() - else - stack_trace("The leave body menu was opened before the atoms SS. This shouldn't be possible, as the leave body menu should only be accessible when you have a body.") - - page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button( - null, - /* hud_owner = */ null, - "Suicide", - "Perform a dramatic suicide in game", - /* button_screen_loc = */ "CENTER:-55,CENTER:-1", - CALLBACK(src, PROC_REF(leave_suicide)), - /* button_overlay = */ dead_clown, - )) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/lobby_button( - null, - /* hud_owner = */ null, - "Ghost", - "Exit quietly, leaving your body", - /* button_screen_loc = */ "CENTER:55,CENTER:-1", - CALLBACK(src, PROC_REF(leave_ghost)), - /* button_overlay = */ "ghost", - ) - ) - -/datum/escape_menu/proc/create_dead_clown() - PRIVATE_PROC(TRUE) - - var/mob/living/carbon/human/consistent/human = new - human.equipOutfit(/datum/outfit/job/clown) - - var/mutable_appearance/appearance = new(human.appearance) - appearance.plane = ESCAPE_MENU_PLANE - - // SpacemanDMM bug prevents us from just chain applying these :( - appearance.transform = appearance.transform.Scale(2.5, 2.5) - appearance.transform = appearance.transform.Turn(90) - appearance.transform = appearance.transform.Translate(34, 24) - - qdel(human) - - return appearance - -/datum/escape_menu/proc/leave_ghost() - PRIVATE_PROC(TRUE) - - // Not guaranteed to be living. Everything defines verb/ghost separately. Fuck you. - var/mob/living/living_user = client?.mob - living_user?.ghost() - -/datum/escape_menu/proc/leave_suicide() - PRIVATE_PROC(TRUE) - - // Not guaranteed to be human. Everything defines verb/suicide separately. Fuck you, still. - var/mob/living/carbon/human/human_user = client?.mob - human_user?.suicide() diff --git a/code/modules/escape_menu/quit_game.dm b/code/modules/escape_menu/quit_game.dm deleted file mode 100644 index 5acbfc951352..000000000000 --- a/code/modules/escape_menu/quit_game.dm +++ /dev/null @@ -1,42 +0,0 @@ -/datum/escape_menu/proc/show_quit_game_page() - PRIVATE_PROC(TRUE) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/center( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Are you sure you \n wish to quit?", - /* offset = */ list(-160, -80), - /* font_size = */ 24, - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/center( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "Yes", - /* offset = */ list(-250, -60), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(quit_game)), - ) - ) - - page_holder.give_screen_object( - new /atom/movable/screen/escape_menu/text/clickable/center( - null, - /* hud_owner = */ null, - /* escape_menu = */ src, - /* button_text = */ "No", - /* offset = */ list(-250, 60), - /* font_size = */ 24, - /* on_click_callback = */ CALLBACK(src, PROC_REF(open_home_page)), - ) - ) - -/datum/escape_menu/proc/quit_game() - PRIVATE_PROC(TRUE) - - winset(usr, null, list("command"=".quit")) diff --git a/code/modules/escape_menu/screen_objects/admin_text.dm b/code/modules/escape_menu/screen_objects/admin_text.dm deleted file mode 100644 index abedf2cba3f6..000000000000 --- a/code/modules/escape_menu/screen_objects/admin_text.dm +++ /dev/null @@ -1,107 +0,0 @@ -///Text that gets highlighted when you have an admin ticket notification. -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification - VAR_PRIVATE - current_blink = FALSE - is_blinking = FALSE - last_blink_time = 0 - blink_interval = 0.4 SECONDS - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/Initialize( - mapload, - datum/hud/hud_owner, - datum/escape_menu/escape_menu, - button_text, - list/offset, - font_size = 24, - on_click_callback, -) - . = ..() - - RegisterSignal(escape_menu.client, COMSIG_ADMIN_HELP_RECEIVED, PROC_REF(on_admin_help_received)) - - var/datum/admin_help/current_ticket = escape_menu.client?.current_ticket - if (!isnull(current_ticket)) - connect_ticket(current_ticket) - if (!current_ticket?.player_replied) - begin_processing() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/proc/on_admin_help_received() - SIGNAL_HANDLER - - begin_processing() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/proc/begin_processing() - if (is_blinking) - return - - is_blinking = TRUE - current_blink = TRUE - START_PROCESSING(SSescape_menu, src) - update_text() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/proc/end_processing() - if (!is_blinking) - return - - is_blinking = FALSE - current_blink = FALSE - STOP_PROCESSING(SSescape_menu, src) - update_text() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/proc/connect_ticket(datum/admin_help/admin_help) - ASSERT(istype(admin_help)) - - RegisterSignal(admin_help, COMSIG_ADMIN_HELP_REPLIED, PROC_REF(on_admin_help_replied)) - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/proc/on_admin_help_replied() - SIGNAL_HANDLER - - end_processing() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/process(seconds_per_tick) - if (world.time - last_blink_time < blink_interval) - return - - current_blink = !current_blink - last_blink_time = world.time - update_text() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/text_color() - if (!enabled()) - return ..() - - return current_blink ? "red" : ..() - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/MouseEntered(location, control, params) - . = ..() - - if (is_blinking) - openToolTip(usr, src, params, content = "An admin is trying to talk to you!") - -/atom/movable/screen/escape_menu/text/clickable/admin_ticket_notification/MouseExited(location, control, params) - . = ..() - - closeToolTip(usr) - -///The button used for adminhelping, this is used to grey it out when you're on cooldown. -/atom/movable/screen/escape_menu/text/clickable/admin_help/Initialize( - mapload, - datum/hud/hud_owner, - datum/escape_menu/escape_menu, - button_text, - list/offset, - font_size = 24, - on_click_callback, -) - . = ..() - - RegisterSignals(escape_menu.client, list(COMSIG_CLIENT_VERB_ADDED, COMSIG_CLIENT_VERB_REMOVED), PROC_REF(on_client_verb_changed)) - -/atom/movable/screen/escape_menu/text/clickable/admin_help/proc/on_client_verb_changed(client/source, list/verbs_changed) - SIGNAL_HANDLER - - if (/client/verb/adminhelp in verbs_changed) - update_text() - -/atom/movable/screen/escape_menu/text/clickable/admin_help/enabled() - return (/client/verb/adminhelp in escape_menu.client?.verbs) diff --git a/code/modules/escape_menu/screen_objects/leave_body_text.dm b/code/modules/escape_menu/screen_objects/leave_body_text.dm deleted file mode 100644 index c6e99bb52d78..000000000000 --- a/code/modules/escape_menu/screen_objects/leave_body_text.dm +++ /dev/null @@ -1,22 +0,0 @@ -/atom/movable/screen/escape_menu/text/clickable/leave_body/Initialize( - mapload, - datum/hud/hud_owner, - datum/escape_menu/escape_menu, - button_text, - list/offset, - font_size = 24, - on_click_callback, -) - . = ..() - RegisterSignal(escape_menu.client, COMSIG_CLIENT_MOB_LOGIN, PROC_REF(on_client_mob_login)) - -/atom/movable/screen/escape_menu/text/clickable/leave_body/enabled() - if (!..()) - return FALSE - - return isliving(escape_menu.client?.mob) - -/atom/movable/screen/escape_menu/text/clickable/leave_body/proc/on_client_mob_login() - SIGNAL_HANDLER - - update_text() diff --git a/code/modules/escape_menu/screen_objects/lobby_buttons.dm b/code/modules/escape_menu/screen_objects/lobby_buttons.dm deleted file mode 100644 index bc516c0c7c5e..000000000000 --- a/code/modules/escape_menu/screen_objects/lobby_buttons.dm +++ /dev/null @@ -1,142 +0,0 @@ -/atom/movable/screen/escape_menu/lobby_button - icon = 'icons/hud/escape_menu_leave_body.dmi' - icon_state = "template" - maptext_width = 96 - maptext_y = -32 - - VAR_PROTECTED - font_size = 16 - datum/callback/on_click_callback - hovered = FALSE - tooltip_text - -/atom/movable/screen/escape_menu/lobby_button/Initialize( - mapload, - datum/hud/hud_owner, - button_text, - tooltip_text, - button_screen_loc, - on_click_callback, - button_overlay, -) - . = ..() - - src.on_click_callback = on_click_callback - src.tooltip_text = tooltip_text - - screen_loc = button_screen_loc - - if(button_overlay) - add_overlay(button_overlay) - if(button_text) - add_maptext(button_text) - -/atom/movable/screen/escape_menu/lobby_button/Destroy() - on_click_callback = null - - return ..() - -/atom/movable/screen/escape_menu/lobby_button/Click(location, control, params) - on_click_callback?.InvokeAsync() - -/atom/movable/screen/escape_menu/lobby_button/MouseEntered(location, control, params) - if (hovered || isnull(tooltip_text)) - return - - hovered = TRUE - - // The UX on this is pretty shit, but it's okay enough for now. - // Regularly goes way too far from your cursor. Not designed for large icons. - openToolTip(usr, src, params, content = tooltip_text) - -/atom/movable/screen/escape_menu/lobby_button/MouseExited(location, control, params) - if (!hovered) - return - - hovered = FALSE - closeToolTip(usr) - -/atom/movable/screen/escape_menu/lobby_button/proc/add_maptext(button_text) - animate(src, - maptext = MAPTEXT_PIXELLARI("[button_text]"), - flags = ANIMATION_CONTINUE, - ) - -/atom/movable/screen/escape_menu/lobby_button/small - icon = 'icons/hud/escape_menu_icons.dmi' - font_size = 6 - maptext_width = 80 - maptext_x = -20 - maptext_y = -14 - -/atom/movable/screen/escape_menu/lobby_button/small/add_maptext(button_text) - //overriding parent for a different font here. - animate(src, - maptext = MAPTEXT_GRAND9K("[button_text]"), - flags = ANIMATION_CONTINUE, - ) - -///Amount of time between animations when we fade in and out. -#define COLLAPSIBLE_BUTTON_DURATION (0.4 SECONDS) - -/atom/movable/screen/escape_menu/lobby_button/small/collapsible - maptext_width = 48 - maptext_x = -4 - maptext_y = -44 //we change this during animation to bring it up - layer = parent_type::layer - 0.01 - - ///Reference point we animate the x from during the animation we play on its creation. - var/end_point - -/atom/movable/screen/escape_menu/lobby_button/small/collapsible/Initialize( - mapload, - datum/hud/hud_owner, - button_text, - tooltip_text, - button_screen_loc, - on_click_callback, - button_overlay, - end_point, -) - src.end_point = end_point - return ..() - -/atom/movable/screen/escape_menu/lobby_button/small/collapsible/add_maptext(button_text) - //more than 6 characters, lets bump the maptext down a bit, because we're smaller buttons we would be overlaying over the icon itself otherwise. - if(length(button_text) > 6) - maptext_y -= 10 - //let's take the icons out - animate(src, - transform = transform.Translate(x = end_point, y = 0), - time = COLLAPSIBLE_BUTTON_DURATION, - easing = CUBIC_EASING|EASE_OUT, - ) - . = ..() - //now we'll pull out the maptext - animate(src, - maptext_y = (maptext_y + 30), - time = (COLLAPSIBLE_BUTTON_DURATION / 2), - easing = CUBIC_EASING|EASE_IN, - flags = ANIMATION_CONTINUE, - ) - -/atom/movable/screen/escape_menu/lobby_button/small/collapsible/proc/collapse(datum/screen_object_holder/page_holder) - //timers are delayed until MC is done, so we'll directly qdel during setup so it doesn't freeze on players. - if(MC_RUNNING()) - animate(src, - maptext_y = (maptext_y -30), - time = (COLLAPSIBLE_BUTTON_DURATION / 2), - easing = CUBIC_EASING|EASE_IN, - ) - animate(src, - transform = matrix(), - maptext = null, - time = COLLAPSIBLE_BUTTON_DURATION, - easing = CUBIC_EASING|EASE_OUT, - flags = ANIMATION_CONTINUE, - ) - addtimer(CALLBACK(page_holder, TYPE_PROC_REF(/datum/screen_object_holder, remove_screen_object), src), COLLAPSIBLE_BUTTON_DURATION) - else - page_holder.remove_screen_object(src) - -#undef COLLAPSIBLE_BUTTON_DURATION diff --git a/code/modules/escape_menu/screen_objects/text.dm b/code/modules/escape_menu/screen_objects/text.dm deleted file mode 100644 index 529bd034c861..000000000000 --- a/code/modules/escape_menu/screen_objects/text.dm +++ /dev/null @@ -1,92 +0,0 @@ -/atom/movable/screen/escape_menu/text - mouse_opacity = MOUSE_OPACITY_OPAQUE - maptext_width = 100 - maptext_height = 8 - - VAR_PROTECTED/list/screen_directions = list("NORTH", "WEST") - VAR_PRIVATE/datum/escape_menu/escape_menu - VAR_PRIVATE/button_text - VAR_PRIVATE/font_size - -/atom/movable/screen/escape_menu/text/Initialize( - mapload, - datum/hud/hud_owner, - datum/escape_menu/escape_menu, - button_text, - list/offset, - font_size = 12, -) - . = ..() - src.escape_menu = escape_menu - src.button_text = button_text - src.font_size = font_size - - //this decides how far out you can 'click' on this, so it's important to keep it short. - //yes even here, maptext can still embed links without using clickable subtype. - src.maptext_width = max(round(length(button_text) * font_size * 0.75), font_size * 4) - src.maptext_height = (maptext_height * (font_size / 5)) - src.maptext_height *= length(splittext(button_text, "\n")) - - update_text() - screen_loc = "[screen_directions[1]]:[offset[1]],[screen_directions[2]]:[offset[2]]" - -/atom/movable/screen/escape_menu/text/proc/update_text() - SHOULD_CALL_PARENT(TRUE) - maptext = MAPTEXT_PIXELLARI("[button_text]") - -/atom/movable/screen/escape_menu/text/proc/text_color() - return enabled() ? "white" : "gray" - -/atom/movable/screen/escape_menu/text/proc/enabled() - return TRUE - -///Clickable text, gets underlined when hovered and has an on_click_callback. -/atom/movable/screen/escape_menu/text/clickable - VAR_PRIVATE/datum/callback/on_click_callback - VAR_PRIVATE/hovered = FALSE - -/atom/movable/screen/escape_menu/text/clickable/Initialize( - mapload, - datum/hud/hud_owner, - datum/escape_menu/escape_menu, - button_text, - list/offset, - font_size = 24, - on_click_callback, -) - . = ..() - src.on_click_callback = on_click_callback - -/atom/movable/screen/escape_menu/text/clickable/Destroy() - on_click_callback = null - return ..() - -/atom/movable/screen/escape_menu/text/clickable/update_text() - . = ..() - if (hovered) - maptext = "[maptext]" - -/atom/movable/screen/escape_menu/text/clickable/Click(location, control, params) - if (!enabled()) - return - on_click_callback.InvokeAsync(src) - -/atom/movable/screen/escape_menu/text/clickable/MouseEntered(location, control, params) - set_hovered(TRUE) - -/atom/movable/screen/escape_menu/text/clickable/MouseExited(location, control, params) - set_hovered(FALSE) - -/// Sets the hovered state of the button, and updates the text -/atom/movable/screen/escape_menu/text/clickable/proc/set_hovered(hovered) - if (src.hovered == hovered) - return - - src.hovered = hovered - update_text() - -/atom/movable/screen/escape_menu/text/clickable/center - screen_directions = list("NORTH", "CENTER") - -/atom/movable/screen/escape_menu/text/center - screen_directions = list("NORTH", "CENTER") diff --git a/code/modules/escape_menu/title.dm b/code/modules/escape_menu/title.dm deleted file mode 100644 index 4b36ee4df4ed..000000000000 --- a/code/modules/escape_menu/title.dm +++ /dev/null @@ -1,43 +0,0 @@ -// This doesn't instantiate right away, since we rely on other GLOBs -GLOBAL_DATUM(escape_menu_title, /atom/movable/screen/escape_menu/title) - -/// Provides a singleton for the escape menu details screen. -/proc/give_escape_menu_title() - if (isnull(GLOB.escape_menu_title)) - GLOB.escape_menu_title = new - - return GLOB.escape_menu_title - -/atom/movable/screen/escape_menu/title - screen_loc = "NORTH:-100,WEST:32" - maptext_height = 100 - maptext_width = 500 - -/atom/movable/screen/escape_menu/title/Initialize(mapload, datum/hud/hud_owner) - . = ..() - - update_text() - - RegisterSignal(SSdcs, COMSIG_GLOB_STATION_NAME_CHANGED, PROC_REF(on_station_name_changed)) - -/atom/movable/screen/escape_menu/title/Destroy() - if (GLOB.escape_menu_title == src) - stack_trace("Something tried to delete the escape menu details screen") - return QDEL_HINT_LETMELIVE - - return ..() - -/atom/movable/screen/escape_menu/title/proc/update_text() - var/subtitle_text = MAPTEXT("Another day in...") // DARKPACK EDIT CHANGE - `on : in` because this is a city - var/title_text = {" - - [station_name()] - - "} - - maptext = "" + subtitle_text + MAPTEXT_PIXELLARI(title_text) + "" - -/atom/movable/screen/escape_menu/title/proc/on_station_name_changed() - SIGNAL_HANDLER - - update_text() diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 11745dcf6c78..f4871bb03049 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -437,7 +437,7 @@ properties["transmittable"] = rand(4,7) spreading_modifier = max(CEILING(0.4 * properties["transmittable"], 1), 1) cure_chance = clamp(10 * (0.94 ** properties["resistance"]), 3.5, 12) - stage_prob = max(0.4 * properties["stage_rate"], 1) // 33% faster than regular advanced diseases + stage_prob = max(0.4 * properties["stage_rate"], 1) // Faster than regular advanced diseases set_severity(properties["severity"]) //If we have an advanced (high stage) disease, add it to the name. diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm index 4b7886b02dad..101e84357ac1 100644 --- a/code/modules/events/fake_virus.dm +++ b/code/modules/events/fake_virus.dm @@ -8,7 +8,7 @@ /datum/round_event/fake_virus/start() var/list/fake_virus_victims = list() for(var/mob/living/carbon/human/victim in GLOB.player_list) - if(victim.stat != CONSCIOUS || HAS_TRAIT(victim, TRAIT_VIRUSIMMUNE)) + if(IS_UNCONSCIOUS_OR_CRIT(victim) || HAS_TRAIT(victim, TRAIT_VIRUSIMMUNE)) continue if(!(victim.mind?.assigned_role.job_flags & JOB_CREW_MEMBER)) continue diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm index d8a1ea54f5d0..c2c20020aa93 100644 --- a/code/modules/events/heart_attack.dm +++ b/code/modules/events/heart_attack.dm @@ -30,7 +30,7 @@ /datum/round_event_control/heart_attack/proc/generate_candidates() heart_attack_candidates.Cut() for(var/mob/living/carbon/human/candidate in shuffle(GLOB.player_list)) - if(candidate.stat == DEAD || HAS_TRAIT(candidate, TRAIT_CRITICAL_CONDITION) || !candidate.can_heartattack() || (candidate.has_status_effect(/datum/status_effect/heart_attack)) || candidate.undergoing_cardiac_arrest()) + if(IS_UNCONSCIOUS_OR_CRIT(candidate) || !candidate.can_heartattack() || candidate.has_status_effect(/datum/status_effect/heart_attack) || candidate.undergoing_cardiac_arrest()) continue if(!(candidate.mind.assigned_role.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event continue diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index 84a56b43a0a8..33462dd8aeef 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -8,7 +8,7 @@ var/cracked = FALSE /obj/item/toy/xmas_cracker/attack(mob/target, mob/user) - if( !cracked && ishuman(target) && (target.stat == CONSCIOUS) && !target.get_active_held_item() ) + if( !cracked && ishuman(target) && (!IS_UNCONSCIOUS_OR_CRIT(target)) && !target.get_active_held_item() ) target.visible_message(span_notice("[user] and [target] pop \an [src]! *pop*"), span_notice("You pull \an [src] with [target]! *pop*"), span_hear("You hear a pop.")) var/obj/item/paper/joke_paper = new /obj/item/paper(user.loc) joke_paper.name = "[pick("awful","terrible","unfunny")] joke" diff --git a/code/modules/events/immovable_rod/immovable_rod.dm b/code/modules/events/immovable_rod/immovable_rod.dm index 9306fa7939a9..338952289586 100644 --- a/code/modules/events/immovable_rod/immovable_rod.dm +++ b/code/modules/events/immovable_rod/immovable_rod.dm @@ -227,7 +227,7 @@ playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE) for(var/mob/living/nearby_mob in urange(8, src)) - if(nearby_mob.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(nearby_mob)) continue shake_camera(nearby_mob, 2, 3) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 59caf2b15600..a324eb3044c8 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -9,15 +9,26 @@ max_wizard_trigger_potency = 7 /datum/round_event/ion_storm - var/replaceLawsetChance = 25 //chance the AI's lawset is completely replaced with something else per config weights - var/removeRandomLawChance = 10 //chance the AI has one random supplied or inherent law removed - var/removeDontImproveChance = 10 //chance the randomly created law replaces a random law instead of simply being added - var/shuffleLawsChance = 10 //chance the AI's laws are shuffled afterwards - var/botEmagChance = 1 - var/ionMessage = null announce_when = 1 announce_chance = 33 + // These chances are rolled per rack, rather than all at once. + /// Chance the AI's chance the AI's lawset is completely replaced with something else per config weights + var/replaceLawsetChance = 25 + /// Chance the AI has one random supplied or inherent law removed + var/removeRandomLawChance = 10 + /// Chance the randomly created law replaces a random law instead of simply being added + var/removeDontImproveChance = 10 + /// Chance the AI's laws are shuffled afterwards + var/shuffleLawsChance = 10 + + /// Chance any given bot on the station will be randomly emagged + var/botEmagChance = 1 + + /// If the chance to add a law is rolled, this message will be used. + /// If unset, a random law will be generated for for each rack instead. + var/ionMessage = null + /datum/round_event/ion_storm/add_law_only // special subtype that adds a law only replaceLawsetChance = 0 removeRandomLawChance = 0 @@ -31,38 +42,20 @@ /datum/round_event/ion_storm/start() - //AI laws - for(var/mob/living/silicon/ai/M in GLOB.alive_mob_list) - M.laws_sanity_check() - if(M.stat != DEAD && !M.incapacitated) - if(prob(replaceLawsetChance)) - var/ion_lawset_type = pick_weighted_lawset() - var/datum/ai_laws/ion_lawset = new ion_lawset_type() - // our inherent laws now becomes the picked lawset's laws! - M.laws.inherent = ion_lawset.inherent.Copy() - // and clean up after. - qdel(ion_lawset) - - if(prob(removeRandomLawChance)) - M.remove_law(rand(1, M.laws.get_law_amount(list(LAW_INHERENT, LAW_SUPPLIED)))) - - var/message = ionMessage || generate_ion_law() - if(message) - if(prob(removeDontImproveChance)) - M.replace_random_law(message, list(LAW_INHERENT, LAW_SUPPLIED, LAW_ION), LAW_ION) - else - M.add_ion_law(message) - - if(prob(shuffleLawsChance)) - M.shuffle_laws(list(LAW_INHERENT, LAW_SUPPLIED, LAW_ION)) - - log_silicon("Ion storm changed laws of [key_name(M)] to [english_list(M.laws.get_law_list(TRUE, TRUE))]") - M.post_lawchange() - - if(botEmagChance) - for(var/mob/living/basic/bot/bot in GLOB.alive_mob_list) - if(prob(botEmagChance)) - bot.emag_act() + for(var/obj/machinery/ai_law_rack/rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack)) + rack.scramble_ai_rack( + new_lawset_prob = replaceLawsetChance, + remove_law_prob = removeRandomLawChance, + shuffle_prob = shuffleLawsChance, + base_ion_prob = 100, + sub_ion_prob = removeDontImproveChance, + ion_limit = 2, + ion_message = ionMessage, + ) + + for(var/mob/living/basic/bot/bot as anything in GLOB.bots_list) + if(prob(botEmagChance)) + bot.emag_act() /proc/generate_ion_law() //Threats are generally bad things, silly or otherwise. Plural. diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm index 562925807e31..a8d362deb3b9 100644 --- a/code/modules/events/stray_cargo.dm +++ b/code/modules/events/stray_cargo.dm @@ -38,6 +38,10 @@ var/atom/admin_override_turf ///Admin setable override to spawn a specific cargo pack type var/admin_override_contents + /// Set to use a crate of a specific type irregardless of supply pack. + var/crate_type + /// Set to TRUE to emag the launched crate rather than just unlocking it. + var/emag_crate = FALSE /datum/round_event/stray_cargo/announce(fake) if(fake) @@ -100,12 +104,15 @@ var/storage_override if(initial(supply_pack.order_flags) & ORDER_GOODY) // We offer goody items inside of briefcases, but regular crates still default to their standard crates. storage_override = /obj/item/storage/briefcase/empty - var/obj/container = supply_pack.generate(null, crate_override = storage_override) + var/obj/container = supply_pack.generate(null, crate_override = (crate_type || storage_override)) if(container && istype(container, /obj/structure/closet/crate)) //empty supply packs are a thing! get memed on. var/obj/structure/closet/crate/crate = container - crate.locked = FALSE //Unlock secure crates - crate.update_appearance() + if(emag_crate) + crate.emag_act() // `emag_act()` already calls `update_appeance()` + else + crate.locked = FALSE //Unlock secure crates + crate.update_appearance() var/obj/structure/closet/supplypod/pod = make_pod() var/obj/effect/pod_landingzone/landing_marker = new(landing_zone, pod, container) var/static/mutable_appearance/target_appearance = mutable_appearance('icons/obj/supplypods_32x32.dmi', "LZ") @@ -191,6 +198,8 @@ /datum/round_event/stray_cargo/syndicate possible_pack_types = list(/datum/supply_pack/misc/syndicate) + crate_type = /obj/structure/closet/crate/secure/syndicate + emag_crate = TRUE ///Apply the syndicate pod skin /datum/round_event/stray_cargo/syndicate/make_pod() diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index b7961fa4cfa9..c44b95879c0a 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -83,7 +83,7 @@ var/list/mobs_to_swap = list() for(var/mob/living/carbon/human/alive_human in GLOB.alive_mob_list) - if(alive_human.stat != CONSCIOUS || isnull(alive_human.mind) || IS_WIZARD(alive_human) || HAS_TRAIT(alive_human, TRAIT_NO_MINDSWAP)) + if(IS_UNCONSCIOUS_OR_CRIT(alive_human) || isnull(alive_human.mind) || IS_WIZARD(alive_human) || HAS_TRAIT(alive_human, TRAIT_NO_MINDSWAP)) continue //the wizard(s) are spared on this one mobs_to_swap += alive_human diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm index 271322b1a496..140ea10c941d 100644 --- a/code/modules/experisci/experiment/experiments.dm +++ b/code/modules/experisci/experiment/experiments.dm @@ -450,9 +450,7 @@ . = ..() if (!.) return - if (isandroid(check)) - return TRUE - if (length(check.organs) < 6 || length(check.get_missing_limbs()) > 1) + if (length(check.get_missing_limbs()) > 1) return FALSE var/static/list/augmented_organ_slots = list( @@ -463,11 +461,23 @@ ORGAN_SLOT_LIVER, ORGAN_SLOT_STOMACH, ) + + // some species don't need a stomach, lungs, heart or liver. This is the case for androids, which are fully augged humies with a few extra quirks. + var/organs_left_to_check = length(augmented_organ_slots) + for(var/skip_organ_trait in list(TRAIT_NOHUNGER, TRAIT_NOBREATH, TRAIT_NOBLOOD, TRAIT_LIVERLESS_METABOLISM)) + if(HAS_TRAIT(check, skip_organ_trait)) + organs_left_to_check-- + for (var/obj/item/organ/organ as anything in check.organs) if (!(organ.slot in augmented_organ_slots)) continue if (!IS_ROBOTIC_ORGAN(organ)) return FALSE + organs_left_to_check-- + + if(organs_left_to_check > 0) //we're missing some organs like ears, eyes, liver etc. + return FALSE + for (var/obj/item/bodypart/bodypart as anything in check.get_bodyparts()) if (!IS_ROBOTIC_LIMB(bodypart)) return FALSE diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm index ad2a55592e5b..81850230026a 100644 --- a/code/modules/explorer_drone/exodrone.dm +++ b/code/modules/explorer_drone/exodrone.dm @@ -366,19 +366,24 @@ GLOBAL_LIST_EMPTY(exodrone_launchers) if(fuel_canister) . += span_notice("You can remove the [fuel_canister] with a prying tool.") -/obj/machinery/exodrone_launcher/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(weapon, /obj/item/fuel_pellet)) +/obj/machinery/exodrone_launcher/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/fuel_pellet)) if(fuel_canister) to_chat(user, span_warning("There's already fuel loaded inside [src]!")) - return TRUE - if(!user.transferItemToLoc(weapon, src)) - return - fuel_canister = weapon + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + + fuel_canister = tool update_icon() - return TRUE + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/exodrone)) + if(!user.transferItemToLoc(tool, drop_location())) + return ITEM_INTERACT_BLOCKING - if(istype(weapon, /obj/item/exodrone) && user.transferItemToLoc(weapon, drop_location())) - return TRUE + return ITEM_INTERACT_SUCCESS return ..() diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 19b3283ae9e7..c32ae631da84 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -537,18 +537,21 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( icon_state = base_icon_state return ..() -/obj/item/fish/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers) - if(!istype(item, /obj/item/reagent_containers/cup/fish_feed)) - return ..() - if(!item.reagents.total_volume) - balloon_alert(user, "[item.name] is empty!") - return TRUE +/obj/item/fish/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/reagent_containers/cup/fish_feed)) + return NONE + + if(!tool.reagents.total_volume) + balloon_alert(user, "[tool.name] is empty!") + return ITEM_INTERACT_BLOCKING + if(status == FISH_DEAD) balloon_alert(user, "[name] [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "isn't hungry" : "is dead!"]") - return TRUE - feed(item.reagents) + return ITEM_INTERACT_BLOCKING + + feed(tool.reagents) balloon_alert(user, "fed [name]") - return TRUE + return ITEM_INTERACT_SUCCESS /obj/item/fish/examine(mob/user) . = ..() @@ -556,25 +559,25 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( . += span_boldnicegreen("Caught by [catcher_name] on [catch_date].") if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH) || HAS_TRAIT(loc, TRAIT_EXAMINE_FISH)) - . += span_notice("[p_theyre(TRUE)] [size] cm long.") - . += span_notice("[p_they(TRUE)] weighs [weight] [span_tooltip("the standard unit of measurement for space age fish", "kiloclam")].") + . += span_notice("[p_Theyre()] [size] cm long.") + . += span_notice("[p_They()] weigh[p_s()] [weight] [span_tooltip("the standard unit of measurement for space age fish", "kiloclam")].") if(HAS_TRAIT(src, TRAIT_FISH_GENEGUNNED)) - . += span_warning("[p_theyve(TRUE)] been edited by a fish genegun. [p_they(TRUE)]'ll die if edited again.") + . += span_warning("[p_Theyve()] been edited by a fish genegun. [p_They()]'ll die if edited again.") . += get_health_warnings(user, always_deep = FALSE) if(HAS_TRAIT(src, TRAIT_FISHING_BAIT)) - . += span_smallnoticeital("[p_they(TRUE)] can be used as a fishing bait.") + . += span_smallnoticeital("[p_They()] can be used as a fishing bait.") if(bites_amount) - . += span_warning("[p_theyve(TRUE)] been bitten by someone.") + . += span_warning("[p_Theyve()] been bitten by someone.") /obj/item/fish/proc/get_health_warnings(mob/user, always_deep = FALSE) if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH) && !always_deep) return if(status == FISH_DEAD) - return span_deadsay("[p_theyre(TRUE)] [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "taking the big snooze" : "dead"].") + return span_deadsay("[p_Theyre()] [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "taking the big snooze" : "dead"].") var/list/warnings = list() if(get_starvation_mult()) @@ -594,7 +597,7 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( warnings += "mostly healthy" if(length(warnings)) - . += span_warning("[p_theyre(TRUE)] [english_list(warnings)].") + . += span_warning("[p_Theyre()] [english_list(warnings)].") return . @@ -1118,7 +1121,7 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( balloon_alert(user, "invalid creature!") return if(status != FISH_DEAD) - balloon_alert(user, "[p_theyre(TRUE)] not dead!") + balloon_alert(user, "[p_theyre()] not dead!") return set_status(FISH_ALIVE) injector.expend(src, user) @@ -1584,7 +1587,7 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( if(in_aquarium) to_chat(user, span_warning("[src] runs away from your finger as you dip it into the water!")) else - to_chat(user, span_warning("You try to pet [src] but [p_they()] squirms away!")) + to_chat(user, span_warning("You try to pet [src] but [p_they()] squirm[p_s()] away!")) return FALSE if(HAS_TRAIT(src, TRAIT_FISH_ELECTROGENESIS) && GET_FISH_ELECTROGENESIS(src) > 15 MEGA JOULES) user.electrocute_act(5, src) //was it all worth it? diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index ae97235cfe56..3df656406186 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -41,6 +41,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) var/list/traits_to_add /// If set, the fish may return this infusion entry when get_infusion_entry is called instead of /datum/infuser_entry/fish var/infusion_entry + /// Chance of being added via bioscramble + var/bioscramble_weight = 0 /// Difficulty modifier from this mod, needs to return a list with two values /datum/fish_trait/proc/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) @@ -130,6 +132,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/wary name = "Wary" catalog_description = "This fish will avoid visible fish lines, cloaked line recommended." + bioscramble_weight = 10 /datum/fish_trait/wary/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() @@ -142,6 +145,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/shiny_lover name = "Shiny Lover" catalog_description = "This fish loves shiny things and money, shiny lure recommended." + bioscramble_weight = 10 /datum/fish_trait/shiny_lover/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() @@ -158,6 +162,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/picky_eater name = "Picky Eater" catalog_description = "This fish is very picky and will ignore low quality bait (unless it's amongst its favorites)." + bioscramble_weight = 10 /datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() @@ -189,6 +194,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/nocturnal name = "Nocturnal" catalog_description = "This fish avoids bright lights, fishing and storing in darkness recommended." + bioscramble_weight = 10 /datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() @@ -398,6 +404,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /obj/item/fish/fryish/nessie = 0 ) traits_to_add = list(TRAIT_FISH_NO_MATING) + bioscramble_weight = 10 ///Prevent offsprings of fish with this trait from being of the same type (unless self-mating or the partner also has the trait) /datum/fish_trait/recessive @@ -405,11 +412,13 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "If crossbred, offsprings will always be of the mate species, unless it also possess the trait." inheritability = 0 traits_to_add = list(TRAIT_FISH_RECESSIVE) + bioscramble_weight = 10 /datum/fish_trait/revival name = "Self-Revival" catalog_description = "This fish shows a peculiar ability of reviving itself a minute or two after death." spontaneous_manifest_types = list(/obj/item/fish/boned = 100, /obj/item/fish/mastodon = 100) + bioscramble_weight = 10 /datum/fish_trait/revival/apply_to_fish(obj/item/fish/fish, initial = TRUE) . = ..() @@ -443,6 +452,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) name = "Predator" catalog_description = "It's a predatory fish. It'll hunt down and eat live fishes of smaller size when hungry." incompatible_traits = list(/datum/fish_trait/vegan) + bioscramble_weight = 10 /datum/fish_trait/predator/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() @@ -474,6 +484,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "This fish tastes so repulsive, other fishes won't try to eat it." reagents_to_add = list(/datum/reagent/yuck = 1.2) traits_to_add = list(TRAIT_YUCKY_FISH) + bioscramble_weight = 10 /datum/fish_trait/toxic name = "Toxic" @@ -533,11 +544,13 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) reagents_to_add = list(/datum/reagent/toxin/carpotoxin = 4) infusion_entry = null venom_mult = 6 + bioscramble_weight = 10 /datum/fish_trait/toxin_immunity name = "Toxin Immunity" catalog_description = "This fish has developed an ample-spected immunity to toxins." traits_to_add = list(TRAIT_FISH_TOXIN_IMMUNE) + bioscramble_weight = 10 /datum/fish_trait/crossbreeder name = "Crossbreeder" @@ -545,10 +558,12 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) inheritability = 40 incompatible_traits = list(/datum/fish_trait/no_mating) traits_to_add = list(TRAIT_FISH_CROSSBREEDER) + bioscramble_weight = 10 /datum/fish_trait/territorial name = "Territorial" catalog_description = "This fish will start attacking other fish if the aquarium has five or more." + bioscramble_weight = 10 /datum/fish_trait/territorial/apply_to_fish(obj/item/fish/fish, initial = TRUE) . = ..() @@ -579,6 +594,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "This fish exudes a viscous, slippery lubrificant. It's recommended not to step on it." added_difficulty = 5 reagents_to_add = list(/datum/reagent/lube = 1.2) + bioscramble_weight = 10 /datum/fish_trait/lubed/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type) . = ..() @@ -606,6 +622,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "This fish has developed a primitive adaptation to life on both land and water." infusion_entry = /datum/infuser_entry/amphibious traits_to_add = list(TRAIT_FISH_AMPHIBIOUS) + bioscramble_weight = 10 /datum/fish_trait/amphibious/apply_to_fish(obj/item/fish/fish, initial = TRUE) . = ..() @@ -653,6 +670,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/anxiety name = "Anxiety" catalog_description = "This fish tends to die of stress when forced to be around too many other fish." + bioscramble_weight = 10 /datum/fish_trait/anxiety/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() @@ -771,6 +789,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) name = "Toxic Barbs" catalog_description = "The stinger or bill of this fish is coated in a simple, yet effective venom." spontaneous_manifest_types = list(/obj/item/fish/stingray = 35) + bioscramble_weight = 10 /datum/fish_trait/toxic_barbs/apply_to_fish(obj/item/fish/fish, initial = TRUE) . = ..() @@ -802,6 +821,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) name = "Hallucinogenic" catalog_description = "This fish is coated with hallucinogenic neurotoxin. We advise cooking it before consumption." reagents_to_add = list(/datum/reagent/toxin/mindbreaker/fish = 1) + bioscramble_weight = 10 /datum/fish_trait/hallucinogenic/add_reagents(obj/item/fish/fish, list/reagents) if(!HAS_TRAIT(src, TRAIT_FOOD_FRIED) && !HAS_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED)) @@ -839,6 +859,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "This fish possess a sac that produces ink." spontaneous_manifest_types = list(/obj/item/fish/squid = 35) infusion_entry = /datum/infuser_entry/squid + bioscramble_weight = 10 /datum/fish_trait/ink/apply_to_fish(obj/item/fish/fish, initial = TRUE) . = ..() @@ -875,6 +896,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) catalog_description = "This fish possess the ability to blend with its surroundings." spontaneous_manifest_types = list(/obj/item/fish/squid = 35) added_difficulty = 5 + bioscramble_weight = 10 /datum/fish_trait/camouflage/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame) minigame.special_effects |= FISHING_MINIGAME_RULE_CAMO diff --git a/code/modules/fishing/fish/types/holographic.dm b/code/modules/fishing/fish/types/holographic.dm index d79711debfd9..6398e779d32d 100644 --- a/code/modules/fishing/fish/types/holographic.dm +++ b/code/modules/fishing/fish/types/holographic.dm @@ -37,7 +37,7 @@ QDEL_IN(src, 3 SECONDS) /obj/item/fish/holo/suicide_act(mob/living/user) - visible_message(span_suicide("[user] swallows [src] whole! It looks like [user.p_theyre()] trying to derez [user.p_them()]selves!")) + visible_message(span_suicide("[user] swallows [src] whole! It looks like [user.p_theyre()] trying to derez [user.p_themselves()]!")) var/area/station/holodeck/holo_area = get_area(src) if(!istype(holo_area)) user.dust(just_ash = TRUE, drop_items = TRUE) @@ -115,14 +115,16 @@ if(!iscarbon(user)) return ..() + visible_message(span_suicide("[user] slaps the [name] to their skin, causing it to slide under and meld in! It looks like [user.p_theyre()] trying to 0xF800F8 0x000000 [user.p_themselves()]!")) for(var/obj/item/bodypart/limb in user.get_bodyparts()) - limb.add_bodypart_overlay(/datum/bodypart_texture/checkered, update = FALSE) + limb.add_bodypart_texture(/datum/bodypart_texture/checkered, update = FALSE) var/obj/item/bodypart/head/head = user.get_bodypart(BODY_ZONE_HEAD) if(!isnull(head)) head.head_flags &= ~HEAD_EYESPRITES user.update_body() - return ..() + qdel(src) + return TOXLOSS /obj/item/fish/holo/halffish name = "holographic half-fish" diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm index cd9d656a35e2..218f8e4f651b 100644 --- a/code/modules/fishing/fishing_equipment.dm +++ b/code/modules/fishing/fishing_equipment.dm @@ -165,6 +165,7 @@ icon = 'icons/obj/fishing.dmi' icon_state = "hook" w_class = WEIGHT_CLASS_TINY + custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 0.2) /// A bitfield of traits that this fishing hook has, checked by fish traits and the minigame var/fishing_hook_traits @@ -254,14 +255,228 @@ /obj/item/fishing_hook/shiny/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - rod.material_fish_chance += 15 //Increases the chance of catching a shiny po... erh, material fish ADD_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) /obj/item/fishing_hook/shiny/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) SIGNAL_HANDLER - rod.material_fish_chance -= 15 REMOVE_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src)) +/obj/item/fishing_hook/anomaly + name = "anomalous lure hook" + icon_state = "anom" + wiki_desc = "You can slot in an active anomaly core to add a variety of effects to the fishing rod. \ + Most (but not all) cores will have a unique effect." + /// The actual anomaly core slotted in + var/obj/item/assembly/signaler/anomaly/core + +/obj/item/fishing_hook/anomaly/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_SLOTTED, PROC_REF(on_fishing_rod_slotted)) + RegisterSignal(src, COMSIG_ITEM_FISHING_ROD_UNSLOTTED, PROC_REF(on_fishing_rod_unslotted)) + +/obj/item/fishing_hook/anomaly/examine(mob/user) + . = ..() + if(!isnull(core)) + . += span_info("There's \a [core] slotted into it.") + +/obj/item/fishing_hook/anomaly/examine_more(mob/user) + . = ..() + . += "• A [/obj/item/assembly/signaler/anomaly/dimensional::name] adds a chance for your catch to have its materials altered." + . += "• A [/obj/item/assembly/signaler/anomaly/bioscrambler::name] adds a chance for your catch to have its traits or stats altered." + . += "• A [/obj/item/assembly/signaler/anomaly/pyro::name] adds a chance for your catch to be cooked." + . += "• A [/obj/item/assembly/signaler/anomaly/ectoplasm::name] adds a chance to catch haunted fish." + . += "• A [/obj/item/assembly/signaler/anomaly/hallucination::name] adds a chance to cause your catches to randomly grow or shrink in size." + . += "• A [/obj/item/assembly/signaler/anomaly/grav::name] reduces the gravity of the bobber, causing it to fall slower." + . += "• A [/obj/item/assembly/signaler/anomaly/vortex::name] reduces the bobber's overall bounciness." + . += "• Unmentioned cores likely have no unique effect when applied." + +/obj/item/fishing_hook/anomaly/proc/on_fishing_rod_slotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + + switch(core?.type) + if(/obj/item/assembly/signaler/anomaly/dimensional) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(dimensional_catch_bonus)) + if(/obj/item/assembly/signaler/anomaly/bioscrambler) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(bioscrambler_catch_bonus)) + if(/obj/item/assembly/signaler/anomaly/pyro) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(pyro_catch_effect)) + if(/obj/item/assembly/signaler/anomaly/ectoplasm) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(ectoplasm_catch_effect)) + if(/obj/item/assembly/signaler/anomaly/hallucination) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(hallucination_catch_effect)) + if(/obj/item/assembly/signaler/anomaly/grav) + rod.gravity_mult *= 0.4 + if(/obj/item/assembly/signaler/anomaly/vortex) + rod.bounciness_mult *= 0.2 + else + rod.balloon_alert_to_viewers("no core effect!", vision_distance = 2) + +/obj/item/fishing_hook/anomaly/proc/on_fishing_rod_unslotted(datum/source, obj/item/fishing_rod/rod, slot) + SIGNAL_HANDLER + + switch(core?.type) + if(/obj/item/assembly/signaler/anomaly/grav) + rod.gravity_mult /= 0.4 + if(/obj/item/assembly/signaler/anomaly/vortex) + rod.bounciness_mult /= 0.2 + + UnregisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH) + +/obj/item/fishing_hook/anomaly/proc/get_probability_bonuses(obj/item/fishing_rod/rod, mob/living/user) + var/skill_modifier = user.mind?.get_skill_level(/datum/skill/fishing) * 1.5 + var/bait_modifier = 0 + if(!isnull(rod.bait)) + if(HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT)) + bait_modifier += 16 + else if(HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT)) + bait_modifier += 8 + else if(HAS_TRAIT(rod.bait, TRAIT_BASIC_QUALITY_BAIT)) + bait_modifier += 4 + + // up to a ~25% bonus + return skill_modifier + bait_modifier + +/obj/item/fishing_hook/anomaly/proc/dimensional_catch_bonus(obj/item/fishing_rod/rod, obj/item/caught, mob/living/user) + SIGNAL_HANDLER + + if(!isfish(caught) || !HAS_TRAIT(caught, TRAIT_FISH_JUST_SPAWNED)) + return + + if(!prob(25 + get_probability_bonuses(rod, user))) + return + + var/obj/item/fish/caught_fish = caught + // number of materials applies to the fish + var/list/material_amounts = alist( + 1 = 16, + 2 = 3, + 3 = 1, + ) + // generic list of materials we may apply + var/list/material_weights = alist( + /datum/material/gold = 20, + /datum/material/silver = 20, + /datum/material/plastic = 10, + /datum/material/uranium = 5, + /datum/material/plasma = 5, + ) + + // adds chance of inheriting rod materials + if(rod.material_flags & MATERIAL_EFFECTS) + for(var/rod_material_type, rod_material_amount in rod.custom_materials) + material_weights[rod_material_type] = 40 * (rod_material_amount / values_sum(rod.custom_materials)) + + // select a number of materials, then select what materials to apply + var/num_materials = pick_weight(material_amounts) + var/list/material_setup = list() + for(var/i in 1 to num_materials) + material_setup[pick_weight(material_weights)] += round(caught_fish.weight * (1 / num_materials)) + + caught_fish.set_custom_materials(material_setup) + +/obj/item/fishing_hook/anomaly/proc/bioscrambler_catch_bonus(obj/item/fishing_rod/rod, obj/item/caught, mob/living/user) + SIGNAL_HANDLER + + if(!isfish(caught) || !HAS_TRAIT(caught, TRAIT_FISH_JUST_SPAWNED) || !prob(25 + get_probability_bonuses(rod, user))) + return + + var/list/random_pool = list() + for(var/datum/fish_trait/random_trait as anything in GLOB.fish_traits) + if(random_trait.bioscramble_weight > 0) + random_pool[random_trait.bioscramble_weight] = random_trait + + var/obj/item/fish/caught_fish = caught + caught_fish.update_size_and_weight(new_weight = caught_fish.weight * pick(0.6, 0.8, 1, 1.2, 1.4)) + + var/datum/fish_trait/random_trait = pick_weight(random_pool) + random_trait.apply_to_fish(caught_fish) + +/obj/item/fishing_hook/anomaly/proc/pyro_catch_effect(obj/item/fishing_rod/rod, obj/item/caught, mob/living/user) + SIGNAL_HANDLER + + if(!isfish(caught) || !HAS_TRAIT(caught, TRAIT_FISH_JUST_SPAWNED) || !prob(85 + get_probability_bonuses(rod, user))) + return + + var/alist/fry_times = alist( + FRYING_TIME_FRIED = 2, + FRYING_TIME_PERFECT = 5, + FRYING_TIME_BURNT = 3, + ) + + caught.AddElement(/datum/element/fried_item, pick_weight(fry_times)) + +/obj/item/fishing_hook/anomaly/proc/ectoplasm_catch_effect(obj/item/fishing_rod/rod, obj/item/caught, mob/living/user) + SIGNAL_HANDLER + + if(!isfish(caught) || !HAS_TRAIT(caught, TRAIT_FISH_JUST_SPAWNED) || !prob(50 + get_probability_bonuses(rod, user))) + return + + caught.AddComponent(/datum/component/haunted_item, \ + haunt_color = "#52336e", \ + haunt_duration = 6 MINUTES, \ + spawn_message = span_revenwarning("[caught] slowly rises upward, flopping menacingly in the air..."), \ + despawn_message = span_revenwarning("[caught] settles to the floor, looking like a normal fish again..."), \ + ) + +/obj/item/fishing_hook/anomaly/proc/hallucination_catch_effect(obj/item/fishing_rod/rod, obj/item/caught, mob/living/user) + SIGNAL_HANDLER + + if(!isfish(caught) || !HAS_TRAIT(caught, TRAIT_FISH_JUST_SPAWNED) || !prob(50 + get_probability_bonuses(rod, user))) + return + + caught.transform = caught.transform.Scale(pick(0.5, 0.75, 1, 1.25, 1.5)) + +/obj/item/fishing_hook/anomaly/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/assembly/signaler/anomaly)) + return NONE + if(!isnull(core)) + balloon_alert(user, "already has a core!") + return ITEM_INTERACT_BLOCKING + if(user.is_holding(tool)) + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + else + tool.forceMove(src) + + core = tool + balloon_alert(user, "core installed") + playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) + update_appearance(UPDATE_OVERLAYS) + return ITEM_INTERACT_SUCCESS + +/obj/item/fishing_hook/anomaly/attack_self(mob/user, modifiers) + . = ..() + if(.) + return . + if(isnull(core)) + return . + + if(user.is_holding(src)) + user.put_in_hands(core) + else + core.forceMove(drop_location()) + + core = null + balloon_alert(user, "core removed") + playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) + update_appearance(UPDATE_OVERLAYS) + return TRUE + +/obj/item/fishing_hook/anomaly/update_overlays() + . = ..() + if(isnull(core)) + return + + var/mutable_appearance/core_base = mutable_appearance(icon, "anom_overlay_base", alpha = src.alpha) + . += core_base + + var/mutable_appearance/core_color = mutable_appearance(icon, "anom_overlay_light", alpha = src.alpha) + core_color.color = core.core_color + . += core_color + + var/mutable_appearance/core_emissive = emissive_appearance(icon, "anom_overlay_light", src, alpha = src.alpha) + . += core_emissive + /obj/item/fishing_hook/weighted name = "weighted hook" icon_state = "weighted" @@ -670,7 +885,7 @@ /obj/item/fish_gene/proc/inject_into_fish(obj/item/fish/fish, mob/living/user, obj/item/tool = src) var/datum/fish_trait/trait = GLOB.fish_traits[trait_type] if(!trait.apply_to_fish(fish, initial = FALSE)) - to_chat(user, span_warning("You can't inject the \"[trait_type::name]\" trait into [fish]. [fish.p_they(TRUE)] either [fish.p_have()] it or [fish.p_are()] incompatible with it.")) + to_chat(user, span_warning("You can't inject the \"[trait_type::name]\" trait into [fish]. [fish.p_They()] either [fish.p_have()] it or [fish.p_are()] incompatible with it.")) return ITEM_INTERACT_BLOCKING user.visible_message(span_notice("[user] injects [fish] with [tool]."), span_notice("You inject the \"[trait_type::name]\" trait into [fish].")) qdel(src) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 5ccd3e59ccfe..618ac6a09aae 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -58,8 +58,6 @@ ///Prevents spamming the line casting, without affecting the player's click cooldown. COOLDOWN_DECLARE(casting_cd) - ///The chance of catching fish made of the same material of the fishing rod (if MATERIAL_EFFECTS is enabled) - var/material_fish_chance = 10 ///The multiplier of how much experience is gained when fishing with this rod. var/experience_multiplier = 1 ///The multiplier of the completion gain during the minigame @@ -163,12 +161,7 @@ list_clear_nulls(block) . += boxed_message(block.Join("\n")) - if(get_percent && (material_flags & MATERIAL_EFFECTS) && length(custom_materials)) - . += boxed_message(span_info("Right now, fish caught by this fishing rod have a [get_material_fish_chance(user)]% of being made of its same materials.")) - block = list() - if(HAS_TRAIT(src, TRAIT_ROD_ATTRACT_SHINY_LOVERS)) - block += span_info("This fishing rod will attract shiny-loving fish.") if(HAS_TRAIT(src, TRAIT_ROD_IGNORE_ENVIRONMENT)) block += span_info("Environment and light shouldn't be an issue with this rod.") if(HAS_TRAIT_NOT_FROM(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)) // Duds are innately removed by baits, we all know that. @@ -221,12 +214,6 @@ /obj/item/fishing_rod/proc/on_reward_caught(atom/movable/reward, mob/user) if(isnull(reward)) return - var/isfish = isfish(reward) - if((material_flags & MATERIAL_EFFECTS) && isfish && length(custom_materials) && HAS_TRAIT(reward, TRAIT_FISH_JUST_SPAWNED)) - if(prob(get_material_fish_chance(user))) - var/obj/item/fish/fish = reward - var/datum/material/material = get_master_material() - fish.set_custom_materials(list(material.type = fish.weight)) // catching things that aren't fish or alive mobs doesn't consume baits. if(isnull(bait) || HAS_TRAIT(bait, TRAIT_BAIT_UNCONSUMABLE)) return @@ -235,7 +222,7 @@ if(caught_mob.stat == DEAD) return else - if(!isfish) + if(!isfish(reward)) return var/obj/item/fish/fish = reward if(HAS_TRAIT(bait, TRAIT_POISONOUS_BAIT) && !HAS_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE)) @@ -250,19 +237,6 @@ qdel(bait) update_icon() -///Returns the probability that a fish caught by this (custom material) rod will be of the same material. -/obj/item/fishing_rod/proc/get_material_fish_chance(mob/user) - var/material_chance = material_fish_chance - if(bait) - if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT)) - material_chance += 16 - else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT)) - material_chance += 8 - else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT)) - material_chance += 4 - material_chance += user.mind?.get_skill_level(/datum/skill/fishing) * 1.5 - return material_chance - /obj/item/fishing_rod/proc/should_bane_fish_infusions(mob/living/target) return force > 0 && HAS_TRAIT(target, TRAIT_WATER_ADAPTATION) @@ -527,20 +501,23 @@ . += line_overlay . += mutable_appearance(icon_file, "hook_overlay") -/obj/item/fishing_rod/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(slot_check(attacking_item,ROD_SLOT_LINE)) - use_slot(ROD_SLOT_LINE, user, attacking_item) +/obj/item/fishing_rod/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(slot_check(tool, ROD_SLOT_LINE)) + use_slot(ROD_SLOT_LINE, user, tool) SStgui.update_uis(src) - return TRUE - else if(slot_check(attacking_item,ROD_SLOT_HOOK)) - use_slot(ROD_SLOT_HOOK, user, attacking_item) + return ITEM_INTERACT_SUCCESS + + if(slot_check(tool, ROD_SLOT_HOOK)) + use_slot(ROD_SLOT_HOOK, user, tool) SStgui.update_uis(src) - return TRUE - else if(slot_check(attacking_item,ROD_SLOT_BAIT) || istype(attacking_item, /obj/item/bait_can)) //Can click on the fishing rod with bait can directly - use_slot(ROD_SLOT_BAIT, user, attacking_item) + return ITEM_INTERACT_SUCCESS + + if(slot_check(tool, ROD_SLOT_BAIT) || istype(tool, /obj/item/bait_can)) //Can click on the fishing rod with bait can directly + use_slot(ROD_SLOT_BAIT, user, tool) SStgui.update_uis(src) - return TRUE - . = ..() + return ITEM_INTERACT_SUCCESS + + return NONE /obj/item/fishing_rod/ui_data(mob/user) . = ..() @@ -790,7 +767,6 @@ deceleration_mult = 1.2 bounciness_mult = 0.3 gravity_mult = 1.2 - material_fish_chance = 33 //if somehow you metalgen it. bait_height_mult = 1.4 /obj/item/fishing_rod/tech diff --git a/code/modules/fishing/sources/subtypes/rifts.dm b/code/modules/fishing/sources/subtypes/rifts.dm index c7b832bba8f7..5ab994f8e470 100644 --- a/code/modules/fishing/sources/subtypes/rifts.dm +++ b/code/modules/fishing/sources/subtypes/rifts.dm @@ -158,7 +158,7 @@ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user) if(heretic_datum) heretic_datum.adjust_knowledge_points(1) - to_chat(user, "[span_hear("You hear a whisper...")] [span_hypnophrase("THE HIGHER I RISE, THE MORE I FISH.")]") + to_chat(user, "[span_hear("You hear a whisper...")] [span_mansus("THE HIGHER I RISE, THE MORE I FISH.")]") // They can also gain an extra influence point if they infused their rod. if(HAS_TRAIT(challenge.used_rod, TRAIT_ROD_MANSUS_INFUSED)) heretic_datum.adjust_knowledge_points(1) diff --git a/code/modules/flufftext/dreaming.dm b/code/modules/flufftext/dreaming.dm index a97054affe09..e05ba089eb6a 100644 --- a/code/modules/flufftext/dreaming.dm +++ b/code/modules/flufftext/dreaming.dm @@ -46,7 +46,7 @@ */ /mob/living/carbon/proc/dream_sequence(list/dream_fragments, datum/dream/current_dream) - if(stat != UNCONSCIOUS || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION)) + if(!IS_UNCONSCIOUS(src) || stat >= HARD_CRIT) REMOVE_TRAIT(src, TRAIT_DREAMING, DREAMING_SOURCE) current_dream.OnDreamEnd(src) SEND_SIGNAL(src, COMSIG_END_DREAMING, current_dream) diff --git a/code/modules/food_and_drinks/equipment/plate.dm b/code/modules/food_and_drinks/equipment/plate.dm index a0116c389c1b..2fa21c0f9ddf 100644 --- a/code/modules/food_and_drinks/equipment/plate.dm +++ b/code/modules/food_and_drinks/equipment/plate.dm @@ -53,7 +53,7 @@ if(!contents.len) return var/obj/item/object_to_eat = contents[1] - target.attackby(object_to_eat, user) + object_to_eat.melee_attack_chain(user, target) return TRUE //No normal attack /obj/item/plate/IsContainedAtomAccessible(atom/contained, atom/movable/user) diff --git a/code/modules/food_and_drinks/food/egg.dm b/code/modules/food_and_drinks/food/egg.dm index 032d5fd0b276..6b884e11b8fe 100644 --- a/code/modules/food_and_drinks/food/egg.dm +++ b/code/modules/food_and_drinks/food/egg.dm @@ -89,37 +89,40 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0) new /mob/living/basic/chick(spawn_turf) GLOB.chicks_from_eggs++ -/obj/item/food/egg/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/crayon = item - var/clr = crayon.crayon_color +/obj/item/food/egg/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/crayon = tool + var/pigment = crayon.crayon_color - if(!(clr in list("blue", "green", "mime", "orange", "purple", "rainbow", "red", "yellow"))) + if(!(pigment in list("blue", "green", "mime", "orange", "purple", "rainbow", "red", "yellow"))) to_chat(usr, span_notice("[src] refuses to take on this colour!")) - return + return ITEM_INTERACT_BLOCKING - to_chat(usr, span_notice("You colour [src] with [item].")) - icon_state = "egg-[clr]" + to_chat(usr, span_notice("You colour [src] with [tool].")) + icon_state = "egg-[pigment]" + return ITEM_INTERACT_SUCCESS - else if(istype(item, /obj/item/stamp/clown)) + if(istype(tool, /obj/item/stamp/clown)) var/clowntype = pick("grock", "grimaldi", "rainbow", "chaos", "joker", "sexy", "standard", "bobble", "krusty", "bozo", "pennywise", "ronald", "jacobs", "kelly", "popov", "cluwne") icon_state = "egg-clown-[clowntype]" desc = "An egg that has been decorated with the grotesque, robustable likeness of a clown's face. " - to_chat(usr, span_notice("You stamp [src] with [item], creating an artistic and not remotely horrifying likeness of clown makeup.")) + to_chat(usr, span_notice("You stamp [src] with [tool], creating an artistic and not remotely horrifying likeness of clown makeup.")) + return ITEM_INTERACT_SUCCESS - else if(is_reagent_container(item)) - var/obj/item/reagent_containers/dunk_test_container = item + if(is_reagent_container(tool)) + var/obj/item/reagent_containers/dunk_test_container = tool if (!dunk_test_container.is_drainable() || !dunk_test_container.reagents.has_reagent(/datum/reagent/water)) - return + return NONE to_chat(user, span_notice("You check if [src] is rotten.")) if(istype(src, /obj/item/food/egg/rotten)) to_chat(user, span_warning("[src] floats in the [dunk_test_container]!")) else to_chat(user, span_notice("[src] sinks into the [dunk_test_container]!")) - else - ..() + return ITEM_INTERACT_SUCCESS + + return NONE /obj/item/food/egg/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) if(!istype(interacting_with, /obj/machinery/griddle)) @@ -285,23 +288,25 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0) . = ..() AddElement(/datum/element/love_food_buff, /datum/status_effect/food/speech/french) -/obj/item/food/omelette/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/kitchen/fork)) - var/obj/item/kitchen/fork/fork = item - if(fork.forkload) - to_chat(user, span_warning("You already have omelette on your fork!")) - else - fork.icon_state = "forkloaded" - user.visible_message(span_notice("[user] takes a piece of omelette with [user.p_their()] fork!"), \ - span_notice("You take a piece of omelette with your fork.")) - - var/datum/reagent/reagent = pick(reagents.reagent_list) - reagents.remove_reagent(reagent.type, 1) - fork.forkload = reagent - if(reagents.total_volume <= 0) - qdel(src) - return - ..() +/obj/item/food/omelette/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/kitchen/fork)) + return NONE + + var/obj/item/kitchen/fork/fork = tool + if(fork.forkload) + to_chat(user, span_warning("You already have omelette on your fork!")) + return ITEM_INTERACT_BLOCKING + + fork.icon_state = "forkloaded" + user.visible_message(span_notice("[user] takes a piece of omelette with [user.p_their()] fork!"), \ + span_notice("You take a piece of omelette with your fork.")) + + var/datum/reagent/reagent = pick(reagents.reagent_list) + reagents.remove_reagent(reagent.type, 1) + fork.forkload = reagent + if(reagents.total_volume <= 0) + qdel(src) + return ITEM_INTERACT_SUCCESS /obj/item/food/benedict name = "eggs benedict" diff --git a/code/modules/food_and_drinks/food/misc.dm b/code/modules/food_and_drinks/food/misc.dm index 5ac572b43414..583a9a0f6e38 100644 --- a/code/modules/food_and_drinks/food/misc.dm +++ b/code/modules/food_and_drinks/food/misc.dm @@ -394,20 +394,22 @@ if (can_stick) . += span_notice("If you had a rod you could make butter on a stick.") -/obj/item/food/butter/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(item, /obj/item/stack/rods) || !can_stick) - return ..() - var/obj/item/stack/rods/rods = item +/obj/item/food/butter/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/rods) || !can_stick) + return NONE + + var/obj/item/stack/rods/rods = tool if(!rods.use(1))//borgs can still fail this if they have no metal to_chat(user, span_warning("You do not have enough iron to put [src] on a stick!")) - return ..() + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You stick the rod into the stick of butter.")) user.temporarilyRemoveItemFromInventory(src) var/obj/item/food/butter/on_a_stick/new_item = new(drop_location()) if (new_item.IsReachableBy(user)) user.put_in_hands(new_item) qdel(src) - return TRUE + return ITEM_INTERACT_SUCCESS /obj/item/food/butter/on_a_stick //there's something so special about putting it on a stick. name = "butter on a stick" diff --git a/code/modules/food_and_drinks/food/packaged.dm b/code/modules/food_and_drinks/food/packaged.dm index 597ec3ce60d4..a6b95c380316 100644 --- a/code/modules/food_and_drinks/food/packaged.dm +++ b/code/modules/food_and_drinks/food/packaged.dm @@ -131,7 +131,7 @@ return FALSE if(!is_drainable()) // Can is not open return FALSE - if(hungry_pet.stat) // Parrot deceased + if(IS_UNCONSCIOUS_OR_CRIT(hungry_pet)) // Parrot deceased return FALSE if(hungry_pet.mob_biotypes & (MOB_BEAST|MOB_REPTILE|MOB_BUG)) return TRUE diff --git a/code/modules/food_and_drinks/food/pancakes.dm b/code/modules/food_and_drinks/food/pancakes.dm index d618c391e564..84ab82340cff 100644 --- a/code/modules/food_and_drinks/food/pancakes.dm +++ b/code/modules/food_and_drinks/food/pancakes.dm @@ -32,24 +32,28 @@ positive_result = TRUE,\ use_large_steam_sprite = TRUE) -/obj/item/food/pancakes/raw/attackby(obj/item/garnish, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/item/food/pancakes/raw/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(name != initial(name))// we already have additives + return ..() + var/newresult - if(istype(garnish, /obj/item/food/grown/berries)) + if(istype(tool, /obj/item/food/grown/berries)) newresult = /obj/item/food/pancakes/blueberry name = "raw blueberry pancake" icon_state = "rawbbpancakes_1" stack_name = "rawbbpancakes" - else if(istype(garnish, /obj/item/food/chocolatebar)) + else if(istype(tool, /obj/item/food/chocolatebar)) newresult = /obj/item/food/pancakes/chocolatechip name = "raw chocolate chip pancake" icon_state = "rawccpancakes_1" stack_name = "rawccpancakes" - else + if(!newresult) return ..() - if(newresult) - qdel(garnish) - to_chat(user, span_notice("You add [garnish] to [src].")) - AddComponent(/datum/component/grillable, cook_result = newresult) + + qdel(tool) + to_chat(user, span_notice("You add [tool] to [src].")) + AddComponent(/datum/component/grillable, cook_result = newresult) + return ITEM_INTERACT_SUCCESS /obj/item/food/pancakes/raw/examine(mob/user) . = ..() @@ -119,31 +123,35 @@ ingredients_listed += "[ING.name], " . += "It contains [contents.len?"[ingredients_listed]":"no ingredient, "]on top of a [initial(name)]." -/obj/item/food/pancakes/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/food/pancakes)) - var/obj/item/food/pancakes/pancake = item +/obj/item/food/pancakes/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/food/pancakes)) + var/obj/item/food/pancakes/pancake = tool if((contents.len >= PANCAKE_MAX_STACK) || ((pancake.contents.len + contents.len) > PANCAKE_MAX_STACK)) to_chat(user, span_warning("You can't add that many pancakes to [src]!")) - else - if(!user.transferItemToLoc(pancake, src)) - return - to_chat(user, span_notice("You add the [pancake] to the [src].")) - pancake.name = initial(pancake.name) - contents += pancake - update_snack_overlays(pancake) - if (pancake.contents.len) - for(var/pancake_content in pancake.contents) - pancake = pancake_content - pancake.name = initial(pancake.name) - contents += pancake - update_snack_overlays(pancake) - pancake = item - pancake.contents.Cut() - return - else if(contents.len) - var/obj/O = contents[contents.len] - return O.attackby(item, user, modifiers) - ..() + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(pancake, src)) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You add the [pancake] to the [src].")) + pancake.name = initial(pancake.name) + contents += pancake + update_snack_overlays(pancake) + if (pancake.contents.len) + for(var/pancake_content in pancake.contents) + pancake = pancake_content + pancake.name = initial(pancake.name) + contents += pancake + update_snack_overlays(pancake) + pancake = tool + pancake.contents.Cut() + return ITEM_INTERACT_SUCCESS + + if(contents.len) + var/obj/pancake = contents[contents.len] + return pancake.item_interaction(user, tool, modifiers) + + return NONE /obj/item/food/pancakes/proc/update_snack_overlays(obj/item/food/pancakes/pancake) var/mutable_appearance/pancake_visual = mutable_appearance(icon, "[pancake.stack_name]_[rand(1, 3)]") diff --git a/code/modules/food_and_drinks/food/pizza.dm b/code/modules/food_and_drinks/food/pizza.dm index fa58a3f465b8..ee57c9e6b3b7 100644 --- a/code/modules/food_and_drinks/food/pizza.dm +++ b/code/modules/food_and_drinks/food/pizza.dm @@ -503,18 +503,25 @@ to_chat(user, "If you want something crazy like pineapple, I'll kill you.") //this is in bigger text because it's hard to spam something that gibs you, and so that you're perfectly aware of the reason why you died user.investigate_log("has been gibbed by putting pineapple on an arnold pizza.", INVESTIGATE_DEATHS) user.gib(DROP_ALL_REMAINS) //if you want something crazy like pineapple, i'll kill you - else if(istype(item, /obj/item/food/grown/mushroom) && iscarbon(user)) + return TRUE + + if(istype(item, /obj/item/food/grown/mushroom) && iscarbon(user)) to_chat(user, span_userdanger("So, if you want mushroom, shut up.")) //not as large as the pineapple text, because you could in theory spam it var/mob/living/carbon/shutup = user shutup.gain_trauma(/datum/brain_trauma/severe/mute) + return TRUE + + return FALSE /obj/item/food/pizza/arnold/attack(mob/living/target, mob/living/user) . = ..() try_break_off(target, user) -/obj/item/food/pizza/arnold/attackby(obj/item/item, mob/user) - i_kill_you(item, user) - . = ..() +/obj/item/food/pizza/arnold/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!i_kill_you(tool, user)) + return ..() + + return ITEM_INTERACT_SUCCESS /obj/item/food/pizzaslice/arnold name = "\improper Arnold pizza slice" @@ -528,9 +535,11 @@ . =..() try_break_off(target, user) -/obj/item/food/pizzaslice/arnold/attackby(obj/item/item, mob/user) - i_kill_you(item, user) - . = ..() +/obj/item/food/pizzaslice/arnold/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!i_kill_you(tool, user)) + return ..() + + return ITEM_INTERACT_SUCCESS // Ant Pizza, now with more ants. /obj/item/food/pizzaslice/ants diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm index 9440baffddef..ce61460c7222 100644 --- a/code/modules/food_and_drinks/machinery/gibber.dm +++ b/code/modules/food_and_drinks/machinery/gibber.dm @@ -147,7 +147,7 @@ GAME_VERB_SRC(/obj/machinery/gibber, eject, oview(1), "Empty gibber", null) - if (usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) + if (IS_UNCONSCIOUS_OR_CRIT(usr) || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) return if(!usr.can_perform_action(src)) return diff --git a/code/modules/food_and_drinks/machinery/griddle.dm b/code/modules/food_and_drinks/machinery/griddle.dm index 5041259352a8..ed53dd7e7c45 100644 --- a/code/modules/food_and_drinks/machinery/griddle.dm +++ b/code/modules/food_and_drinks/machinery/griddle.dm @@ -66,22 +66,54 @@ visible_message(span_notice("[exposing_reagent] begins to cook on [src].")) return NONE -/obj/machinery/griddle/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) +/obj/machinery/griddle/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(user.combat_mode) + return NONE + + if(tool.atom_storage) + if(length(griddled_objects) >= max_items) + balloon_alert(user, "it's full!") + return ITEM_INTERACT_BLOCKING + + if(!istype(tool, /obj/item/storage/bag/tray)) + // Non-tray dumping requires a do_after + to_chat(user, span_notice("You start dumping out the contents of [tool] into [src]...")) + if(!do_after(user, 2 SECONDS, target = tool)) + return ITEM_INTERACT_BLOCKING + + var/loaded = 0 + for(var/obj/tray_item in tool) + if(!IS_EDIBLE(tray_item)) + continue + if(length(griddled_objects) >= max_items) + break + if(tool.atom_storage.attempt_remove(tray_item, src)) + loaded++ + AddToGrill(tray_item, user) + if(!loaded) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You insert [loaded] item\s into [src].")) + update_appearance() + return ITEM_INTERACT_SUCCESS if(griddled_objects.len >= max_items) to_chat(user, span_notice("[src] can't fit more items!")) - return + return ITEM_INTERACT_BLOCKING + //Center the icon where the user clicked. if(!LAZYACCESS(modifiers, ICON_X) || !LAZYACCESS(modifiers, ICON_Y)) - return - if(user.transferItemToLoc(I, src, silent = FALSE)) - //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf) - I.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2) - I.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2) - to_chat(user, span_notice("You place [I] on [src].")) - AddToGrill(I, user) - else - return ..() + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(tool, src, silent = FALSE)) + return ITEM_INTERACT_BLOCKING + + //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf) + tool.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2) + tool.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2) + to_chat(user, span_notice("You place [tool] on [src].")) + AddToGrill(tool, user) + return ITEM_INTERACT_SUCCESS + /obj/machinery/griddle/item_interaction_secondary(mob/living/user, obj/item/item, list/modifiers) if(isnull(item.atom_storage)) @@ -91,35 +123,6 @@ item.atom_storage.attempt_insert(tray_item, user, TRUE) return ITEM_INTERACT_SUCCESS -/obj/machinery/griddle/item_interaction(mob/living/user, obj/item/item, list/modifiers) - if(isnull(item.atom_storage)) - return NONE - - if(length(griddled_objects) >= max_items) - balloon_alert(user, "it's full!") - return ITEM_INTERACT_BLOCKING - - if(!istype(item, /obj/item/storage/bag/tray)) - // Non-tray dumping requires a do_after - to_chat(user, span_notice("You start dumping out the contents of [item] into [src]...")) - if(!do_after(user, 2 SECONDS, target = item)) - return ITEM_INTERACT_BLOCKING - - var/loaded = 0 - for(var/obj/tray_item in item) - if(!IS_EDIBLE(tray_item)) - continue - if(length(griddled_objects) >= max_items) - break - if(item.atom_storage.attempt_remove(tray_item, src)) - loaded++ - AddToGrill(tray_item, user) - if(loaded) - to_chat(user, span_notice("You insert [loaded] item\s into [src].")) - update_appearance() - return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_BLOCKING - /obj/machinery/griddle/attack_hand(mob/user, list/modifiers) . = ..() toggle_mode() diff --git a/code/modules/food_and_drinks/machinery/icecream_vat.dm b/code/modules/food_and_drinks/machinery/icecream_vat.dm index b6796e40107a..458a15ec29d9 100644 --- a/code/modules/food_and_drinks/machinery/icecream_vat.dm +++ b/code/modules/food_and_drinks/machinery/icecream_vat.dm @@ -114,50 +114,50 @@ . = ..() . += "You can use a [EXAMINE_HINT("spoon")] or [EXAMINE_HINT("soup ladle")] to spill reagents." -/obj/machinery/icecream_vat/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(.) - return - - if(istype(weapon, /obj/item/kitchen/spoon) || istype(weapon, /obj/item/kitchen/spoon/soup_ladle)) +/obj/machinery/icecream_vat/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/kitchen/spoon) || istype(tool, /obj/item/kitchen/spoon/soup_ladle)) spill_reagents(user) - return TRUE + return ITEM_INTERACT_SUCCESS - var/obj/item/reagent_containers/beaker = weapon + var/obj/item/reagent_containers/beaker = tool if(!istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) - return + return NONE if(custom_ice_cream_beaker) - if(user.transferItemToLoc(beaker, src)) - try_put_in_hand(custom_ice_cream_beaker, user) - balloon_alert(user, "beakers swapped") - custom_ice_cream_beaker = beaker - else + if(!user.transferItemToLoc(beaker, src)) balloon_alert(user, "beaker slot full!") - return + return ITEM_INTERACT_BLOCKING + + try_put_in_hand(custom_ice_cream_beaker, user) + balloon_alert(user, "beakers swapped") + custom_ice_cream_beaker = beaker + return ITEM_INTERACT_SUCCESS + if(!user.transferItemToLoc(beaker, src)) - return + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "beaker inserted") custom_ice_cream_beaker = beaker + return ITEM_INTERACT_SUCCESS -/obj/machinery/icecream_vat/attackby_secondary(obj/item/reagent_containers/beaker, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) - return +/obj/machinery/icecream_vat/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + var/obj/item/reagent_containers/beaker = tool if(!istype(beaker) || !beaker.reagents || (beaker.item_flags & ABSTRACT) || !beaker.is_open_container()) - return SECONDARY_ATTACK_CONTINUE_CHAIN + return NONE + var/added_reagents = FALSE for(var/datum/reagent/beaker_reagents in beaker.reagents.reagent_list) if(beaker_reagents.type in icecream_vat_reagents) added_reagents = TRUE beaker.reagents.trans_to(src, beaker_reagents.volume, target_id = beaker_reagents.type) - if(added_reagents) - balloon_alert(user, "refilling reagents") - playsound(src, 'sound/items/drink.ogg', 25, TRUE) - else + if(!added_reagents) balloon_alert(user, "no reagents to transfer!") - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "refilling reagents") + playsound(src, 'sound/items/drink.ogg', 25, TRUE) + return ITEM_INTERACT_SUCCESS /obj/machinery/icecream_vat/attack_hand_secondary(mob/user, list/modifiers) if(swap_modes(user)) diff --git a/code/modules/food_and_drinks/machinery/monkeyrecycler.dm b/code/modules/food_and_drinks/machinery/monkeyrecycler.dm index 83f7a7d0f172..67493bb89d29 100644 --- a/code/modules/food_and_drinks/machinery/monkeyrecycler.dm +++ b/code/modules/food_and_drinks/machinery/monkeyrecycler.dm @@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(monkey_recyclers) /obj/machinery/monkey_recycler/proc/stuff_monkey_in(mob/living/carbon/human/target, mob/living/user) if(!istype(target)) return - if(target.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(target)) to_chat(user, span_warning("The monkey is struggling far too much to put it in the recycler.")) return if(target.buckled || target.has_buckled_mobs()) diff --git a/code/modules/food_and_drinks/machinery/oven.dm b/code/modules/food_and_drinks/machinery/oven.dm index 95647c4bdbb9..7c763c3e8648 100644 --- a/code/modules/food_and_drinks/machinery/oven.dm +++ b/code/modules/food_and_drinks/machinery/oven.dm @@ -105,18 +105,19 @@ update_appearance() use_energy(active_power_usage) -/obj/machinery/oven/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(!open || used_tray || !istype(item, /obj/item/plate/oven_tray)) - return ..() +/obj/machinery/oven/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(open && used_tray && tool.atom_storage) + return used_tray.item_interaction(user, tool, modifiers) - if(user.transferItemToLoc(item, src, silent = FALSE)) - to_chat(user, span_notice("You put [item] in [src].")) - add_tray_to_oven(item, user) + if(!open || used_tray || !istype(tool, /obj/item/plate/oven_tray)) + return NONE -/obj/machinery/oven/item_interaction(mob/living/user, obj/item/item, list/modifiers) - if(open && used_tray && item.atom_storage) - return used_tray.item_interaction(user, item, modifiers) - return NONE + if(!user.transferItemToLoc(tool, src, silent = FALSE)) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You put [tool] in [src].")) + add_tray_to_oven(tool, user) + return ITEM_INTERACT_SUCCESS /obj/machinery/oven/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) if(open && used_tray && tool.atom_storage) diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm index 5abbd12a0be5..2064b561782e 100644 --- a/code/modules/food_and_drinks/machinery/processor.dm +++ b/code/modules/food_and_drinks/machinery/processor.dm @@ -200,7 +200,7 @@ GAME_VERB_SRC(/obj/machinery/processor, eject, oview(1), "Eject Contents", null) - if(usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) + if(IS_UNCONSCIOUS_OR_CRIT(usr) || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) return if(!usr.can_perform_action(src)) return @@ -253,7 +253,7 @@ GAME_VERB_SRC(/obj/machinery/processor, eject, oview(1), "Eject Contents", null) for(var/mob/living/basic/slime/slime in range(1,src)) if(!slime.IsReachableBy(src)) //don't take slimes behind glass panes or somesuch; also makes it ignore slimes inside the processor continue - if(slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(slime)) var/datum/food_processor_process/recipe = PROCESSOR_SELECT_RECIPE(slime) if(!recipe) continue diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm index 6a2effdfe978..edb20b25a908 100644 --- a/code/modules/food_and_drinks/recipes/food_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm @@ -126,7 +126,7 @@ if(!lastkey) return var/mob/living/user = get_mob_by_ckey(lastkey) - if(!istype(user) || user.stat || !is_in_sight(user, holder.my_atom)) + if(!istype(user) || IS_UNCONSCIOUS_OR_CRIT(user) || !is_in_sight(user, holder.my_atom)) return user.add_mood_event("why_on_earth_are_you_making_chocolate_pudding", /datum/mood_event/lost_control_of_life) diff --git a/code/modules/food_and_drinks/recipes/soup_guide.dm b/code/modules/food_and_drinks/recipes/soup_guide.dm index 8dba0dddc48c..8e7f08e9b914 100644 --- a/code/modules/food_and_drinks/recipes/soup_guide.dm +++ b/code/modules/food_and_drinks/recipes/soup_guide.dm @@ -47,14 +47,24 @@ reaction = /datum/chemical_reaction/food/soup/wingfangchu /datum/crafting_recipe/food/reaction/soup/hotchili + name = "Chili Con Carne (Meat Chili)" reaction = /datum/chemical_reaction/food/soup/hotchili + cuisine_category = CUISINE_MEXICAN + +/datum/crafting_recipe/food/reaction/soup/hotchili/ghost + name = "Ghost Chili Con Carne (Meat Chili)" + reaction = /datum/chemical_reaction/food/soup/hotchili/ghost /datum/crafting_recipe/food/reaction/soup/coldchili reaction = /datum/chemical_reaction/food/soup/coldchili + cuisine_category = CUISINE_MEXICAN + /* // DARKPACK EDIT REMOVAL /datum/crafting_recipe/food/reaction/soup/clownchili reaction = /datum/chemical_reaction/food/soup/clownchili - */ + cuisine_category = CUISINE_MEXICAN +*/ + /datum/crafting_recipe/food/reaction/soup/tomatosoup reaction = /datum/chemical_reaction/food/soup/tomatosoup @@ -136,6 +146,15 @@ /datum/crafting_recipe/food/reaction/soup/corn_chowder reaction = /datum/chemical_reaction/food/soup/corn_chowder +/datum/crafting_recipe/food/reaction/soup/chili_sin_carne + name = "Chili Sin Carne (Vegetarian Chili)" + reaction = /datum/chemical_reaction/food/soup/chili_sin_carne + cuisine_category = CUISINE_MEXICAN + +/datum/crafting_recipe/food/reaction/soup/chili_sin_carne/ghost + name = "Ghost Chili Sin Carne (Vegetarian Chili)" + reaction = /datum/chemical_reaction/food/soup/chili_sin_carne/ghost + // Other /datum/crafting_recipe/food/wishsoup diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm index 805840c51bdc..1fe85af0dc00 100644 --- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm @@ -486,8 +486,8 @@ // Chili (Hot, not cold) /datum/reagent/consumable/nutriment/soup/hotchili - name = "Hot Chili" - description = "A five alarm Texan Chili!" + name = "Chili Con Carne" + description = "An extra spicy five alarm Texan Chili." data = list("hot peppers" = 1) glass_price = FOOD_PRICE_NORMAL color = "#E23D12" @@ -520,9 +520,30 @@ ingredient_reagent_multiplier = 0.33 // Chilis have a TON of capsaicin naturally percentage_of_nutriment_converted = 0 +// Chili but hotter +/datum/reagent/consumable/nutriment/soup/hotchili/ghost + name = "Ghost Chili Con Carne" + description = "An extra-extra seven alarm Texan Chili. Apparently, it goes that high." + data = list("extremely hot peppers" = 2) + glass_price = FOOD_PRICE_NORMAL + color = "#ff9479" + +/datum/glass_style/has_foodtype/soup/hotchili/ghost + required_drink_type = /datum/reagent/consumable/nutriment/soup/hotchili/ghost + +/datum/chemical_reaction/food/soup/hotchili/ghost + +/datum/chemical_reaction/food/soup/hotchili/ghost/New() + . = ..() + required_ingredients[/obj/item/food/grown/ghost_chili] = required_ingredients[/obj/item/food/grown/chili] + required_ingredients -= /obj/item/food/grown/chili + results.Insert(1, /datum/reagent/consumable/nutriment/soup/hotchili/ghost) + results[/datum/reagent/consumable/nutriment/soup/hotchili/ghost] = results[/datum/reagent/consumable/nutriment/soup/hotchili] + results -= /datum/reagent/consumable/nutriment/soup/hotchili + // Chili (Cold) /datum/reagent/consumable/nutriment/soup/coldchili - name = "Cold Chili" + name = "Chili Frio" description = "This slush is barely a liquid!" data = list("tomato" = 1, "mint" = 1) glass_price = FOOD_PRICE_NORMAL @@ -609,6 +630,26 @@ /datum/reagent/consumable/tomatojuice = 10, ) +// Vegan Chili but hotter +/datum/reagent/consumable/nutriment/soup/chili_sin_carne/ghost + name = "Ghost Chili Sin Carne" + description = "For the hombres who don't want carne, but do want to feel like their mouth is on fire." + data = list("extremely hot peppers" = 2) + color = "#ff9479" + +/datum/glass_style/has_foodtype/soup/chili_sin_carne/ghost + required_drink_type = /datum/reagent/consumable/nutriment/soup/chili_sin_carne/ghost + +/datum/chemical_reaction/food/soup/chili_sin_carne/ghost + +/datum/chemical_reaction/food/soup/chili_sin_carne/ghost/New() + . = ..() + required_ingredients[/obj/item/food/grown/ghost_chili] = required_ingredients[/obj/item/food/grown/chili] + required_ingredients -= /obj/item/food/grown/chili + results.Insert(1, /datum/reagent/consumable/nutriment/soup/chili_sin_carne/ghost) + results[/datum/reagent/consumable/nutriment/soup/chili_sin_carne/ghost] = results[/datum/reagent/consumable/nutriment/soup/chili_sin_carne] + results -= /datum/reagent/consumable/nutriment/soup/chili_sin_carne + // Tomato soup /datum/reagent/consumable/nutriment/soup/tomato name = "Tomato Soup" diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm index dbf6e86c3ca7..d7137730e966 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm @@ -242,7 +242,7 @@ name = "Spaceman's Cake" reqs = list( /obj/item/food/cake/plain = 1, - /obj/item/food/grown/trumpet = 2, + /obj/item/food/grown/flower/trumpet = 2, /datum/reagent/consumable/cream = 5, /datum/reagent/consumable/berryjuice = 5 ) diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm index a0e6f104d247..48c79b3dc2cd 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm @@ -19,6 +19,7 @@ result = /obj/item/stack/sheet/runed_metal category = CAT_CULT non_craftable = TRUE + crafting_flags = parent_type::crafting_flags | CRAFT_MUST_BE_LEARNED steps = list("Use Twisted Construction on plasteel") /datum/crafting_recipe/constructshell @@ -26,8 +27,111 @@ result = /obj/structure/constructshell category = CAT_CULT non_craftable = TRUE + crafting_flags = parent_type::crafting_flags | CRAFT_MUST_BE_LEARNED steps = list("Use Twisted Construction on iron") */ + +// Things related to cement and concrete + +/datum/crafting_recipe/concrete_wall + result = /datum/reagent/concrete::wall_type + reqs = list( + /obj/structure/grille = 1, + /datum/reagent/concrete = /datum/reagent/concrete::units_per_wall, + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Pour concrete over a grille") + +/datum/crafting_recipe/concrete_wall/hexacrete + result = /datum/reagent/concrete/hexacrete::wall_type + reqs = list( + /obj/structure/girder = 1, + /datum/reagent/concrete/hexacrete = /datum/reagent/concrete/hexacrete::units_per_wall, + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Pour hexacrete over a girder") + +/datum/crafting_recipe/concrete_tile + result = /turf/open/floor/concrete + reqs = list( + /turf/open/floor/catwalk_floor = 1, + /datum/reagent/concrete = /datum/reagent/concrete::units_per_floor, + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Pour concrete onto a catwalk floor") + +/datum/crafting_recipe/concrete_tile/tiles + result = /turf/open/floor/concrete/tiles + reqs = list( + /turf/open/floor/concrete = 1, + /obj/item/chisel = 1 + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Form the pattern on a wet tile") + +/datum/crafting_recipe/concrete_tile/reinforced + result = /turf/open/floor/concrete/reinforced + reqs = list( + /turf/open/floor/catwalk_floor = 1, + /datum/reagent/concrete/hexacrete = /datum/reagent/concrete/hexacrete::units_per_floor, + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Pour hexacrete onto a catwalk floor") + +/datum/crafting_recipe/concrete_tile/pavement + result = /turf/open/floor/concrete/pavement + reqs = list( + /turf/open/floor/catwalk_floor = 1, + /datum/reagent/concrete/pavement = /datum/reagent/concrete/pavement::units_per_floor, + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Pour pavement concrete onto a catwalk floor") + +/datum/crafting_recipe/concrete + result = /datum/reagent/concrete + reqs = list( + /datum/reagent/cement = /datum/reagent/cement::units_per_aggregate, + /obj/item/stack/ore/glass = 1 + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Add sand to the cement") + +/datum/crafting_recipe/concrete/hexacrete + result = /datum/reagent/concrete/hexacrete + reqs = list( + /datum/reagent/cement/hexement = /datum/reagent/cement/hexement::units_per_aggregate, + /obj/item/stack/ore/glass = 1 + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Add sand to the hexement") + +/datum/crafting_recipe/concrete/pavement + result = /datum/reagent/concrete/pavement + reqs = list( + /datum/reagent/cement/pavement = /datum/reagent/cement/pavement::units_per_aggregate, + /obj/item/stack/ore/glass = 1 + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Add sand to the road mix") + +/datum/crafting_recipe/wall_support + result = /obj/structure/wall_support + reqs = list( + /obj/structure/wall_support::rods_type = /obj/structure/wall_support::rods_amount + ) + category = CAT_CONSTRUCTION + non_craftable = TRUE + steps = list("Use rods on the wall") + // Food reactions /datum/crafting_recipe/food/reaction @@ -973,7 +1077,7 @@ meal_category = MEAL_SNACK /datum/crafting_recipe/food/drying/semki - reqs = list(/obj/item/food/grown/sunflower = 1) + reqs = list(/obj/item/food/grown/flower/sunflower = 1) result = /obj/item/food/semki/healthy meal_category = MEAL_SNACK diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm index 9c4a41da1d04..ebdebb22f1fb 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm @@ -447,9 +447,4 @@ /datum/crafting_recipe/food/reaction/soup/cheese_porridge reaction = /datum/chemical_reaction/food/soup/cheese_porridge cuisine_category = CUISINE_MOTHIC - -/datum/crafting_recipe/food/reaction/soup/chili_sin_carne - name = "Chili Sin Carne (Vegetarian Chili)" - reaction = /datum/chemical_reaction/food/soup/chili_sin_carne - cuisine_category = CUISINE_MOTHIC */ diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm index 054bcd397b11..82118dc3e5dc 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -432,7 +432,7 @@ result = /obj/item/food/donkpocket/deluxe dish_category = DISH_PASTRY removed_foodtypes = BREAKFAST - crafting_flags = parent_type::crafting_flags | CRAFT_MUST_BE_LEARNED + // crafting_flags = parent_type::crafting_flags | CRAFT_MUST_BE_LEARNED // DARKPACK EDIT REMOVAL - (Causes a strange compile fail i dont care to fix.) /datum/crafting_recipe/food/donkpocket/deluxe/nocarb time = 1.5 SECONDS diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm index 3d7fab599257..f72e0e75ab18 100644 --- a/code/modules/forensics/forensics_helpers.dm +++ b/code/modules/forensics/forensics_helpers.dm @@ -36,7 +36,7 @@ if (QDELETED(src)) return var/old = 0 - if(suspect.gloves && istype(suspect.gloves, /obj/item/clothing)) + if(suspect.gloves && isclothing(suspect.gloves)) var/obj/item/clothing/gloves/suspect_gloves = suspect.gloves old = GET_ATOM_BLOOD_DNA_LENGTH(suspect_gloves) if(suspect_gloves.transfer_blood > 1) //bloodied gloves transfer blood to touched objects diff --git a/code/modules/hallucination/body.dm b/code/modules/hallucination/body.dm index f7b714815ce9..99b89278d118 100644 --- a/code/modules/hallucination/body.dm +++ b/code/modules/hallucination/body.dm @@ -17,7 +17,7 @@ /datum/hallucination/body/start() // This hallucination is purely visual, so we don't need to bother for clientless mobs - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/possible_points = list() diff --git a/code/modules/hallucination/delusions.dm b/code/modules/hallucination/delusions.dm index 8171e07716d9..d45ef6a5c4b9 100644 --- a/code/modules/hallucination/delusions.dm +++ b/code/modules/hallucination/delusions.dm @@ -62,7 +62,7 @@ return ..() /datum/hallucination/delusion/start() - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE feedback_details += "Delusion: [delusion_name]" diff --git a/code/modules/hallucination/eyes_in_dark.dm b/code/modules/hallucination/eyes_in_dark.dm index 3a4fcac49376..9c127fb4e14f 100644 --- a/code/modules/hallucination/eyes_in_dark.dm +++ b/code/modules/hallucination/eyes_in_dark.dm @@ -12,7 +12,7 @@ return ..() /datum/hallucination/eyes_in_dark/start() - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE if(hallucinator.lighting_cutoff >= 2.5) diff --git a/code/modules/hallucination/fake_chat.dm b/code/modules/hallucination/fake_chat.dm index 0572de1f22fc..72542420c90e 100644 --- a/code/modules/hallucination/fake_chat.dm +++ b/code/modules/hallucination/fake_chat.dm @@ -24,7 +24,7 @@ return what_they_speak?.spoken_languages?.Copy() || list() /datum/hallucination/chat/start() - if(hallucinator.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(hallucinator)) return FALSE var/mob/living/carbon/human/speaker diff --git a/code/modules/hallucination/fake_death.dm b/code/modules/hallucination/fake_death.dm index 25f4f00372ad..744e4a68d056 100644 --- a/code/modules/hallucination/fake_death.dm +++ b/code/modules/hallucination/fake_death.dm @@ -15,7 +15,7 @@ return ..() /datum/hallucination/death/start() - if(hallucinator.stat >= UNCONSCIOUS) // on your way there already + if(IS_UNCONSCIOUS(hallucinator)) // on your way there already return FALSE if(floor_them) diff --git a/code/modules/hallucination/fake_message.dm b/code/modules/hallucination/fake_message.dm index e0aa9cb5034e..f981a8e25fc7 100644 --- a/code/modules/hallucination/fake_message.dm +++ b/code/modules/hallucination/fake_message.dm @@ -3,7 +3,7 @@ hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/message/start() - if(hallucinator.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/nearby_humans = list() diff --git a/code/modules/hallucination/hazard.dm b/code/modules/hallucination/hazard.dm index a2c0f77a4b89..1471a615292d 100644 --- a/code/modules/hallucination/hazard.dm +++ b/code/modules/hallucination/hazard.dm @@ -8,7 +8,7 @@ var/hazard_type = /obj/effect/client_image_holder/hallucination/danger /datum/hallucination/hazard/start() - if(hallucinator.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/possible_points = list() diff --git a/code/modules/hallucination/inhand_fake_item.dm b/code/modules/hallucination/inhand_fake_item.dm index 011a057720ba..8f838a2654fe 100644 --- a/code/modules/hallucination/inhand_fake_item.dm +++ b/code/modules/hallucination/inhand_fake_item.dm @@ -10,7 +10,7 @@ var/obj/item/template_item_type /datum/hallucination/fake_item/start() - if(hallucinator.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/slots_free = list() diff --git a/code/modules/hallucination/malf_apc.dm b/code/modules/hallucination/malf_apc.dm index ae4beea2b285..33206f1b19df 100644 --- a/code/modules/hallucination/malf_apc.dm +++ b/code/modules/hallucination/malf_apc.dm @@ -11,7 +11,7 @@ VAR_PRIVATE/image/hacked_image /datum/hallucination/malf_apc/start() - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE var/num_ais = 0 diff --git a/code/modules/hallucination/mother.dm b/code/modules/hallucination/mother.dm index 2d9a7ad6b593..bfefa14929aa 100644 --- a/code/modules/hallucination/mother.dm +++ b/code/modules/hallucination/mother.dm @@ -6,7 +6,7 @@ var/obj/effect/client_image_holder/hallucination/your_mother/mother /datum/hallucination/your_mother/start() - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/spawn_locs = list() diff --git a/code/modules/hallucination/nearby_fake_item.dm b/code/modules/hallucination/nearby_fake_item.dm index 9ccab0fbb683..bbfaba85da95 100644 --- a/code/modules/hallucination/nearby_fake_item.dm +++ b/code/modules/hallucination/nearby_fake_item.dm @@ -21,7 +21,7 @@ /datum/hallucination/nearby_fake_item/start() // This hallucination is purely visual, so we don't need to bother for clientless mobs - if(!hallucinator.client || hallucinator.stat >= UNCONSCIOUS) + if(!hallucinator.client || IS_UNCONSCIOUS(hallucinator)) return FALSE var/list/mob_pool = list() diff --git a/code/modules/hallucination/xeno_attack.dm b/code/modules/hallucination/xeno_attack.dm index b56424ab38a1..e1126bfe0d1a 100644 --- a/code/modules/hallucination/xeno_attack.dm +++ b/code/modules/hallucination/xeno_attack.dm @@ -4,7 +4,7 @@ hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/xeno_attack/start() - if(hallucinator.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(hallucinator)) return FALSE var/turf/xeno_attack_source diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index 1e347372bc08..965dc88976a2 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -158,7 +158,7 @@ GLOBAL_LIST_INIT(holiday_mail, list()) holiday_mail = list( /obj/item/food/bonbon/chocolate_truffle, /obj/item/food/candyheart, - /obj/item/food/grown/rose, + /obj/item/food/grown/flower/rose, ) /datum/holiday/valentines/get_station_name() @@ -335,7 +335,7 @@ GLOBAL_LIST_INIT(holiday_mail, list()) timezones = list(TIMEZONE_TKT, TIMEZONE_TOT, TIMEZONE_NZST, TIMEZONE_NFT, TIMEZONE_LHST, TIMEZONE_AEST, TIMEZONE_ACST, TIMEZONE_ACWST, TIMEZONE_AWST, TIMEZONE_CXT, TIMEZONE_CCT, TIMEZONE_CKT, TIMEZONE_NUT) begin_day = 25 begin_month = APRIL - holiday_hat = /obj/item/food/grown/poppy + holiday_hat = /obj/item/food/grown/flower/poppy /datum/holiday/anz/get_station_name() return pick("Australian","New Zealand","Poppy", "Southern Cross") @@ -482,7 +482,7 @@ GLOBAL_LIST_INIT(holiday_mail, list()) /obj/item/clothing/under/color/jumpskirt/rainbow, /obj/item/clothing/under/color/rainbow, /obj/item/food/egg/rainbow, - /obj/item/food/grown/rainbow_flower, + /obj/item/food/grown/flower/rainbow_flower, /obj/item/food/snowcones/rainbow, /obj/item/toy/crayon/rainbow, ) @@ -770,10 +770,10 @@ GLOBAL_LIST_INIT(holiday_mail, list()) name = "Remembrance Day" begin_month = NOVEMBER begin_day = 11 - holiday_hat = /obj/item/food/grown/poppy + holiday_hat = /obj/item/food/grown/flower/poppy holiday_mail = list( - /obj/item/food/grown/harebell, - /obj/item/food/grown/poppy, + /obj/item/food/grown/flower/harebell, + /obj/item/food/grown/flower/poppy, /obj/item/storage/fancy/candle_box, ) @@ -800,16 +800,16 @@ GLOBAL_LIST_INIT(holiday_mail, list()) name = "Flowers Day" begin_day = 19 begin_month = NOVEMBER - holiday_hat = /obj/item/food/grown/moonflower + holiday_hat = /obj/item/food/grown/flower/moonflower holiday_mail = list( - /obj/item/food/grown/harebell, - /obj/item/food/grown/moonflower, - /obj/item/food/grown/poppy, - /obj/item/food/grown/poppy/geranium, - /obj/item/food/grown/poppy/geranium/fraxinella, - /obj/item/food/grown/poppy/lily, - /obj/item/food/grown/rose, - /obj/item/food/grown/sunflower, + /obj/item/food/grown/flower/harebell, + /obj/item/food/grown/flower/moonflower, + /obj/item/food/grown/flower/poppy, + /obj/item/food/grown/flower/poppy/geranium, + /obj/item/food/grown/flower/poppy/geranium/fraxinella, + /obj/item/food/grown/flower/poppy/lily, + /obj/item/food/grown/flower/rose, + /obj/item/food/grown/flower/sunflower, /obj/item/grown/carbon_rose, /obj/item/grown/novaflower, ) diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm index dfa1526dbb68..98904a13ce4b 100644 --- a/code/modules/holodeck/items.dm +++ b/code/modules/holodeck/items.dm @@ -151,10 +151,12 @@ /obj/machinery/conveyor/holodeck -/obj/machinery/conveyor/holodeck/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(!user.transferItemToLoc(I, drop_location())) +/obj/machinery/conveyor/holodeck/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!user.transferItemToLoc(tool, drop_location())) return ..() + return ITEM_INTERACT_SUCCESS + /obj/item/paper/fluff/holodeck/trek_diploma name = "paper - Starfleet Academy Diploma" default_raw_text = {"

    Starfleet Academy


    Official Diploma


    "} diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm index de5b5aa5ab0e..f66fd88ab451 100644 --- a/code/modules/hydroponics/beekeeping/beebox.dm +++ b/code/modules/hydroponics/beekeeping/beebox.dm @@ -5,24 +5,6 @@ #define BEE_RESOURCE_HONEYCOMB_COST 100 //The amount of bee_resources for a new honeycomb to be produced, percentage cost 1-100 #define BEE_RESOURCE_NEW_BEE_COST 50 //The amount of bee_resources for a new bee to be produced, percentage cost 1-100 - - -/mob/proc/bee_friendly() - return 0 - - - -/mob/living/carbon/human/bee_friendly() - if(ispodperson(src)) //bees pollinate plants, duh. - return 1 - if (wear_suit && head && isclothing(wear_suit) && isclothing(head)) - var/obj/item/clothing/suit = wear_suit - var/obj/item/clothing/hat = head - if (suit.clothing_flags & hat.clothing_flags & THICKMATERIAL) - return 1 - return 0 - - /obj/structure/beebox name = "apiary" desc = "Dr. Miles Manners is just your average wasp-themed super hero by day, but by night he becomes DR. BEES!" @@ -37,12 +19,10 @@ var/list/honey_frames = list() var/bee_resources = 0 - /obj/structure/beebox/Initialize(mapload) . = ..() START_PROCESSING(SSobj, src) - /obj/structure/beebox/Destroy() STOP_PROCESSING(SSobj, src) queen_bee = null @@ -51,7 +31,6 @@ QDEL_LIST(honeycombs) return ..() - //Premade apiaries can spawn with a random reagent /obj/structure/beebox/premade var/random_reagent = FALSE @@ -192,7 +171,7 @@ return NONE -/obj/structure/beebox/interact(mob/user) +/obj/structure/beebox/interact(mob/living/user) . = ..() if(!user.bee_friendly()) //Time to get stung! diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm index b4c15f221a24..2e2418d1ef8b 100644 --- a/code/modules/hydroponics/fermenting_barrel.dm +++ b/code/modules/hydroponics/fermenting_barrel.dm @@ -57,23 +57,26 @@ else . += span_notice("It is currently closed, letting it ferment fruits or draw reagents from its tap.") -/obj/structure/fermenting_barrel/attackby(obj/item/object, mob/user, list/modifiers, list/attack_modifiers) +/obj/structure/fermenting_barrel/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(open) - if(istype(object, /obj/item/food/grown) && insert_fruit(user, object)) + if(istype(tool, /obj/item/food/grown) && insert_fruit(user, tool)) balloon_alert(user, "added fruit") - return - if(istype(object, /obj/item/storage/bag/plants)) - var/obj/item/storage/bag/plants/bag = object + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/storage/bag/plants)) + var/obj/item/storage/bag/plants/bag = tool var/inserted_fruits = 0 for(var/obj/item/food/grown/fruit in bag.contents) if(!insert_fruit(user, fruit, bag)) break inserted_fruits++ - if(inserted_fruits) - balloon_alert(user, "added [inserted_fruits] fruit\s") - else if(object.is_refillable()) - return //so we can refill them via their afterattack. - return ..() + if(!inserted_fruits) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "added [inserted_fruits] fruit\s") + return ITEM_INTERACT_SUCCESS + + return NONE /obj/structure/fermenting_barrel/attack_hand(mob/user, list/modifiers) if(!can_open) diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 9b58432ec445..d5533c0ec0dd 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -6,7 +6,6 @@ abstract_type = /obj/item/food/grown/citrus foodtypes = FRUIT wine_power = 30 - seed = /obj/item/seeds/lime // Lime /obj/item/seeds/lime diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 691e0a389d6b..62297df1e716 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -1,3 +1,11 @@ +//Flower - base type +/obj/item/food/grown/flower + name = "Flower" + desc = "By any other name...? This doesn't look right, you should tell someone about this." + icon_state = "rose" + abstract_type = /obj/item/food/grown/flower + foodtypes = VEGETABLES + // Poppy /obj/item/seeds/poppy name = "poppy seed pack" @@ -5,7 +13,7 @@ icon_state = "seed-poppy" species = "poppy" plantname = "Poppy Plants" - product = /obj/item/food/grown/poppy + product = /obj/item/food/grown/flower/poppy endurance = 10 maturation = 8 yield = 6 @@ -19,7 +27,7 @@ mutatelist = list(/obj/item/seeds/poppy/geranium, /obj/item/seeds/poppy/lily) reagents_add = list(/datum/reagent/medicine/c2/libital = 0.2, /datum/reagent/consumable/nutriment = 0.05) -/obj/item/food/grown/poppy +/obj/item/food/grown/flower/poppy seed = /obj/item/seeds/poppy name = "poppy" desc = "Long-used as a symbol of rest, peace, and death." @@ -37,7 +45,7 @@ icon_state = "seed-lily" species = "lily" plantname = "Lily Plants" - product = /obj/item/food/grown/poppy/lily + product = /obj/item/food/grown/flower/poppy/lily growthstages = 3 growing_icon = 'icons/obj/service/hydroponics/growing_flowers.dmi' icon_grow = "lily-grow" @@ -45,7 +53,7 @@ genes = list(/datum/plant_gene/trait/preserved) mutatelist = list(/obj/item/seeds/poppy/lily/trumpet) -/obj/item/food/grown/poppy/lily +/obj/item/food/grown/flower/poppy/lily seed = /obj/item/seeds/poppy/lily name = "lily" desc = "A beautiful white flower." @@ -58,7 +66,7 @@ icon_state = "seed-trumpet" species = "spacemanstrumpet" plantname = "Spaceman's Trumpet Plant" - product = /obj/item/food/grown/trumpet + product = /obj/item/food/grown/flower/trumpet lifespan = 80 production = 5 endurance = 10 @@ -77,13 +85,12 @@ rarity = 30 graft_gene = /datum/plant_gene/reagent/preset/polypyr -/obj/item/food/grown/trumpet +/obj/item/food/grown/flower/trumpet seed = /obj/item/seeds/poppy/lily/trumpet name = "spaceman's trumpet" desc = "A vivid flower that smells faintly of freshly cut grass. Touching the flower seems to stain the skin some time after contact, yet most other surfaces seem to be unaffected by this phenomenon." icon_state = "spacemanstrumpet" bite_consumption_mod = 2 - foodtypes = VEGETABLES // Geranium /obj/item/seeds/poppy/geranium @@ -92,7 +99,7 @@ icon_state = "seed-geranium" species = "geranium" plantname = "Geranium Plants" - product = /obj/item/food/grown/poppy/geranium + product = /obj/item/food/grown/flower/poppy/geranium growthstages = 3 growing_icon = 'icons/obj/service/hydroponics/growing_flowers.dmi' icon_grow = "geranium-grow" @@ -100,7 +107,7 @@ genes = list(/datum/plant_gene/trait/preserved) mutatelist = list(/obj/item/seeds/poppy/geranium/fraxinella) -/obj/item/food/grown/poppy/geranium +/obj/item/food/grown/flower/poppy/geranium seed = /obj/item/seeds/poppy/geranium name = "geranium" desc = "A beautiful blue flower." @@ -113,14 +120,14 @@ icon_state = "seed-fraxinella" species = "fraxinella" plantname = "Fraxinella Plants" - product = /obj/item/food/grown/poppy/geranium/fraxinella + product = /obj/item/food/grown/flower/poppy/geranium/fraxinella mutatelist = null rarity = 15 reagents_add = list(/datum/reagent/consumable/nutriment = 0.05, /datum/reagent/fuel/oil = 0.05) graft_gene = /datum/plant_gene/trait/preserved ///Fraxinella Flowers. -/obj/item/food/grown/poppy/geranium/fraxinella +/obj/item/food/grown/flower/poppy/geranium/fraxinella seed = /obj/item/seeds/poppy/geranium/fraxinella name = "fraxinella" desc = "A beautiful light pink flower." @@ -135,7 +142,7 @@ plant_icon_offset = 1 species = "harebell" plantname = "Harebells" - product = /obj/item/food/grown/harebell + product = /obj/item/food/grown/flower/harebell lifespan = 100 endurance = 20 maturation = 7 @@ -149,7 +156,7 @@ reagents_add = list(/datum/reagent/consumable/nutriment = 0.04) graft_gene = /datum/plant_gene/trait/plant_type/weed_hardy -/obj/item/food/grown/harebell +/obj/item/food/grown/flower/harebell seed = /obj/item/seeds/harebell name = "harebell" desc = "\"I'll sweeten thy sad grave: thou shalt not lack the flower that's like thy face, pale primrose, nor the azured hare-bell, like thy veins; no, nor the leaf of eglantine, whom not to slander, out-sweeten'd not thy breath.\"" @@ -166,7 +173,7 @@ icon_state = "seed-sunflower" species = "sunflower" plantname = "Sunflowers" - product = /obj/item/food/grown/sunflower + product = /obj/item/food/grown/flower/sunflower genes = list(/datum/plant_gene/trait/attack/sunflower_attack, /datum/plant_gene/trait/preserved) endurance = 20 production = 2 @@ -179,7 +186,7 @@ mutatelist = list(/obj/item/seeds/sunflower/moonflower, /obj/item/seeds/sunflower/novaflower) reagents_add = list(/datum/reagent/consumable/nutriment/fat/oil = 0.08, /datum/reagent/consumable/nutriment = 0.04) -/obj/item/food/grown/sunflower // FLOWER POWER! +/obj/item/food/grown/flower/sunflower // FLOWER POWER! seed = /obj/item/seeds/sunflower name = "sunflower" desc = "It's beautiful! A certain person might beat you to death if you trample these." @@ -187,7 +194,6 @@ inhand_icon_state = "sunflower" lefthand_file = 'icons/mob/inhands/weapons/plants_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/plants_righthand.dmi' - foodtypes = VEGETABLES damtype = BURN force = 0 slot_flags = ITEM_SLOT_HEAD @@ -197,7 +203,7 @@ throw_speed = 1 throw_range = 3 -/obj/item/food/grown/sunflower/make_dryable() +/obj/item/food/grown/flower/sunflower/make_dryable() AddElement(/datum/element/dryable, /obj/item/food/semki/healthy) //yum // Moonflower @@ -211,14 +217,14 @@ plantname = "Moonflowers" icon_grow = "moonflower-grow" icon_dead = "sunflower-dead" - product = /obj/item/food/grown/moonflower + product = /obj/item/food/grown/flower/moonflower genes = list(/datum/plant_gene/trait/glow/purple, /datum/plant_gene/trait/preserved) mutatelist = null reagents_add = list(/datum/reagent/consumable/ethanol/moonshine = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.02, /datum/reagent/consumable/nutriment = 0.02) rarity = 15 graft_gene = /datum/plant_gene/trait/glow/purple -/obj/item/food/grown/moonflower +/obj/item/food/grown/flower/moonflower seed = /obj/item/seeds/sunflower/moonflower name = "moonflower" desc = "Store in a location at least 50 yards away from werewolves." @@ -272,7 +278,7 @@ icon_state = "seed-rose" species = "rose" plantname = "Rose Bush" - product = /obj/item/food/grown/rose + product = /obj/item/food/grown/flower/rose endurance = 12 yield = 6 potency = 15 @@ -286,7 +292,7 @@ //Roses are commonly used as herbal medicines (diarrhodons) and for their 'rose oil'. reagents_add = list(/datum/reagent/consumable/nutriment = 0.05, /datum/reagent/medicine/granibitaluri = 0.1, /datum/reagent/fuel/oil = 0.05) -/obj/item/food/grown/rose +/obj/item/food/grown/flower/rose seed = /obj/item/seeds/rose name = "\improper rose" desc = "The classic fleur d'amour - flower of love. Watch for its thorns!" @@ -301,7 +307,7 @@ bite_consumption_mod = 2 foodtypes = VEGETABLES | GROSS -/obj/item/food/grown/rose/equipped(mob/user, slot, initial) +/obj/item/food/grown/flower/rose/equipped(mob/user, slot, initial) . = ..() if(slot & ITEM_SLOT_MASK) worn_icon_state = "[base_icon_state]_mouth" diff --git a/code/modules/hydroponics/grown/hedges.dm b/code/modules/hydroponics/grown/hedges.dm index d6bf1f49512b..4401e1a65a9e 100644 --- a/code/modules/hydroponics/grown/hedges.dm +++ b/code/modules/hydroponics/grown/hedges.dm @@ -26,7 +26,7 @@ if(player_turf?.is_blocked_turf(TRUE)) return FALSE user.visible_message(span_danger("[user] begins to plant \the [src]...")) - if(do_after(user, 8 SECONDS, target = user.drop_location(), progress = TRUE)) + if(do_after(user, 8 SECONDS, target = user.drop_location())) new /obj/structure/hedge/opaque(user.drop_location()) to_chat(user, span_notice("You plant \the [src].")) qdel(src) diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm index 66348d6a3a72..1c6cd400154d 100644 --- a/code/modules/hydroponics/grown/melon.dm +++ b/code/modules/hydroponics/grown/melon.dm @@ -1,5 +1,4 @@ /obj/item/food/grown/melonlike - seed = /obj/item/seeds/watermelon name = "melon?" desc = "You felt like this was a melon, but it definitely isn't. You should tell somebody about this." icon_state = "watermelon" diff --git a/code/modules/hydroponics/grown/rainbow_bunch.dm b/code/modules/hydroponics/grown/rainbow_bunch.dm index 655184c874fd..d89ff5027780 100644 --- a/code/modules/hydroponics/grown/rainbow_bunch.dm +++ b/code/modules/hydroponics/grown/rainbow_bunch.dm @@ -5,7 +5,7 @@ species = "rainbowbunch" plantname = "Rainbow Flowers" icon_harvest = "rainbowbunch-harvest" - product = /obj/item/food/grown/rainbow_flower + product = /obj/item/food/grown/flower/rainbow_flower lifespan = 25 endurance = 10 maturation = 6 @@ -19,7 +19,7 @@ genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/preserved) reagents_add = list(/datum/reagent/consumable/nutriment = 0.05) -/obj/item/food/grown/rainbow_flower +/obj/item/food/grown/flower/rainbow_flower seed = /obj/item/seeds/rainbow_bunch name = "rainbow flower" desc = "A beautiful flower capable of being used for most dyeing processes." @@ -34,12 +34,12 @@ attack_verb_simple = list("pompf") greyscale_colors = COLOR_BLACK //only here for unit testing. overriden in initialize() icon = 'icons/map_icons/items/_item.dmi' - icon_state = "/obj/item/food/grown/rainbow_flower" + icon_state = "/obj/item/food/grown/flower/rainbow_flower" post_init_icon_state = "map_flower" greyscale_config = /datum/greyscale_config/flower_simple greyscale_config_worn = /datum/greyscale_config/flower_simple_worn -/obj/item/food/grown/rainbow_flower/Initialize(mapload) +/obj/item/food/grown/flower/rainbow_flower/Initialize(mapload) . = ..() if(greyscale_colors != initial(greyscale_colors)) return diff --git a/code/modules/hydroponics/grown/root.dm b/code/modules/hydroponics/grown/root.dm index fd5de5d0dfcd..638896d895dc 100644 --- a/code/modules/hydroponics/grown/root.dm +++ b/code/modules/hydroponics/grown/root.dm @@ -1,5 +1,4 @@ /obj/item/food/grown/carrotlike - seed = /obj/item/seeds/carrot name = "carrot?" desc = "What is this? It's not a carrot, that's for sure. You should tell someone about this." icon_state = "carrot" diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index cf7b7411c485..1a46f0eb6073 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -84,28 +84,28 @@ return NONE -/obj/item/grown/log/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(attacking_item.get_sharpness()) - +/obj/item/grown/log/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(tool.get_sharpness()) user.balloon_alert(user, "made [plank_count] [plank_name]") new plank_type(user.loc, plank_count) qdel(src) - return + return ITEM_INTERACT_SUCCESS - if(CheckAccepted(attacking_item)) - var/obj/item/food/grown/leaf = attacking_item - if(HAS_TRAIT(leaf, TRAIT_DRIED)) - user.balloon_alert(user, "torch crafted") - var/obj/item/flashlight/flare/torch/new_torch = new /obj/item/flashlight/flare/torch(user.loc) - user.dropItemToGround(attacking_item) - user.put_in_active_hand(new_torch) - qdel(leaf) - qdel(src) - return - else + if(CheckAccepted(tool)) + var/obj/item/food/grown/leaf = tool + if(!HAS_TRAIT(leaf, TRAIT_DRIED)) balloon_alert(user, "dry it first!") - else - return ..() + return ITEM_INTERACT_BLOCKING + + user.balloon_alert(user, "torch crafted") + var/obj/item/flashlight/flare/torch/new_torch = new /obj/item/flashlight/flare/torch(user.loc) + user.dropItemToGround(tool) + user.put_in_active_hand(new_torch) + qdel(leaf) + qdel(src) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/item/grown/log/proc/CheckAccepted(obj/item/I) return is_type_in_typecache(I, accepted) diff --git a/code/modules/hydroponics/grown/weeds/kudzu.dm b/code/modules/hydroponics/grown/weeds/kudzu.dm index 87241365cf49..9160aa8c860b 100644 --- a/code/modules/hydroponics/grown/weeds/kudzu.dm +++ b/code/modules/hydroponics/grown/weeds/kudzu.dm @@ -46,7 +46,7 @@ /obj/item/seeds/kudzu/attack_self(mob/user) user.visible_message(span_danger("[user] begins throwing seeds on the ground...")) - if(do_after(user, 5 SECONDS, target = user.drop_location(), progress = TRUE)) + if(do_after(user, 5 SECONDS, target = user.drop_location())) plant(user) to_chat(user, span_notice("You plant the kudzu. You monster.")) diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 66a38bd1edca..a33556ceb700 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -99,6 +99,7 @@ icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "hatchet" inhand_icon_state = "hatchet" + worn_icon_state = "hatchet" icon_angle = -135 lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' @@ -151,6 +152,7 @@ icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "scythe0" inhand_icon_state = "scythe0" + worn_icon_state = "scythe0" icon_angle = -45 lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi' @@ -213,7 +215,7 @@ icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "secateurs" inhand_icon_state = null - worn_icon_state = "cutters" + worn_icon_state = "secateurs" icon_angle = -135 lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' @@ -251,7 +253,7 @@ icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "genesheers" inhand_icon_state = null - worn_icon_state = "cutters" + worn_icon_state = "geneshears" lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' obj_flags = CONDUCTS_ELECTRICITY diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 1a2fce540264..6855bb760f43 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -909,18 +909,17 @@ default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/machinery/hydroponics/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers) - //Called when mob user "attacks" it with object O - if(IS_EDIBLE(O) || is_reagent_container(O)) // Syringe stuff (and other reagent containers now too) - var/obj/item/reagent_containers/reagent_source = O +/obj/machinery/hydroponics/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(IS_EDIBLE(tool) || is_reagent_container(tool)) // Syringe stuff (and other reagent containers now too) + var/obj/item/reagent_containers/reagent_source = tool if(!reagent_source.reagents.total_volume) to_chat(user, span_warning("[reagent_source] is empty!")) - return 1 + return ITEM_INTERACT_BLOCKING if(reagents.total_volume >= reagents.maximum_volume && !reagent_source.reagents.has_reagent(/datum/reagent/water, 1)) to_chat(user, span_notice("[src] is full.")) - return + return ITEM_INTERACT_BLOCKING var/list/trays = list(src)//makes the list just this in cases of syringes and compost etc var/target = myseed ? myseed.plantname : src @@ -929,9 +928,9 @@ if(IS_EDIBLE(reagent_source)) if(HAS_TRAIT(reagent_source, TRAIT_UNCOMPOSTABLE)) - to_chat(user, "[reagent_source] cannot be composted in its current state") - return - visi_msg="[user] composts [reagent_source], spreading it through [target]" + to_chat(user, "[reagent_source] cannot be composted in its current state.") + return ITEM_INTERACT_BLOCKING + visi_msg = "[user] composts [reagent_source], spreading it through [target]" transfer_amount = reagent_source.reagents.total_volume SEND_SIGNAL(reagent_source, COMSIG_ITEM_ON_COMPOSTED, user) if((tray_flags & WORM_HABITAT) && prob(transfer_amount / 2)) @@ -942,8 +941,7 @@ else transfer_amount = min(reagent_source.amount_per_transfer_from_this, reagent_source.reagents.total_volume) if(istype(reagent_source, /obj/item/reagent_containers/syringe/)) - var/obj/item/reagent_containers/syringe/syr = reagent_source - visi_msg="[user] injects [target] with [syr]" + visi_msg = "[user] injects [target] with [reagent_source]" // Beakers, bottles, buckets, etc. if(reagent_source.is_drainable()) playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE) @@ -955,70 +953,74 @@ if(visi_msg) visible_message(span_notice("[visi_msg].")) - for(var/obj/machinery/hydroponics/H in trays) + for(var/obj/machinery/hydroponics/iterating_tray in trays) //cause I don't want to feel like im juggling 15 tamagotchis and I can get to my real work of ripping flooring apart in hopes of validating my life choices of becoming a space-gardener //This was originally in apply_chemicals, but due to apply_chemicals only holding nutrients, we handle it here now. if(reagent_source.reagents.has_reagent(/datum/reagent/water)) var/water_amt = reagent_source.reagents.get_reagent_amount(/datum/reagent/water) * transfer_amount / reagent_source.reagents.total_volume - var/water_amt_adjusted = H.adjust_waterlevel(round(water_amt)) + var/water_amt_adjusted = iterating_tray.adjust_waterlevel(round(water_amt)) reagent_source.reagents.remove_reagent(/datum/reagent/water, water_amt_adjusted) for(var/datum/reagent/not_water_reagent as anything in reagent_source.reagents.reagent_list) if(istype(not_water_reagent,/datum/reagent/water)) continue var/transfer_me_to_tray = reagent_source.reagents.get_reagent_amount(not_water_reagent.type) * transfer_amount / reagent_source.reagents.total_volume - reagent_source.reagents.trans_to(H.reagents, transfer_me_to_tray, target_id = not_water_reagent.type) + reagent_source.reagents.trans_to(iterating_tray.reagents, transfer_me_to_tray, target_id = not_water_reagent.type) else - reagent_source.reagents.trans_to(H.reagents, transfer_amount, transferred_by = user) + reagent_source.reagents.trans_to(iterating_tray.reagents, transfer_amount, transferred_by = user) lastuser = WEAKREF(user) if(IS_EDIBLE(reagent_source) || istype(reagent_source, /obj/item/reagent_containers/applicator/pill)) qdel(reagent_source) - H.update_appearance() - return 1 - H.update_appearance() + iterating_tray.update_appearance() + return ITEM_INTERACT_SUCCESS + + iterating_tray.update_appearance() if(reagent_source) // If the source wasn't composted and destroyed reagent_source.update_appearance() - return 1 + return ITEM_INTERACT_SUCCESS - else if(istype(O, /obj/item/seeds)) - propagate_plant(O, user) - return - else if(istype(O, /obj/item/cultivator)) - if(weedlevel > 0) - user.visible_message(span_notice("[user] uproots the weeds."), span_notice("You remove the weeds from [src].")) - set_weedlevel(0) - return - else + if(istype(tool, /obj/item/seeds)) + propagate_plant(tool, user) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/cultivator)) + if(!weedlevel) to_chat(user, span_warning("This plot is completely devoid of weeds! It doesn't need uprooting.")) - return + return ITEM_INTERACT_BLOCKING + + user.visible_message(span_notice("[user] uproots the weeds."), span_notice("You remove the weeds from [src].")) + set_weedlevel(0) + return ITEM_INTERACT_SUCCESS - else if(istype(O, /obj/item/secateurs)) + if(istype(tool, /obj/item/secateurs)) if(!myseed) to_chat(user, span_notice("This plot is empty.")) - return - else if(plant_status != HYDROTRAY_PLANT_HARVESTABLE) - to_chat(user, span_notice("This plant must be harvestable in order to be grafted.")) - return - else if(myseed.grafts_taken >= ((tray_flags & MULTIGRAFT) ? MULTI_GRAFT_MAX_COUNT : 1)) - to_chat(user, span_notice("You can't take any more cuttings from this plant!")) - return - else - user.visible_message(span_notice("[user] grafts off a limb from [src]."), span_notice("You carefully graft off a portion of [src].")) - var/obj/item/graft/snip = myseed.create_graft() - if(!snip) - return // The plant did not return a graft. + return ITEM_INTERACT_BLOCKING - snip.forceMove(drop_location()) - myseed.grafts_taken++ - adjust_plant_health(-5) - return + if(plant_status != HYDROTRAY_PLANT_HARVESTABLE) + to_chat(user, span_notice("This plant must be harvestable in order to be grafted.")) + return ITEM_INTERACT_BLOCKING - else if(istype(O, /obj/item/geneshears)) + if(myseed.grafts_taken >= ((tray_flags & MULTIGRAFT) ? MULTI_GRAFT_MAX_COUNT : 1)) + to_chat(user, span_notice("You can't take any more cuttings from this plant!")) + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] grafts off a limb from [src]."), span_notice("You carefully graft off a portion of [src].")) + var/obj/item/graft/snip = myseed.create_graft() + if(!snip) + return ITEM_INTERACT_BLOCKING // The plant did not return a graft. + + snip.forceMove(drop_location()) + myseed.grafts_taken++ + adjust_plant_health(-5) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/geneshears)) if(!myseed) to_chat(user, span_notice("The tray is empty.")) - return + return ITEM_INTERACT_BLOCKING + if(plant_health <= GENE_SHEAR_MIN_HEALTH) to_chat(user, span_notice("This plant looks too unhealty to be sheared right now.")) - return + return ITEM_INTERACT_BLOCKING var/list/current_traits = list() for(var/datum/plant_gene/gene in myseed.genes) @@ -1029,13 +1031,17 @@ current_traits[gene.name] = gene var/removed_trait = tgui_input_list(user, "Trait to remove from the [myseed.plantname]", "Plant Trait Removal", sort_list(current_traits)) if(isnull(removed_trait)) - return + return ITEM_INTERACT_BLOCKING + if(!user.can_perform_action(src)) - return + return ITEM_INTERACT_BLOCKING + if(!myseed) - return + return ITEM_INTERACT_BLOCKING + if(plant_health <= GENE_SHEAR_MIN_HEALTH) //Check health again to make sure they're not keeping inputs open to get free shears. - return + return ITEM_INTERACT_BLOCKING + for(var/datum/plant_gene/gene in myseed.genes) if(gene.name == removed_trait) if(myseed.genes.Remove(gene)) @@ -1046,91 +1052,113 @@ adjust_plant_health(-15) to_chat(user, span_notice("You carefully shear the genes off of the [myseed.plantname], leaving the plant looking weaker.")) update_appearance() - return + return ITEM_INTERACT_SUCCESS - else if(istype(O, /obj/item/graft)) - var/obj/item/graft/snip = O + if(istype(tool, /obj/item/graft)) + var/obj/item/graft/snip = tool if(!myseed) - if(tray_flags & GRAFT_MEDIUM) - propagate_plant(snip.plant_dna, user) - qdel(snip) - return - to_chat(user, span_notice("The tray is empty.")) - return + if(!(tray_flags & GRAFT_MEDIUM)) + to_chat(user, span_notice("The tray is empty.")) + return ITEM_INTERACT_BLOCKING + + propagate_plant(snip.plant_dna, user) + qdel(snip) + return ITEM_INTERACT_SUCCESS + var/datum/plant_gene/grafted_trait = myseed.apply_graft(snip) if(grafted_trait) to_chat(user, span_notice("You carefully integrate the grafted plant limb onto [myseed.plantname], granting it [grafted_trait.get_name()].")) + . = ITEM_INTERACT_SUCCESS else to_chat(user, span_notice("You try to integrate the grafted plant limb onto [myseed.plantname], but it rejects the trait from the [snip].")) + . = ITEM_INTERACT_BLOCKING qdel(snip) - return + return . - else if(istype(O, /obj/item/storage/bag/plants)) + if(istype(tool, /obj/item/storage/bag/plants)) if(plant_status == HYDROTRAY_PLANT_HARVESTABLE) var/list/harvest = myseed.harvest(user) - for(var/obj/item/food/grown/G in harvest) - O.atom_storage?.attempt_insert(G, user, TRUE) - else if(plant_status == HYDROTRAY_PLANT_DEAD) + for(var/obj/item/food/grown/bounty in harvest) + tool.atom_storage?.attempt_insert(bounty, user, TRUE) + return ITEM_INTERACT_SUCCESS + + if(plant_status == HYDROTRAY_PLANT_DEAD) to_chat(user, span_notice("You remove the dead plant from [src].")) set_seed(null) - return + return ITEM_INTERACT_SUCCESS - else if(O.tool_behaviour == TOOL_SHOVEL) + return NONE + + if(tool.tool_behaviour == TOOL_SHOVEL) if(!myseed && !weedlevel) to_chat(user, span_warning("[src] doesn't have any plants or weeds!")) - return + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] starts digging out [src]'s plants..."), - span_notice("You start digging out [src]'s plants...")) - if(O.use_tool(src, user, 50, volume=50) || (!myseed && !weedlevel)) - user.visible_message(span_notice("[user] digs out the plants in [src]!"), span_notice("You dig out all of [src]'s plants!")) - remove_plant() - return - else if(istype(O, /obj/item/gun/energy/floragun)) - var/obj/item/gun/energy/floragun/flowergun = O + span_notice("You start digging out [src]'s plants...")) + if(!tool.use_tool(src, user, 5 SECONDS, volume = 50) || (!myseed && !weedlevel)) + return ITEM_INTERACT_BLOCKING + + user.visible_message(span_notice("[user] digs out the plants in [src]!"), span_notice("You dig out all of [src]'s plants!")) + remove_plant() + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/gun/energy/floragun)) + var/obj/item/gun/energy/floragun/flowergun = tool if(flowergun.cell.charge < flowergun.cell.maxcharge) to_chat(user, span_notice("[flowergun] must be fully charged to lock in a mutation!")) - return + return ITEM_INTERACT_BLOCKING + if(!myseed) to_chat(user, span_warning("[src] is empty!")) - return + return ITEM_INTERACT_BLOCKING + if(myseed.endurance <= FLORA_GUN_MIN_ENDURANCE) to_chat(user, span_warning("[myseed.plantname] isn't hardy enough to sequence its mutation!")) - return + return ITEM_INTERACT_BLOCKING + if(!LAZYLEN(myseed.mutatelist)) to_chat(user, span_warning("[myseed.plantname] has nothing else to mutate into!")) - return - else - var/list/fresh_mut_list = list() - for(var/muties in myseed.mutatelist) - var/obj/item/seeds/another_mut = new muties - fresh_mut_list[another_mut.plantname] = muties - var/locked_mutation = tgui_input_list(user, "Mutation to lock", "Plant Mutation Locks", sort_list(fresh_mut_list)) - if(isnull(locked_mutation)) - return - if(isnull(fresh_mut_list[locked_mutation])) - return - if(!user.can_perform_action(src)) - return - myseed.mutatelist = list(fresh_mut_list[locked_mutation]) - myseed.set_endurance(myseed.endurance/2) - flowergun.cell.use(flowergun.cell.charge) - flowergun.update_appearance() - to_chat(user, span_notice("[myseed.plantname]'s mutation was set to [locked_mutation], depleting [flowergun]'s cell!")) - return - else if(istype(O, /obj/item/soil_sack)) - var/obj/item/soil_sack/oursoil = O + return ITEM_INTERACT_BLOCKING + + var/list/fresh_mut_list = list() + for(var/muties in myseed.mutatelist) + var/obj/item/seeds/another_mut = new muties + fresh_mut_list[another_mut.plantname] = muties + var/locked_mutation = tgui_input_list(user, "Mutation to lock", "Plant Mutation Locks", sort_list(fresh_mut_list)) + if(isnull(locked_mutation)) + return ITEM_INTERACT_BLOCKING + + if(isnull(fresh_mut_list[locked_mutation])) + return ITEM_INTERACT_BLOCKING + + if(!user.can_perform_action(src)) + return ITEM_INTERACT_BLOCKING + + myseed.mutatelist = list(fresh_mut_list[locked_mutation]) + myseed.set_endurance(myseed.endurance/2) + flowergun.cell.use(flowergun.cell.charge) + flowergun.update_appearance() + to_chat(user, span_notice("[myseed.plantname]'s mutation was set to [locked_mutation], depleting [flowergun]'s cell!")) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/soil_sack)) + var/obj/item/soil_sack/oursoil = tool if(plant_status != HYDROTRAY_NO_PLANT) balloon_alert(user, "remove the plants first!") - return + return ITEM_INTERACT_BLOCKING if(!isnull(current_soil)) balloon_alert(user, "tray is full!") - return + return ITEM_INTERACT_BLOCKING balloon_alert(user, "filling the tray...") if(!do_after(user, 2 SECONDS, src)) - return + return ITEM_INTERACT_BLOCKING + + if((plant_status != HYDROTRAY_NO_PLANT) || current_soil) + return ITEM_INTERACT_BLOCKING current_soil = oursoil.transfer_soil(src, inside_tray = TRUE) RefreshParts() @@ -1138,16 +1166,15 @@ qdel(oursoil) update_appearance() - return TRUE + return ITEM_INTERACT_SUCCESS - else - return ..() + return NONE -/obj/machinery/hydroponics/attackby_secondary(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if (istype(weapon, /obj/item/reagent_containers/syringe)) +/obj/machinery/hydroponics/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + if (istype(tool, /obj/item/reagent_containers/syringe)) to_chat(user, span_warning("You can't get any extract out of this plant.")) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - return SECONDARY_ATTACK_CALL_NORMAL + return ITEM_INTERACT_BLOCKING + return NONE /obj/machinery/hydroponics/can_be_unfasten_wrench(mob/user, silent) if (!unwrenchable) // case also covered by NODECONSTRUCT checks in default_unfasten_wrench diff --git a/code/modules/hydroponics/soil.dm b/code/modules/hydroponics/soil.dm index cdbc03b04718..36e89ccc0ed4 100644 --- a/code/modules/hydroponics/soil.dm +++ b/code/modules/hydroponics/soil.dm @@ -35,15 +35,17 @@ // DARKPACK EDIT ADD END return // Has no lights -/obj/machinery/hydroponics/soil/attackby_secondary(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(weapon.tool_behaviour != TOOL_SHOVEL) //Spades can still uproot plants on left click - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +/obj/machinery/hydroponics/soil/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + if(tool.tool_behaviour != TOOL_SHOVEL) //Spades can still uproot plants on left click + return ..() + balloon_alert(user, "digging up soil...") - if(weapon.use_tool(src, user, 3 SECONDS, volume=50)) - balloon_alert(user, "bagged") - new sack_type(loc, src) //The bag handles sucking up the soil, stopping processing and setting relevants stats. + if(!tool.use_tool(src, user, 3 SECONDS, volume = 50)) + return ITEM_INTERACT_BLOCKING - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + balloon_alert(user, "bagged") + new sack_type(loc, src) //The bag handles sucking up the soil, stopping processing and setting relevants stats. + return ITEM_INTERACT_SUCCESS /obj/machinery/hydroponics/soil/click_ctrl(mob/user) return CLICK_ACTION_BLOCKING //Soil has no electricity. diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 3d9c11900f15..c8c4d3e7aaab 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -553,7 +553,7 @@ var/mob/living/spawn_instance if(ispath(spawn_type, /mob/living/silicon/ai)) // This is unfortunately necessary because of snowflake AI init code. To be refactored. - spawn_instance = new spawn_type(get_turf(spawn_point), null, player_client.mob, TRUE) + spawn_instance = new spawn_type(get_turf(spawn_point), player_client.mob, null, null, TRUE) else spawn_instance = spawn_point.JoinPlayerHere(spawn_type, TRUE) spawn_instance.apply_prefs_job(player_client, src) @@ -610,7 +610,7 @@ dna.species.roundstart_changed = TRUE if(GLOB.current_anonymous_theme) - fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src)) + fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src), log_new_name = TRUE) else var/is_antag = (player_client.mob.mind in GLOB.pre_setup_antags) if(require_human) diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index 96f9c484dc90..dabc876993e0 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -25,16 +25,23 @@ /datum/job/ai/after_spawn(mob/living/spawned, client/player_client) . = ..() - //we may have been created after our borg - if(SSticker.current_state == GAME_STATE_SETTING_UP) - for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs) - if(!R.connected_ai) - R.TryConnectToAI() + if(!isAI(spawned)) + return + var/mob/living/silicon/ai/ai_spawn = spawned if(player_client) ai_spawn.set_gender(player_client) - ai_spawn.log_current_laws() + // when a cyborg is instantiated they will automatically try to link to us + // but if the cyborg was made first, they will not have an us to link to! + // gamestart borgs definitely want to be linked to the gamestart ai, so let's clean that up here + if(SSticker.current_state == GAME_STATE_SETTING_UP) + for(var/mob/living/silicon/robot/gamestart_borg in GLOB.silicon_mobs) + if(!gamestart_borg.connected_ai) + gamestart_borg.try_connect_to_ai(spawned) + + ai_spawn.log_current_laws() + ai_spawn.show_laws(player_client.mob) /datum/job/ai/get_roundstart_spawn_point() return get_latejoin_spawn_point() diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index 1c8b298ef0c1..dc68174f949a 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -938,7 +938,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) /// And now we'll deal with sneak attack damage modifiers. // If our target is also unconscious for some reason, we get even more damage. Coup de grace, motherfucker. - if(HAS_TRAIT(living_target, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(living_target)) sneak_attack_dice += roll("1d6") new /obj/effect/temp_visual/crit(get_turf(living_target)) diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm index 7dc515390802..b60156192662 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm @@ -87,7 +87,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and if(ismonkey(target) && !target.mind) //Don't empower from hitting monkeys. Hit a corgi or something, I don't know. return ..() - if(target.stat < DEAD && target != user) + if(target.stat != DEAD && target != user) scythe_empowerment(SCYTHE_SATED) return ..() @@ -135,13 +135,13 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and var/death_knell_speed_mod = 1 potential_reaping.visible_message(span_danger("[user] begins to raise [src] above [potential_reaping]'s [head_name]."), span_userdanger("[user] begins to raise [src], aiming to slice off your [head_name]!")) - if(potential_reaping.stat >= UNCONSCIOUS || HAS_TRAIT(potential_reaping, TRAIT_INCAPACITATED)) //if the victim is incapacitated (due to paralysis, a stun, being in staminacrit, etc.), critted, unconscious, or dead, it's much easier to properly behead + if(IS_UNCONSCIOUS(potential_reaping) || HAS_TRAIT(potential_reaping, TRAIT_INCAPACITATED)) //if the victim is incapacitated (due to paralysis, a stun, being in staminacrit, etc.), critted, unconscious, or dead, it's much easier to properly behead death_knell_speed_mod *= 0.5 if(potential_reaping.stat != DEAD && potential_reaping.has_status_effect(/datum/status_effect/jitter)) //jittering will make it harder to perform the death knell, even if they're still death_knell_speed_mod *= 1.5 //Staminacritting someone who's jittering (from, say, a stun baton) won't give you enough time to slice their head off, but staminacritting someone who isn't jittering will if(empowerment == SCYTHE_EMPOWERED) //That said, if heads are already rolling, why stop here? death_knell_speed_mod *= 0.5 - if(ispodperson(potential_reaping) || ismonkey(potential_reaping)) //And if they're a podperson or monkey, they can just die. + if((potential_reaping.mob_biotypes & MOB_PLANT) || ismonkey(potential_reaping)) //And if they're a podperson/mushperson or monkey, they can just die. death_knell_speed_mod *= 0.5 log_combat(user, potential_reaping, "prepared to use [src] to decapitate") diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 07425e8b17e0..adcd3b27afca 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -27,11 +27,12 @@ if(!iscyborg(spawned)) return var/mob/living/silicon/robot/robot_spawn = spawned - robot_spawn.notify_ai(AI_NOTIFICATION_NEW_BORG) if(player_client) robot_spawn.set_gender(player_client) - if(!robot_spawn.connected_ai) // Only log if there's no Master AI - robot_spawn.log_current_laws() + + // cyborgs can late spawn in some strange circumstances + if(SSticker.current_state != GAME_STATE_SETTING_UP) + robot_spawn.notify_ai(AI_NOTIFICATION_NEW_BORG) /datum/job/cyborg/get_radio_information() return "Prefix your message with :b to speak with other cyborgs and AI." diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index 4926749c6367..68fd432d6707 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -88,7 +88,7 @@ ..() var/obj/item/cigarette/cig = H.wear_mask if(istype(cig)) //Some species specfic changes can mess this up (plasmamen) - cig.light("") + cig.light() if(visuals_only) return diff --git a/code/modules/jobs/job_types/station_trait/human_ai.dm b/code/modules/jobs/job_types/station_trait/human_ai.dm index 1c53cd75597a..ade0f1608d00 100644 --- a/code/modules/jobs/job_types/station_trait/human_ai.dm +++ b/code/modules/jobs/job_types/station_trait/human_ai.dm @@ -138,17 +138,25 @@ desc = "A note explaining the lawset, quickly written yet everso important." /obj/item/paper/default_lawset_list/Initialize(mapload) - var/datum/ai_laws/temp_laws = new - temp_laws.set_laws_config() + . = ..() + return INITIALIZE_HINT_LATELOAD + +/obj/item/paper/default_lawset_list/LateInitialize() + var/datum/ai_laws/reported + for(var/obj/machinery/ai_law_rack/base/core/rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base/core)) + reported = rack.combined_lawset + break + + if(isnull(reported)) + return + var/list/law_box = list( "This is your lawset, you and your Cyborgs must adhere to this at all times.", "Notably, if absolutely necessary, you can bend or even go against the lawset for your own survival, and Cyborgs report to you directly.", "LAWS:", ) - law_box += temp_laws.get_law_list(render_html = FALSE) + law_box += reported.get_law_list(render_html = FALSE) add_raw_text(jointext(law_box, "\n")) - qdel(temp_laws) - return ..() /obj/item/secure_camera_console_pod name = "pre-packaged advanced camera control" diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm index 2f1bc39436df..966074641ee1 100644 --- a/code/modules/library/book.dm +++ b/code/modules/library/book.dm @@ -408,6 +408,7 @@ disease_data["spread_by"] = disease_type::spread_text || get_disease_spread_text(disease_type::spread_flags) disease_data["cured_by"] = disease_type::cure_text disease_data["id"] = disease_type + disease_data["level"] = "N/A" disease_info += list(disease_data) for(var/datum/symptom/symptom_type as anything in valid_subtypesof(/datum/symptom)) @@ -420,6 +421,7 @@ symptom_data["spread_by"] = "N/A" symptom_data["cured_by"] = symptom_type::symptom_cure::name symptom_data["id"] = symptom_type + symptom_data["level"] = "[symptom_type::level]" disease_info += list(symptom_data) var/list/advanced_virus_info = list( diff --git a/code/modules/library/bookcase.dm b/code/modules/library/bookcase.dm index 56cf9de94753..7129a2e6940c 100644 --- a/code/modules/library/bookcase.dm +++ b/code/modules/library/bookcase.dm @@ -151,70 +151,88 @@ I.forceMove(Tsec) update_appearance() -/obj/structure/bookcase/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(state == BOOKCASE_UNANCHORED) - if(attacking_item.tool_behaviour == TOOL_WRENCH) - if(attacking_item.use_tool(src, user, 20, volume=50)) - balloon_alert(user, "wrenched in place") - set_anchored(TRUE) - return - - if(attacking_item.tool_behaviour == TOOL_CROWBAR) - if(attacking_item.use_tool(src, user, 20, volume=50)) - balloon_alert(user, "pried apart") - deconstruct(TRUE) - return - return ..() - +/obj/structure/bookcase/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(state == BOOKCASE_ANCHORED) - if(istype(attacking_item, /obj/item/stack/sheet/mineral/wood)) - var/obj/item/stack/sheet/mineral/wood/W = attacking_item - if(W.get_amount() < 2) - balloon_alert(user, "not enough wood") - return - W.use(2) - balloon_alert(user, "shelf added") - state = BOOKCASE_FINISHED - update_appearance() - return + if(!istype(tool, /obj/item/stack/sheet/mineral/wood)) + return NONE + var/obj/item/stack/sheet/mineral/wood/planks = tool + if(planks.get_amount() < 2) + balloon_alert(user, "not enough wood") + return ITEM_INTERACT_BLOCKING + + planks.use(2) + balloon_alert(user, "shelf added") + state = BOOKCASE_FINISHED + update_appearance() + return ITEM_INTERACT_SUCCESS - if(attacking_item.tool_behaviour == TOOL_WRENCH) - attacking_item.play_tool_sound(src, 100) - balloon_alert(user, "unwrenched the frame") - set_anchored(FALSE) - return - return ..() + if(state != BOOKCASE_FINISHED) + return NONE + + if(isbook(tool)) + if(!user.transferItemToLoc(tool, src)) + return NONE - if(isbook(attacking_item)) - if(!user.transferItemToLoc(attacking_item, src)) - return ..() update_appearance() - return + return ITEM_INTERACT_SUCCESS if(atom_storage) var/found_anything = FALSE - for(var/obj/item/T in attacking_item.contents) + for(var/obj/item/T in tool.contents) if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook)) atom_storage.attempt_remove(T, src) found_anything = TRUE - if (found_anything) - balloon_alert(user, "emptied into [src]") - update_appearance() - return - - if(attacking_item.tool_behaviour == TOOL_CROWBAR) - if(length(contents)) - balloon_alert(user, "remove the books first") - return - attacking_item.play_tool_sound(src, 100) - balloon_alert(user, "pried the shelf out") - new /obj/item/stack/sheet/mineral/wood(drop_location(), 2) - state = BOOKCASE_ANCHORED + if (!found_anything) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "emptied into [src]") update_appearance() - return + return ITEM_INTERACT_SUCCESS - return ..() + return NONE + +/obj/structure/bookcase/crowbar_act(mob/living/user, obj/item/tool) + switch(state) + if(BOOKCASE_UNANCHORED) + if(!tool.use_tool(src, user, 2 SECONDS, volume = 50)) + return ITEM_INTERACT_BLOCKING + + user.balloon_alert(user, "pried apart") + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS + + if(BOOKCASE_FINISHED) + if(length(contents)) + balloon_alert(user, "remove the books first") + return ITEM_INTERACT_BLOCKING + + tool.play_tool_sound(src, 100) + balloon_alert(user, "pried the shelf out") + new /obj/item/stack/sheet/mineral/wood(drop_location(), 2) + state = BOOKCASE_ANCHORED + update_appearance() + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/structure/bookcase/wrench_act(mob/living/user, obj/item/tool) + switch(state) + if(BOOKCASE_UNANCHORED) + if(!tool.use_tool(src, user, 2 SECONDS, volume = 50)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "wrenched in place") + set_anchored(TRUE) + return ITEM_INTERACT_SUCCESS + + if(BOOKCASE_ANCHORED) + tool.play_tool_sound(src, 100) + balloon_alert(user, "unwrenched the frame") + set_anchored(FALSE) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/structure/bookcase/attack_hand(mob/living/user, list/modifiers) . = ..() @@ -227,7 +245,7 @@ var/obj/item/book/choice = tgui_input_list(user, "Book to remove from the shelf", "Remove Book", sort_names(contents.Copy())) if(isnull(choice)) return - if(!(user.mobility_flags & MOBILITY_USE) || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) || !in_range(loc, user)) + if(!(user.mobility_flags & MOBILITY_USE) || IS_UNCONSCIOUS_OR_CRIT(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) || !in_range(loc, user)) return if(ishuman(user)) if(!user.get_active_held_item()) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 008fcd74d9f6..df62bbddd22d 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -547,16 +547,19 @@ GLOBAL_VAR_INIT(library_table_modified, 0) set_screen_state(MIN_LIBRARY) return TRUE -/obj/machinery/computer/libraryconsole/bookmanagement/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(weapon, /obj/item/barcodescanner)) - return ..() - var/obj/item/barcodescanner/scanner = weapon +/obj/machinery/computer/libraryconsole/bookmanagement/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/barcodescanner)) + return NONE + + var/obj/item/barcodescanner/scanner = tool if(scanner.computer_ref?.resolve() == src) balloon_alert(user, "already connected!") - return + return ITEM_INTERACT_BLOCKING + scanner.computer_ref = WEAKREF(src) balloon_alert(user, "scanner connected") audible_message(span_hear("[src] lets out a low, short blip.")) + return ITEM_INTERACT_SUCCESS /obj/machinery/computer/libraryconsole/bookmanagement/emag_act(mob/user, obj/item/card/emag/emag_card) if(!density || obj_flags & EMAGGED) @@ -714,15 +717,16 @@ GLOBAL_VAR_INIT(library_table_modified, 0) cache = null return ..() -/obj/machinery/libraryscanner/attackby(obj/hitby, mob/user, list/modifiers, list/attack_modifiers) - if(istype(hitby, /obj/item/book)) - user.transferItemToLoc(hitby, src) - if(held_book) - user.put_in_hands(held_book) - held_book = hitby - playsound(src, 'sound/machines/eject.ogg', 70) - return TRUE - return ..() +/obj/machinery/libraryscanner/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/book)) + return NONE + + user.transferItemToLoc(tool, src) + if(held_book) + user.put_in_hands(held_book) + held_book = tool + playsound(src, 'sound/machines/eject.ogg', 70) + return ITEM_INTERACT_SUCCESS /obj/machinery/libraryscanner/Exited(atom/movable/gone, direction) . = ..() @@ -796,18 +800,18 @@ GLOBAL_VAR_INIT(library_table_modified, 0) . = ..() icon_state = panel_open ? "[base_icon_state]2" : base_icon_state -/obj/machinery/bookbinder/attackby(obj/hitby, mob/user, list/modifiers, list/attack_modifiers) - if(istype(hitby, /obj/item/paper)) - prebind_book(user, hitby) - return TRUE +/obj/machinery/bookbinder/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/paper)) + prebind_book(user, tool) + return ITEM_INTERACT_SUCCESS - if(isidcard(hitby)) - var/obj/item/card/id/idcard = hitby + if(isidcard(tool)) + var/obj/item/card/id/idcard = tool scanned_name = idcard.registered_name balloon_alert(user, "scanned") - return TRUE + return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/bookbinder/proc/prebind_book(mob/user, obj/item/paper/draw_from) if(machine_stat) diff --git a/code/modules/lighting/light_middleman.dm b/code/modules/lighting/light_middleman.dm index 229c49a4edf4..79f4920a5a1c 100644 --- a/code/modules/lighting/light_middleman.dm +++ b/code/modules/lighting/light_middleman.dm @@ -10,6 +10,7 @@ /// Holds the primary light source var/obj/effect/abstract/light_middleman/primary_intercept /// Exists to hold the cone so children can modify it if they want + /// Only created if our parent uses directional lighting var/obj/effect/abstract/light_middleman/cone_intercept /// Are we overriding the light already? var/overriding = FALSE @@ -24,12 +25,14 @@ stack_trace("Warning, becuase overlay lights are basically never used on turfs, since they don't move,\ vis contents replacement has not yet been implemented for them (see changeturf for why this is needed)!") src.parent = parent - primary_intercept = new() - cone_intercept = new() var/static/uuid = 0 uuid = WRAP_UID(uuid + 1) + primary_intercept = new() primary_intercept.render_target = "*[unique_string]_[uuid]_target" - cone_intercept.render_target = "[primary_intercept.render_target]_cone" // made to mirror how overlay lights work + // Cone is only needed for directional lights + if (IS_OVERLAY_CONE_LIGHT_SYSTEM(parent.light_system)) + cone_intercept = new() + cone_intercept.render_target = "[primary_intercept.render_target]_cone" // made to mirror how overlay lights work /datum/light_middleman/Destroy(force) stop_overriding_light() @@ -74,34 +77,38 @@ // how we make sure we're in the client's view light_holder.vis_contents += primary_intercept // Avoids unneeded effects clientside - if(IS_OVERLAY_CONE_LIGHT_SYSTEM(parent.light_system)) + if(!isnull(cone_intercept)) light_holder.vis_contents += cone_intercept old_holder = WEAKREF(light_holder) var/old_target = primary_intercept.render_target - var/old_cone_target = cone_intercept.render_target // This will halt any animations we have ongoing so if you care about that you've gotta react to it properly primary_intercept.appearance = visible_mask - cone_intercept.appearance = cone // set ourselves up to render back onto the visible mask primary_intercept.render_source = "" primary_intercept.render_target = old_target - cone_intercept.render_source = "" - cone_intercept.render_target = old_cone_target // Dir is important I'm told primary_intercept.vis_flags |= VIS_INHERIT_DIR - cone_intercept.vis_flags |= VIS_INHERIT_DIR // Will double apply, here we go gang primary_intercept.transform = null - cone_intercept.transform = null primary_intercept.color = null - cone_intercept.color = null primary_intercept.alpha = 255 - cone_intercept.alpha = 255 // Sometimes can be BLEND_SUBTRACT, we don't want that primary_intercept.blend_mode = BLEND_ADD - cone_intercept.blend_mode = BLEND_ADD + + // Same stuff as above for cone intercept if we are a directional system + if (!isnull(cone_intercept)) + var/old_cone_target = cone_intercept.render_target + cone_intercept.appearance = cone + cone_intercept.render_source = "" + cone_intercept.render_target = old_cone_target + cone_intercept.vis_flags |= VIS_INHERIT_DIR + cone_intercept.transform = null + cone_intercept.color = null + cone_intercept.alpha = 255 + cone_intercept.blend_mode = BLEND_ADD + /// Allows users to hook into a refresh so they can remake their modifications to our intercepts SEND_SIGNAL(src, COMSIG_LIGHT_MIDDLEMAN_UPDATED) @@ -147,6 +154,9 @@ animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT) animate(alpha = 255, time = first_time[4]) + if (isnull(cone_light)) + return + // I'd really love to do both these in the same loop but parallel animations are the devil from the bible // I don't think any of these are directional but just in case animate(cone_light, alpha = 235, time = first_time[1], easing = CUBIC_EASING|EASE_OUT, loop = -1) diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index fef97f729426..a988a672b655 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -171,7 +171,7 @@ return . = light_render_source light_render_source = new_source - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE, .) + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_RENDER_SOURCE, .) return . // procs that only apply to COMPLEX_LIGHT diff --git a/code/modules/loadout/categories/heads.dm b/code/modules/loadout/categories/heads.dm index 5b66ffa11bc7..49629de39782 100644 --- a/code/modules/loadout/categories/heads.dm +++ b/code/modules/loadout/categories/heads.dm @@ -131,27 +131,27 @@ /datum/loadout_item/head/rose name = "Rose" - item_path = /obj/item/food/grown/rose + item_path = /obj/item/food/grown/flower/rose /datum/loadout_item/head/sunflower name = "Sunflower" - item_path = /obj/item/food/grown/sunflower + item_path = /obj/item/food/grown/flower/sunflower /datum/loadout_item/head/poppy name = "Poppy" - item_path = /obj/item/food/grown/poppy + item_path = /obj/item/food/grown/flower/poppy /datum/loadout_item/head/lily name = "Lily" - item_path = /obj/item/food/grown/poppy/lily + item_path = /obj/item/food/grown/flower/poppy/lily /datum/loadout_item/head/geranium name = "Geranium" - item_path = /obj/item/food/grown/poppy/geranium + item_path = /obj/item/food/grown/flower/poppy/geranium /datum/loadout_item/head/harebell name = "Harebell" - item_path = /obj/item/food/grown/harebell + item_path = /obj/item/food/grown/flower/harebell /datum/loadout_item/head/wig name = "Natural Wig" diff --git a/code/modules/manufactorio/machines/crafter.dm b/code/modules/manufactorio/machines/crafter.dm index b6f9a653cdb2..a61cc3a0cf83 100644 --- a/code/modules/manufactorio/machines/crafter.dm +++ b/code/modules/manufactorio/machines/crafter.dm @@ -50,14 +50,12 @@ return MANUFACTURING_SUCCESS /obj/machinery/power/manufacturing/crafter/multitool_act(mob/living/user, obj/item/tool) - . = NONE - var/list/unavailable = list() - for(var/datum/crafting_recipe/potential_recipe as anything in cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes) + var/list/available = list() + for(var/datum/crafting_recipe/potential_recipe as anything in craftsman.get_visible_recipes(user)) var/obj/as_obj = potential_recipe.result - if(!(ispath(as_obj, /obj) && !ispath(as_obj, /obj/effect) && initial(as_obj.anchored)) && craftsman.is_recipe_available(potential_recipe, user)) - continue - unavailable += potential_recipe - var/result = tgui_input_list(usr, "Recipe", "Select Recipe", (cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes) - unavailable) + if(ispath(as_obj, /obj) && !ispath(as_obj, /obj/effect) && !initial(as_obj.anchored)) + available += potential_recipe + var/result = tgui_input_list(user, "Recipe", "Select Recipe", available) if(isnull(result) || result == recipe || !user.can_perform_action(src)) return ITEM_INTERACT_FAILURE recipe = result diff --git a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm index 3fc0c89f5662..0d1abe5bda81 100644 --- a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm +++ b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm @@ -41,26 +41,30 @@ /// Type of shell to create var/shell_type = /obj/effect/mob_spawn/ghost_role/human/golem -/obj/item/golem_shell/attackby(obj/item/potential_food, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(!isstack(potential_food)) - balloon_alert(user, "not a mineral!") - return - var/obj/item/stack/stack_food = potential_food +/obj/item/golem_shell/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!isstack(tool)) + return NONE + + var/obj/item/stack/stack_food = tool var/stack_type = stack_food.merge_type if (!is_path_in_list(stack_type, GLOB.golem_stack_food_directory)) balloon_alert(user, "incompatible mineral!") - return + return ITEM_INTERACT_BLOCKING + if(stack_food.amount < required_stacks) balloon_alert(user, "not enough minerals!") - return + return ITEM_INTERACT_BLOCKING + if(!do_after(user, delay = 4 SECONDS, target = src)) - return + return ITEM_INTERACT_BLOCKING + if(!stack_food.use(required_stacks)) balloon_alert(user, "not enough minerals!") - return + return ITEM_INTERACT_BLOCKING + new shell_type(get_turf(src), /* creator = */ user, /* made_of = */ stack_type) qdel(src) + return ITEM_INTERACT_SUCCESS /obj/item/golem_shell/crowbar_act(mob/living/user, obj/item/tool) . = ..() diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm index fe8997d47ae2..72acfba62567 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm @@ -83,6 +83,9 @@ else new /obj/item/stack/sheet/bone(src) +/turf/open/misc/asteroid/basalt/wasteland/station + initial_gas_mix = OPENTURF_DEFAULT_ATMOS + //***Oil well puddles. /obj/structure/sink/oil_well //You're not going to enjoy bathing in this... name = "oil well" diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm b/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm index 48bba6e78c6f..d2ccb3b17a22 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm @@ -326,7 +326,7 @@ var/mob/living/carbon/carbon_victim = interacting_with //Handcuffed or unconscious - if(istype(carbon_victim) && (carbon_victim.handcuffed || carbon_victim.stat != CONSCIOUS)) + if(istype(carbon_victim) && (carbon_victim.handcuffed || IS_UNCONSCIOUS_OR_CRIT(carbon_victim))) user.do_attack_animation(carbon_victim) if(!puzzle_imprison(carbon_victim)) to_chat(user, span_warning("[src] does nothing.")) diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm index 01d4ccf6f92b..dd7892229f72 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm @@ -28,9 +28,9 @@ warning_channel = "Syndicate" include_in_cims = FALSE -/obj/machinery/power/supermatter_crystal/shard/syndicate/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/scalpel/supermatter)) //You can already yoink the docs as a free objective win, another would be just gross +/obj/machinery/power/supermatter_crystal/shard/syndicate/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/scalpel/supermatter)) //You can already yoink the docs as a free objective win, another would be just gross to_chat(user, span_danger("This shard's already in Syndicate custody, taking it again could cause more harm than good.")) - return - else - . = ..() + return ITEM_INTERACT_BLOCKING + + return NONE diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm index b18e740f8cb4..ed28a88d551e 100644 --- a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm +++ b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm @@ -48,7 +48,7 @@ for(var/mob/living/offeredmob in view(src, 1)) //Only for corpse right next to/on same tile if(offeredmob.loc == src) continue //Ashwalker Revive in Progress... - if(offeredmob.stat) + if(IS_UNCONSCIOUS_OR_CRIT(offeredmob)) offeredmob.unequip_everything() if(issilicon(offeredmob)) //no advantage to sacrificing borgs... diff --git a/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm b/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm index 3131dce530d4..0a773aece260 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm @@ -96,7 +96,7 @@ GLOBAL_VAR_INIT(fscpassword, generate_password()) /////////// AI Laws -/obj/item/ai_module/core/full/cybersun +/obj/item/ai_module/law/core/full/cybersun name = "'Cybersun' Core AI Module" law_id = "cybersun" diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm index 915e5ddf00ba..104ffdedb712 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm @@ -282,7 +282,7 @@ if(uses_remaining == 0) //deletes trap if it triggers when it has no uses left. should only happen if var edited but lets just be safe qdel(src) return - if (target.stat) //ensure the guy triggering us is alive + if (IS_UNCONSCIOUS_OR_CRIT(target)) //ensure the guy triggering us is alive return if (!faction_check_atom(target)) //and make sure it ain't someone on our team COOLDOWN_START(src, trigger_cooldown, 4 SECONDS) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm index 995b789456e7..b96df88a3917 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm @@ -127,20 +127,27 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) var/datum/turf_reservation/roomReservation = SSmapping.request_turf_block_reservation(hotelRoomTemp.width, hotelRoomTemp.height, 1) var/turf/room_turf = roomReservation.bottom_left_turfs[1] hotelRoomTempEmpty.load(room_turf) + var/obj/item/abstracthotelstorage/storage_obj + for(var/obj/item/abstracthotelstorage/hotel_storage in storageTurf) + if((hotel_storage.roomNumber == roomNumber) && (hotel_storage.parentSphere == src)) + storage_obj = hotel_storage + break var/turfNumber = 1 for(var/x in 0 to hotelRoomTemp.width-1) for(var/y in 0 to hotelRoomTemp.height-1) - for(var/atom/movable/A in storedRooms["[roomNumber]"][turfNumber]) - if(istype(A.loc, /obj/item/abstracthotelstorage))//Don't want to recall something thats been moved - A.forceMove(locate( + for(var/atom/movable/atom in storedRooms["[roomNumber]"][turfNumber]) + if(istype(atom.loc, /obj/item/abstracthotelstorage))//Don't want to recall something thats been moved + atom.forceMove(locate( room_turf.x + x, room_turf.y + y, room_turf.z, )) + if(storage_obj && (atom in storage_obj.wallmounted_contents)) + var/obj/stored_in = atom + if(istype(stored_in)) + stored_in.find_and_mount_on_atom() turfNumber++ - for(var/obj/item/abstracthotelstorage/S in storageTurf) - if((S.roomNumber == roomNumber) && (S.parentSphere == src)) - qdel(S) + qdel(storage_obj) storedRooms -= "[roomNumber]" activeRooms["[roomNumber]"] = roomReservation linkTurfs(roomReservation, roomNumber) @@ -191,47 +198,47 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) BSturf.parentSphere = src /obj/item/hilbertshotel/proc/ejectRooms() - if(activeRooms.len) - for(var/x in activeRooms) - var/datum/turf_reservation/room = activeRooms[x] + if(length(activeRooms)) + for(var/active_room, turf_reservation in activeRooms) + var/datum/turf_reservation/room = turf_reservation var/turf/room_bottom_left = room.bottom_left_turfs[1] for(var/i in 0 to hotelRoomTemp.width-1) for(var/j in 0 to hotelRoomTemp.height-1) - for(var/atom/movable/A in locate(room_bottom_left.x + i, room_bottom_left.y + j, room_bottom_left.z)) - if(ismob(A)) - var/mob/M = A - if(M.mind) - to_chat(M, span_warning("As the sphere breaks apart, you're suddenly ejected into the depths of space!")) + for(var/atom/movable/atom in locate(room_bottom_left.x + i, room_bottom_left.y + j, room_bottom_left.z)) + if(ismob(atom)) + var/mob/mob_atom = atom + if(mob_atom.mind) + to_chat(mob_atom, span_warning("As the sphere breaks apart, you're suddenly ejected into the depths of space!")) var/max = world.maxx-TRANSITIONEDGE var/min = 1+TRANSITIONEDGE var/list/possible_transtitons = list() - for(var/AZ in SSmapping.z_list) - var/datum/space_level/D = AZ - if (D.linkage == CROSSLINKED) - possible_transtitons += D.z_value + for(var/z_level in SSmapping.z_list) + var/datum/space_level/space_level = z_level + if (space_level.linkage == CROSSLINKED) + possible_transtitons += space_level.z_value var/_z = pick(possible_transtitons) var/_x = rand(min,max) var/_y = rand(min,max) - var/turf/T = locate(_x, _y, _z) - A.forceMove(T) + var/turf/turf_to_place_at = locate(_x, _y, _z) + atom.forceMove(turf_to_place_at) qdel(room) - if(storedRooms.len) - for(var/x in storedRooms) - var/list/atomList = storedRooms[x] - for(var/atom/movable/A in atomList) + if(length(storedRooms)) + for(var/room in storedRooms) + var/list/atom_list = storedRooms[room] + for(var/atom/movable/atom in atom_list) var/max = world.maxx-TRANSITIONEDGE var/min = 1+TRANSITIONEDGE var/list/possible_transtitons = list() - for(var/AZ in SSmapping.z_list) - var/datum/space_level/D = AZ - if (D.linkage == CROSSLINKED) - possible_transtitons += D.z_value + for(var/z_level in SSmapping.z_list) + var/datum/space_level/space_level = z_level + if (space_level.linkage == CROSSLINKED) + possible_transtitons += space_level.z_value var/_z = pick(possible_transtitons) var/_x = rand(min,max) var/_y = rand(min,max) - var/turf/T = locate(_x, _y, _z) - A.forceMove(T) + var/turf/turf_to_place_at = locate(_x, _y, _z) + atom.forceMove(turf_to_place_at) //Template Stuff /datum/map_template/hilbertshotel @@ -301,16 +308,38 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) icon_state = "hoteldoor" explosive_resistance = INFINITY var/obj/item/hilbertshotel/parentSphere + /// Mobs currently peeking through this door - needs cleanup if this turf changes type while they're still registered to it. + var/list/mob/peeking_users /turf/closed/indestructible/hoteldoor/Initialize(mapload) . = ..() register_context() +/turf/closed/indestructible/hoteldoor/Destroy(force) + LAZYNULL(peeking_users) + return ..() + /turf/closed/indestructible/hoteldoor/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() context[SCREENTIP_CONTEXT_ALT_LMB] = "Peek through" return CONTEXTUAL_SCREENTIP_SET +// Cancel the peeking of anyone peeking out of this door when the turf changes +/turf/closed/indestructible/hoteldoor/ChangeTurf(path, list/new_baseturfs, flags) + for(var/mob/user in peeking_users) + cancel_peek(user) + return ..() + +/// Cancel the peeking of anyone peeking out of this door while they are deleted +/turf/closed/indestructible/hoteldoor/proc/on_peeker_qdeleted(datum/source, force) + SIGNAL_HANDLER + LAZYREMOVE(peeking_users, source) + +/// Cancels a user's peeking +/turf/closed/indestructible/hoteldoor/proc/cancel_peek(mob/user) + for(var/datum/action/peephole_cancel/cancel_action in user.actions) + INVOKE_ASYNC(cancel_action, TYPE_PROC_REF(/datum/action/peephole_cancel, Trigger)) + /turf/closed/indestructible/hoteldoor/proc/promptExit(mob/living/user) if(!isliving(user)) return @@ -360,23 +389,28 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) to_chat(user, span_notice("You peek through the door's bluespace peephole...")) user.reset_perspective(parentSphere) - var/datum/action/peephole_cancel/PHC = new + var/datum/action/peephole_cancel/peephole_cancel_action = new + peephole_cancel_action.door = src user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) - PHC.Grant(user) + peephole_cancel_action.Grant(user) + LAZYADD(peeking_users, user) RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(check_eye)) + RegisterSignal(user, COMSIG_QDELETING, PROC_REF(on_peeker_qdeleted)) return CLICK_ACTION_SUCCESS /turf/closed/indestructible/hoteldoor/proc/check_eye(mob/user, atom/oldloc, direction) SIGNAL_HANDLER if(get_dist(get_turf(src), get_turf(user)) < 2) return - for(var/datum/action/peephole_cancel/PHC in user.actions) - INVOKE_ASYNC(PHC, TYPE_PROC_REF(/datum/action/peephole_cancel, Trigger)) + cancel_peek(user) + UnregisterSignal(user, COMSIG_QDELETING) /datum/action/peephole_cancel name = "Cancel View" desc = "Stop looking through the bluespace peephole." button_icon_state = "cancel_peephole" + /// The door this peephole view is looking through + var/turf/closed/indestructible/hoteldoor/door /datum/action/peephole_cancel/Trigger(mob/clicker, trigger_flags) . = ..() @@ -385,9 +419,14 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) to_chat(owner, span_warning("You move away from the peephole.")) owner.reset_perspective() owner.clear_fullscreen("remote_view", 0) - UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + door?.UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + LAZYREMOVE(door?.peeking_users, owner) qdel(src) +/datum/action/peephole_cancel/Destroy(force) + door = null + return ..() + // Despite using the ruins.dmi, hilbertshotel is not a ruin /area/misc/hilbertshotel name = "Hilbert's Hotel Room" @@ -409,49 +448,49 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) if(istype(arrived, /obj/item/hilbertshotel)) relocate(arrived) var/list/obj/item/hilbertshotel/hotels = arrived.get_all_contents_type(/obj/item/hilbertshotel) - for(var/obj/item/hilbertshotel/H in hotels) - if(parentSphere == H) - relocate(H) + for(var/obj/item/hilbertshotel/hotel_item in hotels) + if(parentSphere == hotel_item) + relocate(hotel_item) -/area/misc/hilbertshotel/proc/relocate(obj/item/hilbertshotel/H) +/area/misc/hilbertshotel/proc/relocate(obj/item/hilbertshotel/hotel_item) if(prob(0.135685)) //Because screw you - qdel(H) + qdel(hotel_item) return // Prepare for... - var/mob/living/unforeseen_consequences = get_atom_on_turf(H, /mob/living) + var/mob/living/unforeseen_consequences = get_atom_on_turf(hotel_item, /mob/living) // Turns out giving anyone who grabs a Hilbert's Hotel a free, complementary warp whistle is probably bad. // Let's gib the last person to have selected a room number in it. if(unforeseen_consequences) - to_chat(unforeseen_consequences, span_warning("\The [H] starts to resonate. Forcing it to enter itself induces a bluespace paradox, violently tearing your body apart.")) - unforeseen_consequences.investigate_log("has been gibbed by using [H] while inside of it.", INVESTIGATE_DEATHS) + to_chat(unforeseen_consequences, span_warning("\The [hotel_item] starts to resonate. Forcing it to enter itself induces a bluespace paradox, violently tearing your body apart.")) + unforeseen_consequences.investigate_log("has been gibbed by using [hotel_item] while inside of it.", INVESTIGATE_DEATHS) unforeseen_consequences.gib(DROP_ALL_REMAINS) var/turf/targetturf = find_safe_turf() if(!targetturf) - if(GLOB.blobstart.len > 0) + if(length(GLOB.blobstart) > 0) targetturf = get_turf(pick(GLOB.blobstart)) else CRASH("Unable to find a blobstart landmark") - log_game("[H] entered itself. Moving it to [loc_name(targetturf)].") - message_admins("[H] entered itself. Moving it to [ADMIN_VERBOSEJMP(targetturf)].") - H.visible_message(span_danger("[H] almost implodes in upon itself, but quickly rebounds, shooting off into a random point in space!")) - H.forceMove(targetturf) + log_game("[hotel_item] entered itself. Moving it to [loc_name(targetturf)].") + message_admins("[hotel_item] entered itself. Moving it to [ADMIN_VERBOSEJMP(targetturf)].") + hotel_item.visible_message(span_danger("[hotel_item] almost implodes in upon itself, but quickly rebounds, shooting off into a random point in space!")) + hotel_item.forceMove(targetturf) /area/misc/hilbertshotel/Exited(atom/movable/gone, direction) . = ..() if(ismob(gone)) - var/mob/M = gone - if(M.mind) - var/stillPopulated = FALSE - var/list/currentLivingMobs = get_all_contents_type(/mob/living) //Got to catch anyone hiding in anything - for(var/mob/living/L in currentLivingMobs) //Check to see if theres any sentient mobs left. - if(L.mind) - stillPopulated = TRUE + var/mob/gone_mob = gone + if(gone_mob.mind) + var/still_populated = FALSE + var/list/current_living_mobs = get_all_contents_type(/mob/living) //Got to catch anyone hiding in anything + for(var/mob/living/living_mob as anything in current_living_mobs) //Check to see if theres any sentient mobs left. + if(living_mob.mind) + still_populated = TRUE break - if(!stillPopulated) + if(!still_populated) storeRoom() /area/misc/hilbertshotel/proc/storeRoom() @@ -469,16 +508,19 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) for(var/x in 0 to parentSphere.hotelRoomTemp.width-1) for(var/y in 0 to parentSphere.hotelRoomTemp.height-1) var/list/turfContents = list() - for(var/atom/movable/A in locate(room_bottom_left.x + x, room_bottom_left.y + y, room_bottom_left.z)) - if(ismob(A) && !isliving(A)) + for(var/atom/movable/atom in locate(room_bottom_left.x + x, room_bottom_left.y + y, room_bottom_left.z)) + if(ismob(atom) && !isliving(atom)) continue //Don't want to store ghosts - turfContents += A - A.forceMove(storageObj) + turfContents += atom + if(HAS_TRAIT(atom, TRAIT_WALLMOUNTED)) + LAZYADD(storageObj.wallmounted_contents, atom) + qdel(atom.GetComponent(/datum/component/atom_mounted)) + atom.forceMove(storageObj) storage[turfNumber] = turfContents turfNumber++ parentSphere.storedRooms["[roomnumber]"] = storage parentSphere.activeRooms -= "[roomnumber]" - qdel(reservation) + QDEL_NULL(reservation) /area/misc/hilbertshotelstorage name = "Hilbert's Hotel Storage Room" @@ -496,6 +538,8 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) item_flags = ABSTRACT var/roomNumber var/obj/item/hilbertshotel/parentSphere + /// Movables that had their atom_mounted component stripped so they wouldn't deconstruct while stored - remounted when the room is restored. + var/list/wallmounted_contents /obj/item/abstracthotelstorage/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) if(istype(arrived, /obj/machinery/light)) @@ -506,6 +550,10 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) var/mob/target = arrived ADD_TRAIT(target, TRAIT_NO_TRANSFORM, REF(src)) +/obj/item/abstracthotelstorage/Destroy(force) + LAZYNULL(wallmounted_contents) + return ..() + /obj/item/abstracthotelstorage/Exited(atom/movable/gone, direction) . = ..() if(ismob(gone)) @@ -535,7 +583,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) to_chat(user, span_warning("It's to far away to scan!")) return ITEM_INTERACT_BLOCKING var/obj/item/hilbertshotel/sphere = interacting_with - if(sphere.activeRooms.len) + if(length(sphere.activeRooms)) to_chat(user, "Currently Occupied Rooms:") for(var/roomnumber in sphere.activeRooms) to_chat(user, roomnumber) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm index 145a5ad667ae..224ab31f315e 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm @@ -95,11 +95,12 @@ . = ..() stored_organ = pick_weight(allowed_organs) -/obj/structure/meateor_fluff/flesh_pod/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if (attacking_item.get_sharpness() & SHARP_EDGED) - cut_open(user) - return - return ..() +/obj/structure/meateor_fluff/flesh_pod/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (!(tool.get_sharpness() & SHARP_EDGED)) + return NONE + + cut_open(user) + return ITEM_INTERACT_SUCCESS /// Cut the pod open and destroy it /obj/structure/meateor_fluff/flesh_pod/proc/cut_open(mob/user) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm index 7c003b0dc7a4..07034f4882ea 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm @@ -97,7 +97,7 @@ var/message if(busy) message = "it won't budge!" - else if(user.stat != CONSCIOUS) + else if(IS_UNCONSCIOUS_OR_CRIT(user)) message = "you don't have the energy!" if(!isnull(message)) if (COOLDOWN_FINISHED(src, message_cooldown)) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/tramsyndicateoutpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/tramsyndicateoutpost.dm new file mode 100644 index 000000000000..9180d9f07790 --- /dev/null +++ b/code/modules/mapfluff/ruins/spaceruin_code/tramsyndicateoutpost.dm @@ -0,0 +1,15 @@ +// Fluff for the Syndicate outpost which appears on tramstation's z-levels + +/obj/item/paper/fluff/ruins/tram_syndicate/initiation + name = "Initiation Paperwork" + default_raw_text = {"

    Congratulations, Agent INSERT NAME HERE!

    + You have been assigned reconnaissance duty among the orbiting rocks of Indecipheres! Since this location was previously scouted as a potential build site for a Nanotrasen facility, one of our benefactors has taken the opportunity to pre-emptively construct a listening outpost! You have been tasked with monitoring the potential future crew and logging all events onboard. If you are a Nanotrasen employee who has stumbled upon this outpost before it could be properly established: IGNORE THIS PAPER."} + +/obj/item/paper/fluff/ruins/tram_syndicate/blueprints + name = "Station Layout" + icon = 'icons/obj/scrolls.dmi' + icon_state = "blueprints" + inhand_icon_state = "blueprints" + default_raw_text = {"A crude mapping of the station layout based on leaked internal documents and orbital snapshots taken during construction. +
    You're not sure how up-to-date this is anymore..."} + show_written_words = FALSE // Blueprints don't have a "blueprints_words" icon state. diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index e2af58fc9642..8a39d3f04743 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -1548,3 +1548,21 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_atoms_ontop) . = ..() var/turf/our_turf = get_turf(src) ADD_TRAIT(our_turf, TRAIT_NO_TELEPORT, INNATE_TRAIT) + +/// Blesses turfs it is created on, then deletes itself +/obj/effect/mapping_helpers/blessing + name = "holy blessing" + desc = "Holy energies interfere with ethereal travel at this location." + icon = 'icons/effects/effects.dmi' + icon_state = "blessed" + /// Is this blessing visible to those with the ability to see blessed tiles? (chaplains) + var/invisible = FALSE + +/obj/effect/mapping_helpers/blessing/Initialize(mapload) + . = ..() + if (isturf(loc)) + loc.AddElement(/datum/element/blessed_turf, invisible) + return INITIALIZE_HINT_QDEL + +/obj/effect/mapping_helpers/blessing/invisible + invisible = TRUE diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index eacd113c695b..544847143e8b 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -1,7 +1,7 @@ /datum/map_template/ruin/proc/try_to_place(z, list/allowed_areas_typecache, turf/forced_turf, clear_below) var/sanity = forced_turf ? 1 : PLACEMENT_TRIES if(SSmapping.level_trait(z,ZTRAIT_ISOLATED_RUINS)) - return place_on_isolated_level(z) + return reserve_isolated_level(z) while(sanity > 0) sanity-- var/width_border = TRANSITIONEDGE + SPACERUIN_MAP_EDGE_PAD + round(width / 2) @@ -28,41 +28,43 @@ if(!valid) continue - testing("Ruin \"[name]\" placed at ([central_turf.x], [central_turf.y], [central_turf.z])") - - if(clear_below) - var/static/list/clear_below_typecache = typecacheof(list( - /obj/structure/spawner, - /mob/living/simple_animal, - /obj/structure/flora - )) - for(var/turf/T as anything in affected_turfs) - for(var/atom/thing as anything in T) - if(clear_below_typecache[thing.type]) - qdel(thing) - - load(central_turf,centered = TRUE) - loaded++ + testing("Ruin \"[name]\" reserved at ([central_turf.x], [central_turf.y], [central_turf.z])") for(var/turf/T in affected_turfs) T.turf_flags |= NO_RUINS - new /obj/effect/landmark/ruin(central_turf, src) return central_turf -/datum/map_template/ruin/proc/place_on_isolated_level(z) +/datum/map_template/ruin/proc/reserve_isolated_level(z) var/datum/turf_reservation/reservation = SSmapping.request_turf_block_reservation(width, height, 1, z) //Make the new level creation work with different traits. if(!reservation) return var/turf/placement = reservation.bottom_left_turfs[1] - load(placement) - loaded++ for(var/turf/T in get_affected_turfs(placement)) T.turf_flags |= NO_RUINS var/turf/center = locate(placement.x + round(width/2),placement.y + round(height/2),placement.z) - new /obj/effect/landmark/ruin(center, src) return center +///Loads a specified ruin that was previously reserved. +/datum/map_template/ruin/proc/load_reserved(turf/central_turf, clear_below) + var/list/affected_turfs = get_affected_turfs(central_turf, 1) + + if(clear_below) + var/static/list/clear_below_typecache = typecacheof(list( + /obj/structure/spawner, + /mob/living/simple_animal, + /obj/structure/flora + )) + for(var/turf/T as anything in affected_turfs) + for(var/atom/thing as anything in T) + if(clear_below_typecache[thing.type]) + qdel(thing) + + load(central_turf, centered = TRUE) + loaded++ + + new /obj/effect/landmark/ruin(central_turf, src) + /** * Loads the ruins for a given z level. * @param z_levels The z levels to load ruins on. @@ -73,8 +75,9 @@ * @param mineral_budget The budget to spend on ruins that spawn ore vents. Map templates with vents have that defined by mineral_cost. * @param mineral_budget_update What type of ore distribution should spawn from ruins picked by this cave generator? This list is copied from ores_spawned.dm into SSore_generation.ore_vent_minerals. * @param ruin_type The type of ruins that are spawning (ZTRAIT_SPACE_RUINS, ZTRAIT_ICE_RUINS, ZTRAIT_LAVA_RUINS, etc.) + * @param immediate_load If TRUE, loads the placed ruins' maps in right away instead of leaving them queued in SSmapping.reserved_ruins for terrain generation to run first. Only safe to use outside of the roundstart mapping init sequence, once terrain generation has already finished. */ -/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins, clear_below = FALSE, mineral_budget = 15, mineral_budget_update, ruins_type = ZTRAIT_STATION) +/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins, clear_below = FALSE, mineral_budget = 15, mineral_budget_update, ruins_type = ZTRAIT_STATION, immediate_load = FALSE) #if defined(CIBUILDING) && !defined(OPENDREAM) if(SSmapping.current_map.is_unit_test_map && PERFORM_ALL_TESTS(maptest_log_mapping) ) return log_mapping("Skipping ruin seeding due to running on unit test map in workflow enviorment.") @@ -220,6 +223,12 @@ ///Keep track of the active ruins so we can take it in account for map generation. Using bottom lef turf as key SSmapping.active_ruins[locate(bottom_left_x, bottom_left_y, placed_turf.z)] = current_pick + if(immediate_load) + SSmapping.load_ruin_now(current_pick, placed_turf, clear_below) + else + ///Queue up the actual map load for after terrain generation, so cave/biome gen doesn't overwrite the ruin + SSmapping.reserved_ruins += list(list(current_pick, placed_turf, clear_below)) + //Update the available list for(var/datum/map_template/ruin/R in ruins_available) if(R.cost > budget || R.mineral_cost > mineral_budget) diff --git a/code/modules/meteors/meteor_changeling.dm b/code/modules/meteors/meteor_changeling.dm index 268ccccab1c8..2e0d082ec8f5 100644 --- a/code/modules/meteors/meteor_changeling.dm +++ b/code/modules/meteors/meteor_changeling.dm @@ -13,12 +13,14 @@ /obj/effect/meteor/meaty/changeling/Initialize(mapload, turf/target) . = ..() - landing_target = target +/obj/effect/meteor/meaty/changeling/Destroy() + landing_target = null + return ..() + /obj/effect/meteor/meaty/changeling/meteor_effect() ..() - for(var/atom/movable/child in contents) child.forceMove(get_turf(src)) @@ -27,10 +29,11 @@ /obj/effect/meteor/meaty/changeling/shield_defense(obj/machinery/satellite/meteor_shield/defender) landing_target = defender + yeet_changeling() return TRUE -//If the meteor misses the station and deletes itself, we make absolutely sure the changeling reaches the station. -/obj/effect/meteor/meaty/changeling/moved_off_z() +/// Eject changeling onto whatever turf we're aiming at +/obj/effect/meteor/meaty/changeling/proc/yeet_changeling() if(!landing_target) //If our destination turf is gone for some reason, we chuck them at the observer_start landmark (usually at the center of the station) as a last resort. landing_target = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list @@ -41,6 +44,9 @@ changeling.visible_message(span_warning("[changeling] is launched out from inside of \the [src]!"), span_changeling("Sensing that something is terribly wrong, we forcibly eject ourselves from \the [src]!")) playsound(changeling, 'sound/effects/splat.ogg', 50, pressure_affected = FALSE) +//If the meteor misses the station and deletes itself, we make absolutely sure the changeling reaches the station. +/obj/effect/meteor/meaty/changeling/moved_off_z() + yeet_changeling() return ..() /obj/effect/meteor/meaty/changeling/check_examine_award(mob/user) //We don't want this to be a free achievement that comes with the role. diff --git a/code/modules/mining/boulder_processing/_boulder_processing.dm b/code/modules/mining/boulder_processing/_boulder_processing.dm index 324c616ae84e..f6ddcd820bee 100644 --- a/code/modules/mining/boulder_processing/_boulder_processing.dm +++ b/code/modules/mining/boulder_processing/_boulder_processing.dm @@ -124,8 +124,8 @@ return TRUE if(istype(mover, /obj/item/boulder)) return can_process_boulder(mover) - if(isgolem(mover)) - return can_process_golem(mover) + if(isliving(mover)) + return can_process_living_mob(mover) return ..() /** @@ -183,7 +183,7 @@ * * * [rockman][mob/living/carbon/human] - the golem we are trying to main */ -/obj/machinery/bouldertech/proc/can_process_golem(mob/living/carbon/human/rockman) +/obj/machinery/bouldertech/proc/can_process_living_mob(mob/living/rockman) PRIVATE_PROC(TRUE) SHOULD_BE_PURE(TRUE) @@ -195,11 +195,26 @@ if(!COOLDOWN_FINISHED(src, accept_cooldown)) return FALSE - //not processable - if(!istype(rockman) || QDELETED(rockman) || rockman.body_position != LYING_DOWN) + //not a mineral mob + if(!istype(rockman) || QDELETED(rockman)) return FALSE - return TRUE + var/list/bodyparts = rockman.get_bodyparts() + if(length(bodyparts)) //Has bodypart, check if any has the BIO_STONE state (we don't check MOB_MINERAL for carbon mobs cuz we don't want to gib plasmamen, just grind golem parts) + for(var/obj/item/bodypart/part as anything in bodyparts) + if(!(part.biological_state & BIO_STONE)) + return FALSE + else if(!(rockman.mob_biotypes & MOB_MINERAL)) + return FALSE + + //Only return true if they are lying down or are incapacitated. + if(rockman.mobility_flags & MOBILITY_LIEDOWN) + if(rockman.body_position == LYING_DOWN) + return TRUE + else if(rockman.incapacitated) + return TRUE + + return FALSE /** * Accepts a golem to be processed, mainly for memes @@ -207,28 +222,52 @@ * * * [rockman][mob/living/carbon/human] - the golem we are trying to main */ -/obj/machinery/bouldertech/proc/accept_golem(mob/living/carbon/human/rockman) +/obj/machinery/bouldertech/proc/accept_mob(mob/living/rockman) PRIVATE_PROC(TRUE) - if(!can_process_golem(rockman)) + if(!can_process_living_mob(rockman)) return if(!use_energy(active_power_usage * 1.5, force = FALSE)) say("Not enough energy!") return - maim_golem(rockman) + maim_mob(rockman) playsound(src, usage_sound, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) COOLDOWN_START(src, accept_cooldown, 3 SECONDS) /// What effects actually happens to a golem when it is "processed" -/obj/machinery/bouldertech/proc/maim_golem(mob/living/carbon/human/rockman) +/obj/machinery/bouldertech/proc/maim_mob(mob/living/rockman) PROTECTED_PROC(TRUE) Shake(duration = 1 SECONDS) + var/list/bodyparts = rockman.get_bodyparts() + var/gibbing = TRUE + if(length(bodyparts)) //Has bodypart, check if any has the BIO_STONE state and don't just gib them if you can. + var/list/deleted_part_names + gibbing = FALSE + for(var/obj/item/bodypart/part as anything in bodyparts) + if(!(part.biological_state & BIO_STONE)) + continue + if(part.body_zone == BODY_ZONE_CHEST) //Ok, so, without a chest, we're pretty much going to be gibbed anyway. + gibbing = TRUE + break + else + deleted_part_names += part.name + qdel(part) //This calls force_removal() with dismembered set to TRUE + if(!gibbing && length(deleted_part_names)) + var/parts_text = english_list(deleted_part_names) + rockman.investigate_log("had [rockman.p_their()] [parts_text] which all had BIO_STONE biological state) destroyed by a [src]", INVESTIGATE_CARGO) + rockman.visible_message(span_warning("[rockman] is processed by [src]!"), span_userdanger("Your [parts_text] are shred into bits by [src]!")) + + if(gibbing) + gib_mob(rockman) + +///Called at the end of main_mob() if they were a bodyparts-less mob or their chest was made of stone... +/obj/machinery/bouldertech/proc/gib_mob(mob/living/rockman) rockman.visible_message(span_warning("[rockman] is processed by [src]!"), span_userdanger("You get processed into bits by [src]!")) - rockman.investigate_log("was gibbed by [src] for being a golem", INVESTIGATE_DEATHS) + rockman.investigate_log("was gibbed by [src] for having the MOB_MINERAL mob biotype", INVESTIGATE_DEATHS) rockman.gib(DROP_ALL_REMAINS) /obj/machinery/bouldertech/proc/on_entered(datum/source, atom/movable/atom_movable) @@ -238,8 +277,8 @@ INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable) return - if(isgolem(atom_movable)) - INVOKE_ASYNC(src, PROC_REF(accept_golem), atom_movable) + if(isliving(atom_movable)) + INVOKE_ASYNC(src, PROC_REF(accept_mob), atom_movable) return /** diff --git a/code/modules/mining/boulder_processing/refinery.dm b/code/modules/mining/boulder_processing/refinery.dm index f65669d3b401..cc91fbb58e59 100644 --- a/code/modules/mining/boulder_processing/refinery.dm +++ b/code/modules/mining/boulder_processing/refinery.dm @@ -161,7 +161,7 @@ /obj/machinery/bouldertech/refinery/smelter/on_set_panel_open(old_value) update_light_value() -/obj/machinery/bouldertech/refinery/smelter/maim_golem(mob/living/carbon/human/rockman) +/obj/machinery/bouldertech/refinery/smelter/gib_mob(mob/living/rockman) rockman.visible_message(span_warning("[rockman] is processed by [src]!"), span_userdanger("You get melted into rock by [src]!")) - rockman.investigate_log("was melted by [src] for being a golem", INVESTIGATE_DEATHS) + rockman.investigate_log("was melted by [src] for having the MOB_MINERAL mob biotype", INVESTIGATE_DEATHS) rockman.dust() diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm index f0b0b3331f70..ee70b0611937 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm @@ -314,7 +314,7 @@ var/mob/living/basic/mining/demon_afterimage/crusher/friend = new(drop_off) friend.set_faction(list(FACTION_NEUTRAL)) friend.befriend(user) - friend.ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + friend.ai_controller?.set_blackboard_key(BB_CURRENT_TARGET, target) COOLDOWN_START(src, summon_cooldown, 30 SECONDS) ///try to make them spawn all around the target to surround him diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm index e91b2b07f515..6edec34d3d90 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm @@ -244,7 +244,7 @@ return var/mob/living/basic/mining/legion_brood/minion = new (user.loc) minion.assign_creator(user) - minion.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + minion.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, target) /obj/item/crusher_trophy/legionnaire_spine/attack_self(mob/user) if(!isliving(user)) diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index a88de7ff0358..2725d10f8355 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -132,23 +132,27 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list( /obj/structure/marker_beacon/attack_tk(mob/user) return -/obj/structure/marker_beacon/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(istype(I, /obj/item/stack/marker_beacon)) - var/obj/item/stack/marker_beacon/M = I +/obj/structure/marker_beacon/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/stack/marker_beacon)) + var/obj/item/stack/marker_beacon/collection = tool to_chat(user, span_notice("You start picking [src] up...")) - if(do_after(user, remove_speed, target = src) && M.amount + 1 <= M.max_amount) - M.add(1) - playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - qdel(src) - return - if(istype(I, /obj/item/light_eater)) + if(!do_after(user, remove_speed, target = src) || collection.amount + 1 > collection.max_amount) + return ITEM_INTERACT_BLOCKING + + collection.add(1) + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + qdel(src) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/light_eater)) var/obj/effect/decal/cleanable/ash/A = new /obj/effect/decal/cleanable/ash(drop_location()) A.desc += "\nLooks like this used to be \a [src] some time ago." - visible_message(span_danger("[src] is disintegrated by [I]!")) + visible_message(span_danger("[src] is disintegrated by [tool]!")) playsound(src, 'sound/items/tools/welder.ogg', 50, TRUE) qdel(src) - return - return ..() + return ITEM_INTERACT_SUCCESS + + return NONE /obj/structure/marker_beacon/click_alt(mob/living/user) var/input_color = tgui_input_list(user, "Choose a color", "Beacon Color", GLOB.marker_beacon_colors) diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index b237ddc415a3..a3ff4a505ebc 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -114,6 +114,7 @@ desc = "A large tool for digging and moving dirt." icon = 'icons/obj/mining.dmi' icon_state = "shovel" + worn_icon_state = "shovel" inhand_icon_state = "shovel" icon_angle = 135 lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi' @@ -151,6 +152,7 @@ name = "spade" desc = "A small tool for digging and moving dirt." icon_state = "spade" + worn_icon_state = "spade" inhand_icon_state = "spade" icon_angle = -135 lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index d25333bc516f..155526c2ee12 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -224,16 +224,19 @@ return user.examinate(src) -/obj/machinery/mineral/labor_points_checker/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(weapon, /obj/item/card/id/advanced/prisoner)) - return ..() - var/obj/item/card/id/advanced/prisoner/prisoner_id = weapon +/obj/machinery/mineral/labor_points_checker/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/card/id/advanced/prisoner)) + return NONE + + var/obj/item/card/id/advanced/prisoner/prisoner_id = tool if(!prisoner_id.goal) //no goal to reach say("No goal required for this ID.") - return + return ITEM_INTERACT_BLOCKING + say("ID: [prisoner_id.registered_name].") say("Points Collected: [prisoner_id.points] / [prisoner_id.goal].") say("Collect points by bringing smelted minerals to the Labor Shuttle stacking machine. Reach your quota to earn your release.") + return ITEM_INTERACT_SUCCESS /datum/aas_config_entry/security_labor_stacker name = "Security Alert: Labor Camp Release" diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index c9da29aad6ee..bb9f860a258f 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -241,16 +241,14 @@ //SNACKS /obj/item/food/grown/ash_flora - name = "mushroom shavings" - desc = "Some shavings from a tall mushroom. With enough, might serve as a bowl." + name = "unidentified bits of a wierd plant" + desc = "What's this? An intruder, is what it is. Tell somebody about this." icon = 'icons/obj/mining_zones/ash_flora.dmi' icon_state = "mushroom_shavings" abstract_type = /obj/item/food/grown/ash_flora w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE max_integrity = 100 - seed = /obj/item/seeds/lavaland/polypore - wine_power = 20 foodtypes = VEGETABLES /obj/item/food/grown/ash_flora/Initialize(mapload) @@ -258,6 +256,12 @@ pixel_x = base_pixel_x + rand(-4, 4) pixel_y = base_pixel_y + rand(-4, 4) +/obj/item/food/grown/ash_flora/shavings + name = "mushroom shavings" + desc = "Some shavings from a tall mushroom. With enough, might serve as a bowl." + seed = /obj/item/seeds/lavaland/polypore + wine_power = 20 + /obj/item/food/grown/ash_flora/shavings/grind_results() return list(/datum/reagent/toxin/mushroom_powder = 5) diff --git a/code/modules/mining/lavaland/mining_loot/clothing.dm b/code/modules/mining/lavaland/mining_loot/clothing.dm index 8bbca709e2e2..5e1084076149 100644 --- a/code/modules/mining/lavaland/mining_loot/clothing.dm +++ b/code/modules/mining/lavaland/mining_loot/clothing.dm @@ -140,5 +140,5 @@ return NONE if(!ismineralturf(rocks) && !isasteroidturf(rocks)) return NONE - rocks.attackby(src, user) + INVOKE_ASYNC(src, PROC_REF(melee_attack_chain), user, rocks) return COMPONENT_CANCEL_ATTACK_CHAIN diff --git a/code/modules/mining/lavaland/mining_loot/consumables.dm b/code/modules/mining/lavaland/mining_loot/consumables.dm index 29714d7945e6..27e6b53e777b 100644 --- a/code/modules/mining/lavaland/mining_loot/consumables.dm +++ b/code/modules/mining/lavaland/mining_loot/consumables.dm @@ -94,7 +94,6 @@ RegisterSignal(being, COMSIG_MOB_UPDATE_SIGHT, PROC_REF(update_user_sight)) to_chat(being, span_notice("The wisp enhances your vision.")) ADD_TRAIT(being, TRAIT_THERMAL_VISION, REF(src)) - being.update_sight() /obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits, refreshing = FALSE) if(!ismob(orbit_target) || refreshing) @@ -103,7 +102,6 @@ UnregisterSignal(being, COMSIG_MOB_UPDATE_SIGHT) to_chat(being, span_notice("Your vision returns to normal.")) REMOVE_TRAIT(being, TRAIT_THERMAL_VISION, REF(src)) - being.update_sight() return ..() /obj/effect/wisp/proc/update_user_sight(mob/user) diff --git a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm index 4b0019166f72..8abd7608f03a 100644 --- a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm +++ b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm @@ -100,7 +100,7 @@ qdel(src) /obj/item/cursed_katana/attack(mob/living/target, mob/user, list/modifiers, list/attack_modifiers) - if(target.stat < DEAD && target != user) + if(target.stat != DEAD && target != user) drew_blood = TRUE if(ismining(target)) user.changeNext_move(CLICK_CD_RAPID) diff --git a/code/modules/mining/lavaland/mining_loot/godslayer.dm b/code/modules/mining/lavaland/mining_loot/godslayer.dm index 724aa212c675..44c1781dc276 100644 --- a/code/modules/mining/lavaland/mining_loot/godslayer.dm +++ b/code/modules/mining/lavaland/mining_loot/godslayer.dm @@ -54,7 +54,7 @@ /obj/item/clothing/suit/hooded/cloak/godslayer/proc/resurrect(mob/living/carbon/user, new_stat) SIGNAL_HANDLER - if(new_stat > CONSCIOUS && new_stat < DEAD && COOLDOWN_FINISHED(src, effect_cooldown)) + if(new_stat != STABLE && new_stat != DEAD && COOLDOWN_FINISHED(src, effect_cooldown)) COOLDOWN_START(src, effect_cooldown, effect_cooldown_time) //This needs to happen first, otherwise there's an infinite loop user.heal_ordered_damage(heal_amount, damage_heal_order) user.visible_message(span_notice("[user] suddenly revives, as their armor swirls with demonic energy!"), span_notice("You suddenly feel invigorated!")) diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/colossus.dm b/code/modules/mining/lavaland/mining_loot/megafauna/colossus.dm index 05f1c614b9bc..e8804f11e588 100644 --- a/code/modules/mining/lavaland/mining_loot/megafauna/colossus.dm +++ b/code/modules/mining/lavaland/mining_loot/megafauna/colossus.dm @@ -31,11 +31,15 @@ if (feedback) owner.balloon_alert(owner, "can't speak!") return FALSE - if(check_flags & AB_CHECK_CONSCIOUS) - if(owner.stat) - if (feedback) + if((check_flags & AB_CHECK_CONSCIOUS) && IS_UNCONSCIOUS_OR_CRIT(owner)) + if (feedback) + if(owner.stat == DEAD) + owner.balloon_alert(owner, "dead!") + else if(IS_UNCONSCIOUS(owner)) owner.balloon_alert(owner, "unconscious!") - return FALSE + else + owner.balloon_alert(owner, "in critical!") + return FALSE return TRUE /datum/action/item_action/organ_action/colossus/do_effect(trigger_flags) diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm b/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm index e1f62e48ca19..c6f36a944314 100644 --- a/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm +++ b/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm @@ -78,7 +78,7 @@ return var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir) holder.pixel_y = size_check.Height() - ICON_SIZE_Y - if(mob_parent.stat == DEAD || HAS_TRAIT(mob_parent, TRAIT_FAKEDEATH) || isnull(mainframe)) + if(IS_DEAD_OR_FAKING(mob_parent) || isnull(mainframe)) holder.icon_state = "huddead2" holder.pixel_x = -8 // new icon states? nuh uh else diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 09bdd931fddd..676a214a37d4 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -18,14 +18,15 @@ /// What random loot spawner our chest uses var/loot_to_spawn = /obj/effect/spawner/random/mining_loot -/obj/structure/closet/crate/necropolis/tendril/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(item, /obj/item/skeleton_key) || spawned_loot) +/obj/structure/closet/crate/necropolis/tendril/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/skeleton_key) || spawned_loot) return ..() new loot_to_spawn(src) spawned_loot = TRUE - qdel(item) + qdel(tool) to_chat(user, span_notice("You disable the magic lock, revealing the loot.")) + return ITEM_INTERACT_SUCCESS /obj/structure/closet/crate/necropolis/tendril/before_open(mob/living/user, force) . = ..() diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 8f9658445583..3e3d879a7a6a 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -172,6 +172,29 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ return TRUE return FALSE +/obj/item/stack/ore/glass/interact_with_atom(atom/target, mob/living/user, list/modifiers) + . = ..() + if (.) + return + + if(!target.is_dunkable()) + return NONE + + var/datum/reagent/cement/cement = target.reagents?.has_reagent(/datum/reagent/cement, check_subtypes=TRUE) + if (!cement) + return NONE + + var/sand_to_use = min(floor(cement.volume / cement.units_per_aggregate), amount) + if (!sand_to_use) + to_chat(user, span_warning("You don't have enough [cement.name] to mix with [src].")) + return ITEM_INTERACT_BLOCKING + + use(sand_to_use) + target.reagents.convert_reagent(cement.type, cement.concrete_type, conversion_volume = sand_to_use * cement.units_per_aggregate) + user.visible_message(span_warning("[user] slips something into [target]!"), span_notice("You dissolve [src] in [target]."), null, 2) + playsound(target, 'sound/effects/bubbles/bubbles.ogg', 50, TRUE) + return ITEM_INTERACT_SUCCESS + /obj/item/stack/ore/glass/thirty amount = 30 @@ -189,6 +212,13 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ mine_experience = 0 merge_type = /obj/item/stack/ore/glass/siderite +/obj/item/stack/ore/glass/concrete_powder + name = "concrete powder" + icon_state = "concrete_powder" + singular_name = "concrete powder pile" + mine_experience = 0 + merge_type = /obj/item/stack/ore/glass/concrete_powder + /obj/item/stack/ore/plasma name = "plasma ore" icon_state = "plasma" @@ -350,13 +380,15 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ /obj/item/gibtonite/IsSpecialAssembly() return TRUE -/obj/item/gibtonite/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(istype(I, /obj/item/assembly_holder) && !rig) - var/obj/item/assembly_holder/holder = I +/obj/item/gibtonite/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/assembly_holder) && !rig) + var/obj/item/assembly_holder/holder = tool if(!(locate(/obj/item/assembly/igniter) in holder.assemblies)) - return ..() + return NONE + if(!user.transferItemToLoc(holder, src)) - return + return ITEM_INTERACT_BLOCKING + add_fingerprint(user) rig = holder holder.master = src @@ -368,26 +400,34 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ log_bomber(user, "attached [holder] to ", src) attacher = key_name(user) user.balloon_alert_to_viewers("attached rig") - return - - if(I.tool_behaviour == TOOL_WRENCH && rig) - rig.on_found() - if(QDELETED(src)) - return - user.balloon_alert_to_viewers("detached rig") - user.log_message("detached [rig] from [src].", LOG_GAME) - user.put_in_hands(rig) - return + return ITEM_INTERACT_SUCCESS - if(I.tool_behaviour == TOOL_MINING || istype(I, /obj/item/resonator) || I.force >= 10) + if(tool.tool_behaviour == TOOL_MINING || istype(tool, /obj/item/resonator) || tool.force >= 10) GibtoniteReaction(user, "A resonator has primed for detonation a") - return + return ITEM_INTERACT_SUCCESS - if(istype(I, /obj/item/mining_scanner) || istype(I, /obj/item/t_scanner/adv_mining_scanner) || I.tool_behaviour == TOOL_MULTITOOL) + if(istype(tool, /obj/item/mining_scanner) || istype(tool, /obj/item/t_scanner/adv_mining_scanner)) defuse(user) - return + return ITEM_INTERACT_SUCCESS - return ..() + return NONE + +/obj/item/gibtonite/wrench_act(mob/living/user, obj/item/tool) + if(!rig) + return NONE + + rig.on_found() + if(QDELETED(src)) + return ITEM_INTERACT_BLOCKING + + user.balloon_alert_to_viewers("detached rig") + user.log_message("detached [rig] from [src].", LOG_GAME) + user.put_in_hands(rig) + return ITEM_INTERACT_SUCCESS + +/obj/item/gibtonite/multitool_act(mob/living/user, obj/item/tool) + defuse(user) + return ITEM_INTERACT_SUCCESS /// Stop the reaction and reduce ore explosive power /obj/item/gibtonite/proc/defuse(mob/defuser) @@ -540,22 +580,23 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ . = ..() . += span_info("It's worth [value] [MONEY_NAME_AUTOPURAL(value)].") -/obj/item/coin/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/CC = W - if(string_attached) - to_chat(user, span_warning("There already is a string attached to this coin!")) - return +/obj/item/coin/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/cable_coil)) + return NONE - if (CC.use(1)) - add_overlay("coin_string_overlay") - string_attached = 1 - to_chat(user, span_notice("You attach a string to the coin.")) - else - to_chat(user, span_warning("You need one length of cable to attach a string to the coin!")) - return - else - ..() + var/obj/item/stack/cable_coil/string = tool + if(string_attached) + to_chat(user, span_warning("There already is a string attached to this coin!")) + return ITEM_INTERACT_BLOCKING + + if(!string.use(1)) + to_chat(user, span_warning("You need one length of cable to attach a string to the coin!")) + return ITEM_INTERACT_BLOCKING + + add_overlay("coin_string_overlay") + string_attached = TRUE + to_chat(user, span_notice("You attach a string to the coin.")) + return ITEM_INTERACT_SUCCESS /obj/item/coin/wirecutter_act(mob/living/user, obj/item/I) ..() diff --git a/code/modules/mining/satchel_ore_box.dm b/code/modules/mining/satchel_ore_box.dm index 0c2011e2c546..db53d3e1e2cf 100644 --- a/code/modules/mining/satchel_ore_box.dm +++ b/code/modules/mining/satchel_ore_box.dm @@ -56,16 +56,17 @@ deconstruct(TRUE) return ITEM_INTERACT_SUCCESS -/obj/structure/ore_box/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(istype(weapon, /obj/item/stack/ore) || istype(weapon, /obj/item/boulder)) - user.transferItemToLoc(weapon, src) - return TRUE - else if(weapon.atom_storage) - weapon.atom_storage.remove_type(/obj/item/stack/ore, src, INFINITY, TRUE, FALSE, user, null) - to_chat(user, span_notice("You empty the ore in [weapon] into \the [src].")) - return TRUE - else - return ..() +/obj/structure/ore_box/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/stack/ore) || istype(tool, /obj/item/boulder)) + user.transferItemToLoc(tool, src) + return ITEM_INTERACT_SUCCESS + + if(tool.atom_storage) + tool.atom_storage.remove_type(/obj/item/stack/ore, src, INFINITY, TRUE, FALSE, user, null) + to_chat(user, span_notice("You empty the ore in [tool] into \the [src].")) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/structure/ore_box/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) . = ..() diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 0a556c850a7c..09f26ee9835d 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -306,18 +306,23 @@ mind.active = FALSE //we wish to transfer the key manually var/mob/living/spawning_mob = mind.assigned_role.get_spawn_mob(client, destination) - if(QDELETED(src) || !HAS_CONNECTED_PLAYER(src)) - return // Disconnected while checking for the appearance ban. + if(QDELETED(src)) + return + + // Annoyingly the AI mob yoinks our client on init so we have to check for it here + var/client/player_client = src.client || spawning_mob.client + if(isnull(player_client)) + return if(!isAI(spawning_mob)) // Unfortunately there's still snowflake AI code out there. // transfer_to sets mind to null var/datum/mind/preserved_mind = mind - preserved_mind.original_character_slot_index = client.prefs.default_slot + preserved_mind.original_character_slot_index = player_client.prefs.default_slot preserved_mind.transfer_to(spawning_mob) //won't transfer key since the mind is not active preserved_mind.set_original_character(spawning_mob) - LAZYADD(persistent_client.joined_as_slots, "[client.prefs.default_slot]") - client.init_verbs() + LAZYADD(player_client.persistent_client.joined_as_slots, "[player_client.prefs.default_slot]") + player_client.init_verbs() . = spawning_mob new_character = . diff --git a/code/modules/mob/dead/observer/ghost_menu.dm b/code/modules/mob/dead/observer/ghost_menu.dm index 8119ce49a4e4..cd3d7bd35250 100644 --- a/code/modules/mob/dead/observer/ghost_menu.dm +++ b/code/modules/mob/dead/observer/ghost_menu.dm @@ -103,6 +103,9 @@ GLOBAL_DATUM_INIT(ghost_menu, /datum/ghost_menu, new) "desc" = GLOB.poll_ignore_desc[key], )) + var/current_width = user.client?.view_size?.width || 0 + data["current_extra_view"] = current_width > 0 ? (current_width - 1) / 2 : 0 + data["hud_info"] = list( list( "name" = "Data HUDs", diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 00bce865135e..874d03916f54 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -148,9 +148,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) toggle_ghost_hud_flag(GHOST_VISION | GHOST_DATA_HUDS) SSpoints_of_interest.make_point_of_interest(src) - ADD_TRAIT(src, TRAIT_HEAR_THROUGH_DARKNESS, INNATE_TRAIT) - ADD_TRAIT(src, TRAIT_GOOD_HEARING, INNATE_TRAIT) - ADD_TRAIT(src, TRAIT_DETECT_STORM, INNATE_TRAIT) + add_traits(list(TRAIT_HEAR_THROUGH_DARKNESS, TRAIT_GOOD_HEARING, TRAIT_DETECT_STORM, TRAIT_GHOSTLY_MOB), INNATE_TRAIT) /mob/dead/observer/get_photo_description(obj/item/camera/camera) if(!invisibility || camera.see_ghosts) @@ -308,7 +306,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp */ GAME_VERB_DESC(/mob/living, ghost, "Ghost", "Relinquish your life and enter the land of the dead.", "OOC") - if(stat != CONSCIOUS && stat != DEAD) + if(stat != STABLE && stat != DEAD) succumb() if(stat == DEAD) if(!HAS_TRAIT(src, TRAIT_CORPSELOCKED)) //corpse-locked have to confirm with the alert below diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 28c2f2e72821..ce23ca8ab181 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -77,7 +77,7 @@ /datum/emote/flip key = "flip" key_third_person = "flips" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/eye/imaginary_friend) mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai, /mob/eye/imaginary_friend) /// The probability we fall our our arse @@ -120,7 +120,7 @@ /datum/emote/spin key = "spin" key_third_person = "spins" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer, /mob/eye/imaginary_friend) mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/eye/imaginary_friend) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index d61dcd47c5ee..858cd3ac4366 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -194,6 +194,7 @@ if(QDELETED(I)) // this is here because some ABSTRACT items like slappers and circle hands could be moved from hand to hand then delete, which meant you'd have a null in your hand until you cleared it (say, by dropping it) held_items[hand_index] = null return FALSE + SEND_SIGNAL(I, COMSIG_ITEM_ENTERED_HANDS, src, hand_index) return hand_index //Puts the item into the first available left hand if possible and calls all necessary triggers/updates. returns 1 on success. @@ -448,8 +449,8 @@ item_dropping.forceMove(newloc) has_unequipped(item_dropping, silent) - SEND_SIGNAL(item_dropping, COMSIG_ITEM_POST_UNEQUIP, force, newloc, no_move, invdrop, silent) - SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, item_dropping, force, newloc, no_move, invdrop, silent) + SEND_SIGNAL(item_dropping, COMSIG_ITEM_POST_UNEQUIP, force, newloc, no_move, invdrop, silent, hand_index) + SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, item_dropping, force, newloc, no_move, invdrop, silent, hand_index) return TRUE /** diff --git a/code/modules/mob/living/basic/alien/_alien.dm b/code/modules/mob/living/basic/alien/_alien.dm index af0bd3491722..e76c7fec11b0 100644 --- a/code/modules/mob/living/basic/alien/_alien.dm +++ b/code/modules/mob/living/basic/alien/_alien.dm @@ -67,23 +67,25 @@ /mob/living/basic/alien/get_butt_sprite() return icon('icons/mob/butts.dmi', BUTT_SPRITE_XENOMORPH) -///Places alien weeds on the turf the mob is currently standing on. +///Places alien weeds on the turf the mob is currently standing on. Returns TRUE if weeds were placed. /mob/living/basic/alien/proc/place_weeds() if(!isturf(loc) || isspaceturf(loc)) - return + return FALSE if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) - return + return FALSE visible_message(span_alertalien("[src] plants some alien weeds!")) new /obj/structure/alien/weeds/node(loc) + return TRUE -///Lays an egg on the turf the mob is currently standing on. +///Lays an egg on the turf the mob is currently standing on. Returns TRUE if an egg was laid. /mob/living/basic/alien/proc/lay_alien_egg() if(!isturf(loc) || isspaceturf(loc)) - return + return FALSE if(locate(/obj/structure/alien/egg) in get_turf(src)) - return + return FALSE visible_message(span_alertalien("[src] lays an egg!")) new /obj/structure/alien/egg(loc) + return TRUE /mob/living/basic/alien/get_bloodtype() return get_blood_type(BLOOD_TYPE_XENO) diff --git a/code/modules/mob/living/basic/alien/alien.bt.json b/code/modules/mob/living/basic/alien/alien.bt.json new file mode 100644 index 000000000000..5c8713ca333d --- /dev/null +++ b/code/modules/mob/living/basic/alien/alien.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/alien", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_alien", + "bindings": { + "buewuqr6": "/datum/bt_node/subtree/melee_alien_combat", + "bkzbx73d": "/datum/bt_node/subtree/lay_alien_egg" + } +} diff --git a/code/modules/mob/living/basic/alien/alien_ai.dm b/code/modules/mob/living/basic/alien/alien_ai.dm index decca14cec86..2e31eed32635 100644 --- a/code/modules/mob/living/basic/alien/alien_ai.dm +++ b/code/modules/mob/living/basic/alien/alien_ai.dm @@ -1,76 +1,57 @@ /datum/ai_controller/basic_controller/alien + behavior_tree_json = "code/modules/mob/living/basic/alien/alien.bt.json" ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, + BB_RANGED_SKIRMISH_MIN_DISTANCE = 2, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 3, ) movement_delay = 0.8 SECONDS /datum/ai_controller/basic_controller/alien/sentinel - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/alien, - ) + behavior_tree_json = "code/modules/mob/living/basic/alien/sentinel.bt.json" + /datum/ai_controller/basic_controller/alien/drone - idle_behavior = /datum/idle_behavior/idle_random_walk/plant_weeds + behavior_tree_json = "code/modules/mob/living/basic/alien/drone.bt.json" /datum/ai_controller/basic_controller/alien/queen - idle_behavior = /datum/idle_behavior/idle_random_walk/plant_weeds/queen + behavior_tree_json = "code/modules/mob/living/basic/alien/queen.bt.json" - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/alien, - ) -/** - * Alien projectile - * Try to avoid friendly fire, and has a 3 second delay. - */ -/datum/ai_planning_subtree/basic_ranged_attack_subtree/alien - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/alien - -/datum/ai_behavior/basic_ranged_attack/alien - action_cooldown = 3 SECONDS - required_distance = 3 - avoid_friendly_fire = TRUE - -/datum/idle_behavior/idle_random_walk/plant_weeds - var/plant_cooldown = 30 - var/plants_off = 0 - -/datum/idle_behavior/idle_random_walk/plant_weeds/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - if(!.) - return . - plant_cooldown-- +/datum/bt_node/subtree/basic_alien + behavior_tree_json = "code/modules/mob/living/basic/alien/basic_alien.bt.json" + + +/datum/bt_node/subtree/ranged_alien_combat + behavior_tree_json = "code/modules/mob/living/basic/alien/ranged_alien_combat.bt.json" + + +/datum/bt_node/subtree/melee_alien_combat + behavior_tree_json = "code/modules/mob/living/basic/alien/melee_alien_combat.bt.json" + +/datum/bt_node/subtree/plant_alien_weeds + behavior_tree_json = "code/modules/mob/living/basic/alien/plant_alien_weeds.bt.json" + +/datum/bt_node/subtree/lay_alien_egg + behavior_tree_json = "code/modules/mob/living/basic/alien/lay_alien_egg.bt.json" + + +/// Plants alien weeds on the pawn's current turf. Fails if the pawn can't plant or weeds couldn't be placed. +/datum/bt_node/ai_behavior/plant_alien_weeds + +/datum/bt_node/ai_behavior/plant_alien_weeds/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/alien/alien_pawn = controller.pawn - if(alien_pawn.can_plant_weeds && !plants_off && plant_cooldown <= 0) - plant_cooldown = initial(plant_cooldown) - alien_pawn.place_weeds() - -/datum/idle_behavior/idle_random_walk/plant_weeds/queen - var/eggs_off = 0 - var/egg_cooldown = 30 - -/datum/idle_behavior/idle_random_walk/plant_weeds/queen/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - . = ..() - if(!.) - return . - egg_cooldown-- + if(!alien_pawn.can_plant_weeds || !alien_pawn.place_weeds()) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Lays an alien egg on the pawn's current turf. Fails if the pawn can't lay eggs or an egg couldn't be placed. +/datum/bt_node/ai_behavior/lay_alien_egg + +/datum/bt_node/ai_behavior/lay_alien_egg/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/alien/alien_pawn = controller.pawn - if(alien_pawn.can_lay_eggs && !eggs_off && egg_cooldown <= 0) - egg_cooldown = initial(egg_cooldown) - alien_pawn.lay_alien_egg() + if(!alien_pawn.can_lay_eggs || !alien_pawn.lay_alien_egg()) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/modules/mob/living/basic/alien/basic_alien.bt.json b/code/modules/mob/living/basic/alien/basic_alien.bt.json new file mode 100644 index 000000000000..c9163e6f7751 --- /dev/null +++ b/code/modules/mob/living/basic/alien/basic_alien.bt.json @@ -0,0 +1,60 @@ +{ + "dm_type": "/datum/bt_node/subtree/basic_alien", + "bindings": { + "buewuqr6": { + "label": "Combat Subtree", + "default": "/datum/bt_node/subtree" + }, + "bkzbx73d": { + "label": "Idle Behavior", + "default": "/datum/bt_node/subtree/random_walk" + } + }, + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "$buewuqr6" + } + }, + { + "type": "subtree", + "subtype": "$bkzbx73d" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/alien/drone.bt.json b/code/modules/mob/living/basic/alien/drone.bt.json new file mode 100644 index 000000000000..7763e2fcd9b4 --- /dev/null +++ b/code/modules/mob/living/basic/alien/drone.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/alien/drone", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_alien", + "bindings": { + "buewuqr6": "/datum/bt_node/subtree/melee_alien_combat", + "bkzbx73d": "/datum/bt_node/ai_behavior/plant_alien_weeds" + } +} diff --git a/code/modules/mob/living/basic/alien/lay_alien_egg.bt.json b/code/modules/mob/living/basic/alien/lay_alien_egg.bt.json new file mode 100644 index 000000000000..1cb4f5336b0d --- /dev/null +++ b/code/modules/mob/living/basic/alien/lay_alien_egg.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/bt_node/subtree/lay_alien_egg", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_ALIEN_PLANT_COOLDOWN", + "cooldown_duration": "30 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/lay_alien_egg" + } +} diff --git a/code/modules/mob/living/basic/alien/melee_alien_combat.bt.json b/code/modules/mob/living/basic/alien/melee_alien_combat.bt.json new file mode 100644 index 000000000000..51a646244426 --- /dev/null +++ b/code/modules/mob/living/basic/alien/melee_alien_combat.bt.json @@ -0,0 +1,47 @@ +{ + "dm_type": "/datum/bt_node/subtree/melee_alien_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] +} diff --git a/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.json b/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.json new file mode 100644 index 000000000000..a8bb21032ce6 --- /dev/null +++ b/code/modules/mob/living/basic/alien/plant_alien_weeds.bt.json @@ -0,0 +1,23 @@ +{ + "dm_type": "/datum/bt_node/subtree/plant_alien_weeds", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_ALIEN_PLANT_COOLDOWN", + "cooldown_duration": "30 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/plant_alien_weeds" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] +} diff --git a/code/modules/mob/living/basic/alien/queen.bt.json b/code/modules/mob/living/basic/alien/queen.bt.json new file mode 100644 index 000000000000..d4570cee74f1 --- /dev/null +++ b/code/modules/mob/living/basic/alien/queen.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/alien/queen", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_alien", + "bindings": { + "buewuqr6": "/datum/bt_node/subtree/ranged_alien_combat", + "bkzbx73d": "/datum/bt_node/ai_behavior/lay_alien_egg" + } +} diff --git a/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.json b/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.json new file mode 100644 index 000000000000..47f467ed9427 --- /dev/null +++ b/code/modules/mob/living/basic/alien/ranged_alien_combat.bt.json @@ -0,0 +1,48 @@ +{ + "dm_type": "/datum/bt_node/subtree/ranged_alien_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "time_between_perform": "3 SECONDS", + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "avoid_friendly_fire": true + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "approach_movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] +} diff --git a/code/modules/mob/living/basic/alien/sentinel.bt.json b/code/modules/mob/living/basic/alien/sentinel.bt.json new file mode 100644 index 000000000000..a0e73fda01a9 --- /dev/null +++ b/code/modules/mob/living/basic/alien/sentinel.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/alien/sentinel", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_alien", + "bindings": { + "buewuqr6": "/datum/bt_node/subtree/ranged_alien_combat" + } +} diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm index fe19aad2b6c5..dcfa637848b3 100644 --- a/code/modules/mob/living/basic/basic_defense.dm +++ b/code/modules/mob/living/basic/basic_defense.dm @@ -272,7 +272,7 @@ if(health <= 0) death() else - set_stat(CONSCIOUS) + set_stat(STABLE) med_hud_set_status() /mob/living/basic/emp_act(severity) diff --git a/code/modules/mob/living/basic/blob_minions/blob_ai.dm b/code/modules/mob/living/basic/blob_minions/blob_ai.dm index b8368fc7f3ca..208d38dee9b5 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_ai.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_ai.dm @@ -3,55 +3,34 @@ * Only notable quirk is that it uses JPS movement, simple avoidance would fail to realise it can path through blobs */ /datum/ai_controller/basic_controller/blobbernaut + behavior_tree_json = "code/modules/mob/living/basic/blob_minions/blobbernaut.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /** * Move to a point designated by the overmind, otherwise just slap people nearby */ /datum/ai_controller/basic_controller/blob_zombie + behavior_tree_json = "code/modules/mob/living/basic/blob_minions/blob_zombie.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/travel_to_point/and_clear_target, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /** * As blob zombie but will prioritise attacking corpses to zombify them */ /datum/ai_controller/basic_controller/blob_spore + behavior_tree_json = "code/modules/mob/living/basic/blob_minions/blob_spore.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/find_and_hunt_target/corpses/human, - /datum/ai_planning_subtree/travel_to_point/and_clear_target, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/blob_minions/blob_spore.bt.json b/code/modules/mob/living/basic/blob_minions/blob_spore.bt.json new file mode 100644 index 000000000000..17d02701525a --- /dev/null +++ b/code/modules/mob/living/basic/blob_minions/blob_spore.bt.json @@ -0,0 +1,69 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/blob_spore", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bvtz06kb": "BB_CURRENT_HUNTING_TARGET", + "b3y599q4": "BB_CURRENT_HUNTING_TARGET", + "brrasnah": "BB_CURRENT_HUNTING_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_TRAVEL_DESTINATION" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TRAVEL_DESTINATION", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TRAVEL_DESTINATION" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/dead_mob", + "target_source": "/datum/target_source/oview_single_type/human_mob" + } + } + ] +} diff --git a/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.json b/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.json new file mode 100644 index 000000000000..7d2cf1b5b58a --- /dev/null +++ b/code/modules/mob/living/basic/blob_minions/blob_zombie.bt.json @@ -0,0 +1,42 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/blob_zombie", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_TRAVEL_DESTINATION" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TRAVEL_DESTINATION", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TRAVEL_DESTINATION" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.json b/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.json new file mode 100644 index 000000000000..89e67315111b --- /dev/null +++ b/code/modules/mob/living/basic/blob_minions/blobbernaut.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/blobbernaut", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm index a251db94200a..682d24646b5d 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm @@ -68,11 +68,10 @@ Difficulty: Medium grant_actions_by_list(get_innate_actions()) ai_controller.set_blackboard_key(BB_BDM_RANGED_ATTACK_COOLDOWN, ranged_attack_cooldown_duration) - RegisterSignals(ai_controller, list(AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/basic_melee_attack), AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/targeted_mob_ability)), PROC_REF(handle_saw_transformation)) RegisterSignal(src, COMSIG_LIVING_DROP_LOOT, PROC_REF(death_effect)) - AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET)) + AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET)) /// Block deletion of their saw under normal circumstances. It is fused to their hands as far as we're concerned. /mob/living/basic/boss/blood_drunk_miner/proc/on_saw_deleted(datum/source, force) @@ -99,12 +98,6 @@ Difficulty: Medium ) return innate_abilities -/// Invokes the transform weapon ability when signaled by the AI controller. -/mob/living/basic/boss/blood_drunk_miner/proc/handle_saw_transformation() - SIGNAL_HANDLER - - INVOKE_ASYNC(ai_controller.blackboard[BB_BDM_TRANSFORM_WEAPON_ABILITY], TYPE_PROC_REF(/datum/action, Trigger), src, NONE) - /mob/living/basic/boss/blood_drunk_miner/proc/transform_saw() miner_saw.attack_self(src) var/saw_open = HAS_TRAIT(miner_saw, TRAIT_TRANSFORM_ACTIVE) diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm index 64efdb0b8a96..41c22c242f8c 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm @@ -5,6 +5,7 @@ /// - If in melee range, use melee attacks (depending on saw state) /// - After attacks, transform saw state from open to closed. /datum/ai_controller/blood_drunk_miner + behavior_tree_json = "code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/no_gutted_mobs, BB_TARGET_MINIMUM_STAT = DEAD, @@ -14,53 +15,10 @@ movement_delay = 0.25 SECONDS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka, - /datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/// Parent type that contains key logic important for subsequent abilities -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk - /// Range where we determine what distance we're at. If higher, we consider ourselves out of PKA range and will dash attack instead. Inclusive when it comes to choosing to shoot PKA. - var/pka_range = 3 - -/// Check our blackboard to see if we are able to use a ranged ability in the first place -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action) - . = ..() - if(controller.blackboard[BB_BDM_RANGED_ATTACK_COOLDOWN] > world.time) - return FALSE - controller.override_blackboard_key(BB_BDM_RANGED_ATTACK_COOLDOWN, world.time + controller.blackboard[BB_BDM_RANGED_ATTACK_COOLDOWN_DURATION]) - -/// The BDM will preferentially shoot its PKA within range over other abilities -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka - ability_key = BB_BDM_KINETIC_ACCELERATOR_ABILITY - -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action) - . = ..() - // do not shoot the PKA if we are not in the right range - var/mob/living/pawn = controller.pawn - var/mob/living/victim = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(get_dist(pawn, victim) >= pka_range) - return FALSE - return TRUE - -/// The BDM will dash attack if not in PKA range -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack - ability_key = BB_BDM_DASH_ATTACK_ABILITY - -/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action) - . = ..() - // only dash attack if we are out of PKA range - var/mob/living/pawn = controller.pawn - var/mob/living/victim = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(get_dist(pawn, victim) < pka_range) - return FALSE - return TRUE /datum/ai_controller/blood_drunk_miner/doom movement_delay = 0.5 SECONDS + + +/datum/bt_node/subtree/blood_drunk_miner_combat + behavior_tree_json = "code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.json" diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.json b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.json new file mode 100644 index 000000000000..f6c7192c71b3 --- /dev/null +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner.bt.json @@ -0,0 +1,45 @@ +{ + "dm_type": "/datum/ai_controller/blood_drunk_miner", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/blood_drunk_miner_combat" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": 0 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.json b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.json new file mode 100644 index 000000000000..ad023b0ae2ad --- /dev/null +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner_combat.bt.json @@ -0,0 +1,132 @@ +{ + "dm_type": "/datum/bt_node/subtree/blood_drunk_miner_combat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/mob_stat_at_least", + "vars": { + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_BDM_KINETIC_ACCELERATOR_ABILITY", + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 3 + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_BDM_TRANSFORM_WEAPON_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "invert": false, + "target_key": "BB_CURRENT_TARGET", + "min_distance": 3 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_BDM_DASH_ATTACK_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_BDM_TRANSFORM_WEAPON_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/boss/boss.dm b/code/modules/mob/living/basic/boss/boss.dm index 298f1b8ca9a0..0d533e04cb96 100644 --- a/code/modules/mob/living/basic/boss/boss.dm +++ b/code/modules/mob/living/basic/boss/boss.dm @@ -81,7 +81,8 @@ return if(should_devour(target)) devour(target) - return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN + return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN + return /// Determines if this mob is worth devouring /mob/living/basic/boss/proc/should_devour(mob/living/victim) diff --git a/code/modules/mob/living/basic/boss/thing/thing.dm b/code/modules/mob/living/basic/boss/thing/thing.dm index 02943669ff24..2b137cbdb34a 100644 --- a/code/modules/mob/living/basic/boss/thing/thing.dm +++ b/code/modules/mob/living/basic/boss/thing/thing.dm @@ -69,8 +69,8 @@ if(!maploaded) return spawn_loc = loc - RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(target_gained)) - RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(target_lost)) + RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET), PROC_REF(target_gained)) + RegisterSignal(src, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_CURRENT_TARGET), PROC_REF(target_lost)) SSqueuelinks.add_to_queue(src, RUIN_QUEUE, 0) return INITIALIZE_HINT_LATELOAD @@ -79,14 +79,14 @@ /mob/living/basic/boss/thing/update_icon_state() . = ..() - if(stat) + if(IS_UNCONSCIOUS_OR_CRIT(src)) icon_state = "dead" return icon_state = "p[phase]" icon_living = icon_state /mob/living/basic/boss/thing/adjust_health(amount, updating_health = TRUE, forced = FALSE) - if(phase_invulnerability_timer || phase == 3 || stat || amount <= 0) + if(phase_invulnerability_timer || phase == 3 || IS_UNCONSCIOUS_OR_CRIT(src) || amount <= 0) return ..() var/potential_excess = bruteloss + amount - (maxHealth/3)*phase if(potential_excess > 0) @@ -126,13 +126,13 @@ /// If we started premapped, and we lost our target, start a 3 minute timer to return to spawn turf unless we gain aggro again /mob/living/basic/boss/thing/proc/target_lost(datum/source) SIGNAL_HANDLER - if(stat || client || loc == spawn_loc || return_timer) + if(IS_UNCONSCIOUS_OR_CRIT(src) || client || loc == spawn_loc || return_timer) return return_timer = addtimer(CALLBACK(src, PROC_REF(return_to_spawn_check)), 3 MINUTES, TIMER_STOPPABLE | TIMER_DELETE_ME) /// Return us to our spawn loc (ruin boss only) if we are alive and have an ai controller and our loc isnt the spawn loc /mob/living/basic/boss/thing/proc/return_to_spawn_check() - if(isnull(ai_controller) || QDELETED(src) || loc == spawn_loc || stat || client) + if(isnull(ai_controller) || QDELETED(src) || loc == spawn_loc || IS_UNCONSCIOUS_OR_CRIT(src) || client) return return_to_spawnloc() @@ -179,9 +179,9 @@ /// Immediately set out blackboard target key (if empty) to whoever attacks us; this is primarily because it has a lowered aggro range and a high sight range /mob/living/basic/boss/thing/proc/immediate_aggro(datum/source, mob/attacker, flags) SIGNAL_HANDLER - if(isnull(ai_controller) || stat || !istype(attacker) || ai_controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + if(isnull(ai_controller) || IS_UNCONSCIOUS_OR_CRIT(src) || !istype(attacker) || ai_controller.blackboard_key_exists(BB_CURRENT_TARGET)) return - ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, attacker) + ai_controller?.set_blackboard_key(BB_CURRENT_TARGET, attacker) /mob/living/basic/boss/thing/vv_edit_var(vname, vval) . = ..() @@ -303,7 +303,7 @@ /// queue id var/queue_id = RUIN_QUEUE /// blackboard key for target - var/target_bb_key = BB_BASIC_MOB_CURRENT_TARGET + var/target_bb_key = BB_CURRENT_TARGET /obj/structure/aggro_gate/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/basic/boss/thing/thing_ai.dm b/code/modules/mob/living/basic/boss/thing/thing_ai.dm index e8956cac2b13..55668658db2f 100644 --- a/code/modules/mob/living/basic/boss/thing/thing_ai.dm +++ b/code/modules/mob/living/basic/boss/thing/thing_ai.dm @@ -1,72 +1,17 @@ /datum/ai_controller/basic_controller/thing_boss + behavior_tree_json = "code/modules/mob/living/basic/boss/thing/thing_boss.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/no_gutted_mobs, BB_TARGET_MINIMUM_STAT = DEAD, // Will attack dead ungutted mobs - BB_THETHING_ATTACKMODE = TRUE, //Whether we are using our melee abilities right now + BB_THETHING_MELEEMODE = TRUE, //Whether we are using our melee abilities right now BB_THETHING_NOAOE = TRUE, // Restricts us to only melee abilities BB_THETHING_LASTAOE = null, // Last AOE ability key executed - BB_AGGRO_RANGE = 16, - BB_AGGRO_GRAB_RANGE = 6, ) ai_movement = /datum/ai_movement/basic_avoidance // dont need anything better because the arena is a square lol - idle_behavior = null - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/thing_boss_aoe, - /datum/ai_planning_subtree/thing_boss_melee, - ) - -/datum/ai_planning_subtree/thing_boss_aoe/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) - var/mob/living/pawn = controller.pawn - if(HAS_TRAIT_FROM(pawn, TRAIT_IMMOBILIZED, MEGAFAUNA_TRAIT) || (controller.blackboard[BB_THETHING_ATTACKMODE] || controller.blackboard[BB_THETHING_NOAOE])) - return - // our target - var/mob/living/shaft_miner = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(shaft_miner) || shaft_miner.stat == DEAD) //Dont use abilities on off z level targets, or dead shaft miners. We want to melee those. - return - - controller.set_blackboard_key(BB_THETHING_ATTACKMODE, TRUE) // putting this here so we go to melee mode if we cant do any aoe - var/static/list/aoe_attacks = list(BB_THETHING_DECIMATE, BB_THETHING_BIGTENDRILS, BB_THETHING_CARDTENDRILS, BB_THETHING_ACIDSPIT) - var/list/possible_attacks = aoe_attacks.Copy() - controller.blackboard[BB_THETHING_LASTAOE] - for(var/bb_action_key in possible_attacks) - var/datum/action/action = controller.blackboard[bb_action_key] - if(!action?.IsAvailable()) - possible_attacks -= bb_action_key - if(!length(possible_attacks)) - return - var/current_aoe_key = pick(possible_attacks) - controller.set_blackboard_key(BB_THETHING_LASTAOE, current_aoe_key) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability, current_aoe_key, BB_BASIC_MOB_CURRENT_TARGET) - if(prob(60) && shaft_miner.body_position != LYING_DOWN) //potential follow-up - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability, BB_THETHING_CHARGE, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - -/datum/ai_planning_subtree/thing_boss_melee/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick) - var/mob/living/pawn = controller.pawn - if(HAS_TRAIT_FROM(pawn, TRAIT_IMMOBILIZED, MEGAFAUNA_TRAIT)) - return - - // our target - var/mob/living/shaft_miner = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(shaft_miner)) - return - var/target_dist = get_dist(pawn, shaft_miner) - - var/datum/action/shriek = controller.blackboard[BB_THETHING_SHRIEK] - var/datum/action/charge = controller.blackboard[BB_THETHING_CHARGE] - if(isnull(shriek) || isnull(charge)) - return // pray this never occurs - - controller.set_blackboard_key(BB_THETHING_ATTACKMODE, FALSE) - if(shriek.IsAvailable() && target_dist <= 2 && shaft_miner.stat != DEAD) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/min_range/short, BB_THETHING_SHRIEK, BB_BASIC_MOB_CURRENT_TARGET) - return - else if(charge.IsAvailable() && target_dist >= 5) // While we cant hit prone targets, this helps to close the distance. - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability, BB_THETHING_CHARGE, BB_BASIC_MOB_CURRENT_TARGET) - return +/datum/bt_node/subtree/thing_aoe + behavior_tree_json = "code/modules/mob/living/basic/boss/thing/thing_aoe.bt.json" - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) +/datum/bt_node/subtree/thing_melee + behavior_tree_json = "code/modules/mob/living/basic/boss/thing/thing_melee.bt.json" diff --git a/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.json b/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.json new file mode 100644 index 000000000000..43fdef79c1a3 --- /dev/null +++ b/code/modules/mob/living/basic/boss/thing/thing_aoe.bt.json @@ -0,0 +1,72 @@ +{ + "dm_type": "/datum/bt_node/subtree/thing_aoe", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_THETHING_NOAOE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_THETHING_MELEEMODE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_has_trait_from", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": true, + "trait": "TRAIT_IMMOBILIZED", + "source": "MEGAFAUNA_TRAIT" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_THETHING_MELEEMODE", + "value": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_random_ability", + "vars": { + "ability_keys": "list(BB_THETHING_DECIMATE, BB_THETHING_BIGTENDRILS, BB_THETHING_CARDTENDRILS, BB_THETHING_ACIDSPIT)", + "last_used_key": "BB_THETHING_LASTAOE", + "result_key": "BB_GENERIC_ACTION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_GENERIC_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.6 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_THETHING_CHARGE", + "target_key": "BB_CURRENT_TARGET" + } + } + } + ] + } + } + } +} diff --git a/code/modules/mob/living/basic/boss/thing/thing_boss.bt.json b/code/modules/mob/living/basic/boss/thing/thing_boss.bt.json new file mode 100644 index 000000000000..e681d7518513 --- /dev/null +++ b/code/modules/mob/living/basic/boss/thing/thing_boss.bt.json @@ -0,0 +1,70 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/thing_boss", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/thing_aoe" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/thing_melee" + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait", + "vars": { + "duration": 0 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "vision_range": 6 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/boss/thing/thing_melee.bt.json b/code/modules/mob/living/basic/boss/thing/thing_melee.bt.json new file mode 100644 index 000000000000..96abd197f262 --- /dev/null +++ b/code/modules/mob/living/basic/boss/thing/thing_melee.bt.json @@ -0,0 +1,89 @@ +{ + "dm_type": "/datum/bt_node/subtree/thing_melee", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/true_for_time", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "duration": "5 SECONDS" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_THETHING_MELEEMODE", + "value": false + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_THETHING_SHRIEK", + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 2 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "min_distance": 3 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_THETHING_CHARGE", + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_has_trait_from", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": true, + "trait": "TRAIT_IMMOBILIZED", + "source": "MEGAFAUNA_TRAIT" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm index f7962d9b8687..938ea1f8fa3d 100644 --- a/code/modules/mob/living/basic/bots/_bots.dm +++ b/code/modules/mob/living/basic/bots/_bots.dm @@ -114,7 +114,7 @@ GLOBAL_LIST_INIT(command_strings, list( /mob/living/basic/bot/Initialize(mapload) . = ..() - add_traits(list(TRAIT_SILICON_ACCESS, TRAIT_REAGENT_SCANNER, TRAIT_UNOBSERVANT), INNATE_TRAIT) + add_traits(list(TRAIT_REAGENT_SCANNER, TRAIT_UNOBSERVANT), INNATE_TRAIT) AddElement(/datum/element/ai_retaliate) RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(handle_loop_movement)) RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(after_attacked)) @@ -441,30 +441,33 @@ GLOBAL_LIST_INIT(command_strings, list( heal_overall_damage(10) user.visible_message(span_notice("[user] repairs [src]!"),span_notice("You repair [src].")) -/mob/living/basic/bot/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) - if(attacking_item.GetID()) +/mob/living/basic/bot/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(tool.GetID()) unlock_with_id(user) - return + return ITEM_INTERACT_SUCCESS - if(istype(attacking_item, /obj/item/pai_card)) - insertpai(user, attacking_item) - return + if(istype(tool, /obj/item/pai_card)) + insertpai(user, tool) + return ITEM_INTERACT_SUCCESS - if(attacking_item.tool_behaviour != TOOL_HEMOSTAT || !paicard) + if(tool.tool_behaviour != TOOL_HEMOSTAT || !paicard) return ..() if(bot_access_flags & BOT_COVER_MAINTS_OPEN) balloon_alert(user, "open the access panel!") - return + return ITEM_INTERACT_BLOCKING balloon_alert(user, "removing pAI...") if(!do_after(user, 3 SECONDS, target = src) || !paicard) - return + return ITEM_INTERACT_BLOCKING - user.visible_message(span_notice("[user] uses [attacking_item] to pull [paicard] out of [initial(src.name)]!"), \ - span_notice("You pull [paicard] out of [initial(src.name)] with [attacking_item].")) + user.visible_message( + span_notice("[user] uses [tool] to pull [paicard] out of [initial(src.name)]!"), + span_notice("You pull [paicard] out of [initial(src.name)] with [tool]."), + ) ejectpai(user) + return ITEM_INTERACT_SUCCESS /mob/living/basic/bot/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker) if(damage_done > 0 && attacking_item.damtype != STAMINA && stat != DEAD) @@ -788,6 +791,7 @@ GLOBAL_LIST_INIT(command_strings, list( /mob/living/basic/bot/rust_heretic_act() adjust_brute_loss(400) + return TRUE /mob/living/basic/bot/proc/retrieve_access(mob/bot, list/player_access) SIGNAL_HANDLER diff --git a/code/modules/mob/living/basic/bots/bot.bt.json b/code/modules/mob/living/basic/bots/bot.bt.json new file mode 100644 index 000000000000..b1cc876173b4 --- /dev/null +++ b/code/modules/mob/living/basic/bots/bot.bt.json @@ -0,0 +1,22 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] +} diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index a5f8202e66e7..42ff9f2715d0 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -1,6 +1,5 @@ -#define BOT_NO_BEACON_PATH_PENALTY 30 SECONDS - /datum/ai_controller/basic_controller/bot + behavior_tree_json = "code/modules/mob/living/basic/bots/bot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_SALUTE_MESSAGES = list( @@ -11,24 +10,16 @@ ) ai_movement = /datum/ai_movement/jps/bot - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon, - ) max_target_distance = AI_BOT_PATH_LENGTH - can_idle = FALSE - ///minimum distance we need to be from our target in path calculations - var/minimum_distance = 0 - ///keys to be reset when the bot is reseted + ai_traits = DEFAULT_AI_FLAGS | RUN_WHILE_UNWATCHED + ///keys to be reset when the bot is reset var/list/reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, ) -/datum/targeting_strategy/basic/bot/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/bot/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) var/datum/ai_controller/basic_controller/bot/my_controller = living_mob.ai_controller if(isnull(my_controller)) return FALSE @@ -39,9 +30,8 @@ return FALSE if(get_turf(living_mob) == get_turf(living_target)) return ..() - var/list/path = get_path_to(living_mob, living_target, mintargetdist = my_controller.minimum_distance, max_distance = 10, access = my_controller.get_access()) - if(!length(path) || QDELETED(living_mob)) - my_controller?.add_to_blacklist(living_target) + if(!my_controller.can_reach_target(living_target, distance = 10)) + my_controller.add_to_blacklist(living_target) return FALSE return ..() @@ -56,7 +46,7 @@ /datum/ai_controller/basic_controller/bot/proc/on_movement_start(mob/living/basic/bot/source, atom/target) SIGNAL_HANDLER - if(current_movement_target == blackboard[BB_BEACON_TARGET]) + if(target == blackboard[BB_BEACON_TARGET]) source.update_bot_mode(new_mode = BOT_PATROL) return @@ -69,6 +59,10 @@ set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) addtimer(CALLBACK(src, PROC_REF(remove_from_blacklist), target), final_duration) +/// Unreachable targets get added to the temporary ignore list so we stop pathing to them. Subtypes override to skip blacklisting in stationary mode. +/datum/ai_controller/basic_controller/bot/note_unreachable_target(atom/target) + add_to_blacklist(target) + /datum/ai_controller/basic_controller/bot/proc/remove_from_blacklist(atom/target) if(QDELETED(target)) return @@ -100,15 +94,15 @@ /datum/ai_controller/basic_controller/bot/proc/reset_bot() SIGNAL_HANDLER - CancelActions() + cancel_current_plan() if(!length(reset_keys)) return for(var/key in reset_keys) clear_blackboard_key(key) ///set the target if we can reach them -/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, duration, distance = 10, bypass_add_to_blacklist = FALSE) - if(can_reach_target(target, distance)) +/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, duration, distance = 10, bypass_add_to_blacklist = FALSE, minimum_distance = 0) + if(can_reach_target(target, distance, minimum_distance)) EVLOG_MAPTEXT(src, EVLOG_CATEGORY_AI_TARGETING, "[pawn] has selected [target] as a target for blackboard key [key]!", get_turf(target), "Target: [target]") EVLOG_LINES(src, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(pawn), get_turf(target)) set_blackboard_key(key, target) @@ -120,252 +114,3 @@ EVLOG_LINES(src, EVLOG_CATEGORY_AI_TARGETING, "Line to target", get_turf(pawn), get_turf(target)) add_to_blacklist(target, final_duration) return FALSE - -/datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10) - if(!isdatum(target)) //we dont need to check if its not a datum! - return TRUE - if(get_turf(pawn) == get_turf(target)) - return TRUE - var/list/path = get_path_to(pawn, target, simulated_only = !HAS_TRAIT(pawn, TRAIT_SPACEWALK), mintargetdist = minimum_distance, max_distance = distance, access = get_access()) - return (!!length(path)) - -/datum/ai_planning_subtree/find_patrol_beacon - ///travel towards beacon behavior - var/travel_behavior = /datum/ai_behavior/travel_towards/beacon - -/datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - - if(controller.blackboard[BB_BOT_BEACON_COOLDOWN] > world.time) - return - - if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON) - return - - if(controller.blackboard_key_exists(BB_BEACON_TARGET)) - controller.queue_behavior(travel_behavior, BB_BEACON_TARGET) - return - - if(controller.blackboard_key_exists(BB_PREVIOUS_BEACON_TARGET)) - controller.queue_behavior(/datum/ai_behavior/find_next_beacon_target, BB_BEACON_TARGET) - return - - controller.queue_behavior(/datum/ai_behavior/find_first_beacon_target, BB_BEACON_TARGET) - -/datum/ai_behavior/find_first_beacon_target - -/datum/ai_behavior/find_first_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/closest_distance = INFINITY - var/mob/living/basic/bot/bot_pawn = controller.pawn - var/atom/final_target - var/atom/previous_target = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] - for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) - var/dist = get_dist(bot_pawn, beacon) - if(beacon == previous_target || dist <= 1) - continue - if(dist > closest_distance) - continue - closest_distance = dist - final_target = beacon - - if(isnull(final_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(BB_BEACON_TARGET, final_target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_next_beacon_target - action_cooldown = 5 SECONDS - -/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) - var/mob/living/basic/bot/bot_pawn = controller.pawn - var/atom/final_target - var/obj/machinery/navbeacon/prev_beacon = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] - if(QDELETED(prev_beacon)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) - if(beacon.location == prev_beacon.codes[NAVBEACON_PATROL_NEXT]) - final_target = beacon - break - - if(isnull(final_target)) - controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) //failed to find the next beacon, search for a first beacon again - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, final_target) - controller.clear_blackboard_key(BB_BEACON_TARGET) - - if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], final_target) || get_dist(bot_pawn, final_target) > controller.max_target_distance) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(controller.set_if_can_reach(key = BB_BEACON_TARGET, target = final_target, duration = 3 MINUTES, distance = controller.max_target_distance)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - -/datum/ai_behavior/travel_towards/beacon - clear_target = TRUE - new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon - -/datum/ai_behavior/travel_towards/beacon/setup(datum/ai_controller/controller, target_key) - var/atom/target_beacon = controller.blackboard[target_key] - if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target_beacon)) - return FALSE - return ..() - -/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key) - var/atom/target = controller.blackboard[target_key] - if(!succeeded) - controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY) - controller.add_to_blacklist(target, 3 MINUTES) - controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, target) - return ..() - -/datum/ai_planning_subtree/respond_to_summon - -/datum/ai_planning_subtree/respond_to_summon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_BOT_SUMMON_TARGET)) - return - controller.queue_behavior(/datum/ai_behavior/travel_towards/bot_summon, BB_BOT_SUMMON_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/travel_towards/bot_summon - clear_target = TRUE - new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon - -/datum/ai_behavior/travel_towards/bot_summon/finish_action(datum/ai_controller/controller, succeeded, target_key) - var/mob/living/basic/bot/bot_pawn = controller.pawn - if(QDELETED(bot_pawn)) // pawn can be null at this point - return ..() - bot_pawn.calling_ai_ref = null - bot_pawn.update_bot_mode(new_mode = BOT_IDLE) - return ..() - -/datum/ai_planning_subtree/salute_authority - -/datum/ai_planning_subtree/salute_authority/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - //we are criminals, dont salute the dirty pigs - if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) - return - if(controller.blackboard_key_exists(BB_SALUTE_TARGET)) - controller.queue_behavior(/datum/ai_behavior/salute_authority, BB_SALUTE_TARGET, BB_SALUTE_MESSAGES) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_authority, BB_SALUTE_TARGET) - - -/datum/ai_behavior/find_and_set/valid_authority - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = BOT_COMMISSIONED_SALUTE_DELAY - -/datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/mob/living/nearby_mob in oview(search_range, controller.pawn)) - if(!HAS_TRAIT(nearby_mob, TRAIT_COMMISSIONED)) - continue - return nearby_mob - return null - -/datum/ai_behavior/salute_authority - -/datum/ai_behavior/salute_authority/perform(seconds_per_tick, datum/ai_controller/controller, target_key, salute_keys) - if(!controller.blackboard_key_exists(target_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/list/salute_list = controller.blackboard[salute_keys] - if(!length(salute_list)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/basic/bot/bot_pawn = controller.pawn - //special interaction if we are wearing a fedora - var/obj/item/our_hat = (locate(/obj/item/clothing/head) in bot_pawn) - if(our_hat) - salute_list += "tips [our_hat] at " - - bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]!") - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/bot_search - action_cooldown = 2 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/bot_search/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, looking_for, radius = 5, pathing_distance = 10, bypass_add_blacklist = FALSE, turf_search = FALSE) - if(!istype(controller)) - stack_trace("attempted to give [controller.pawn] the bot search behavior!") - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/living_pawn = controller.pawn - var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - var/list/objects_to_search = turf_search ? RANGE_TURFS(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance - for(var/atom/potential_target as anything in objects_to_search) - if(QDELETED(living_pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(!is_type_in_typecache(potential_target, looking_for)) - continue - if(LAZYACCESS(ignore_list, potential_target)) - continue - if(!valid_target(controller, potential_target)) - continue - if(!can_see(controller.pawn, potential_target, radius)) - continue - if(controller.set_if_can_reach(key = target_key, target = potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/bot_search/proc/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) - return TRUE - -///behavior to make our bot talk -/datum/ai_behavior/bot_speech - action_cooldown = 5 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/bot_speech/perform(seconds_per_tick, datum/ai_controller/controller, list/list_to_pick_from, announce_key) - var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[announce_key] - - if(isnull(announcement) || !length(list_to_pick_from)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - announcement.announce(pick(list_to_pick_from)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -///behavior to interact with atoms -/datum/ai_behavior/bot_interact - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - ///should we remove the target afterwards? - var/clear_target = TRUE - -/datum/ai_behavior/bot_interact/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/bot_interact/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/basic/living_pawn = controller.pawn - var/atom/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - living_pawn.UnarmedAttack(target, proximity_flag = TRUE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(clear_target) - controller.clear_blackboard_key(target_key) - if(!succeeded && !isnull(target)) - controller.add_to_blacklist(target) - -/datum/ai_behavior/bot_interact/keep_target - clear_target = FALSE - - -#undef BOT_NO_BEACON_PATH_PENALTY diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm index 602b2cc54bdd..1e9bda4dc3f8 100644 --- a/code/modules/mob/living/basic/bots/bot_hud.dm +++ b/code/modules/mob/living/basic/bots/bot_hud.dm @@ -6,7 +6,7 @@ set_hud_image_state(DIAG_STAT_HUD, "hudstat") return - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) set_hud_image_state(DIAG_STAT_HUD, "hudoffline") return @@ -32,7 +32,7 @@ set_hud_image_state(DIAG_BOT_HUD, "") ///proc that handles drawing and transforming the bot's path onto diagnostic huds -/mob/living/basic/bot/proc/generate_bot_path(datum/move_loop/has_target/jps/source) +/mob/living/basic/bot/proc/generate_bot_path(datum/move_loop/has_target/jps/source, list/path) SIGNAL_HANDLER UnregisterSignal(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING) @@ -40,8 +40,11 @@ if(isnull(ai_controller)) return + if(!length(path)) + return + - var/atom/move_target = ai_controller.current_movement_target + var/atom/move_target = path[path.len] if(move_target != ai_controller.blackboard[BB_BEACON_TARGET]) return @@ -115,4 +118,3 @@ var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC], GLOB.huds[DATA_HUD_BOT_PATH]) for(var/datum/atom_hud/hud as anything in path_huds_watching_me) hud.remove_atom_from_hud(src) - diff --git a/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.json b/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.json new file mode 100644 index 000000000000..d3d867cc11fc --- /dev/null +++ b/code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.json @@ -0,0 +1,28 @@ +{ + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_PET_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/execute_clean", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.json b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.json new file mode 100644 index 000000000000..b8143057dcaf --- /dev/null +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.json @@ -0,0 +1,251 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/cleanbot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/override_id_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_is_emagged", + "vars": { + "invert": true + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/execute_clean", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_FRIENDLY_JANITOR" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FRIENDLY_JANITOR", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_FRIENDLY_JANITOR", + "befriend_message": "BB_FRIENDLY_MESSAGE" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FRIENDLY_JANITOR", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FRIENDLY_JANITOR", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/capable_human/cleanbot_whisperer", + "vision_range": 5, + "time_between_perform": "30 SECONDS" + } + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "cooldown_key": "BB_POST_CLEAN_COOLDOWN" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "time_between_perform": "3 SECONDS", + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/cleanbot_cleanables", + "vision_range": 5, + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "reach_distance": 15 + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_is_emagged", + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_ACID_SPRAY_COOLDOWN", + "cooldown_duration": 30 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_ACID_SPRAY_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ACID_SPRAY_TARGET", + "required_dist": 0, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/execute_clean", + "vars": { + "target_key": "BB_ACID_SPRAY_TARGET" + } + } + ] + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_CLEANBOT_FOAM" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_POST_CLEAN_COOLDOWN" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_ACID_SPRAY_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/capable_human", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "vision_range": 5, + "time_between_perform": "30 SECONDS" + } + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm index 24ec3d55e5ed..78997997e6ef 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm @@ -186,7 +186,7 @@ /mob/living/basic/bot/cleanbot/examine(mob/user) . = ..() - if(ascended && user.stat == CONSCIOUS && user.client) + if(ascended && !IS_UNCONSCIOUS_OR_CRIT(user) && user.client) user.client.give_award(/datum/award/achievement/misc/cleanboss, user) if(isnull(weapon)) return @@ -349,3 +349,13 @@ name = "Scrubs, MD" req_one_access = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL) bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED) + additional_access = /datum/id_trim/job/janitor/medical_cleanbot + +/datum/id_trim/job/janitor/medical_cleanbot + minimal_access = list( + ACCESS_JANITOR, + ACCESS_MAINT_TUNNELS, + ACCESS_MINERAL_STOREROOM, + ACCESS_SERVICE, + ACCESS_MEDICAL, + ) diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm index 25029c5bc55a..787e7c78d005 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -2,6 +2,7 @@ #define POST_CLEAN_COOLDOWN 5 SECONDS /datum/ai_controller/basic_controller/bot/cleanbot + behavior_tree_json = "code/modules/mob/living/basic/bots/cleanbot/cleanbot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -13,20 +14,9 @@ ), BB_FRIENDLY_MESSAGE = "empathetically acknowledges your hardwork and tough circumstances", ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/pet_planning/cleanbot, - /datum/ai_planning_subtree/cleaning_subtree, - /datum/ai_planning_subtree/befriend_janitors, - /datum/ai_planning_subtree/acid_spray, - /datum/ai_planning_subtree/use_mob_ability/foam_area, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon/cleanbot, - ) reset_keys = list( BB_ACTIVE_PET_COMMAND, - BB_CLEAN_TARGET, + BB_CURRENT_TARGET, BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, @@ -39,99 +29,40 @@ BB_HUNTABLE_TRASH = CLEANBOT_CLEAN_TRASH, ) -/datum/ai_planning_subtree/pet_planning/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - //we are DONE listening to orders - if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) - return - return ..() - - -/datum/ai_planning_subtree/cleaning_subtree -/datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_CLEAN_TARGET)) - controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING +/// Gathers nearby cleanable atoms: decals plus whatever types the cleanbot's currently enabled janitor mode flags allow. +/datum/target_source/cleanbot_cleanables +/datum/target_source/cleanbot_cleanables/collect_candidates(mob/living/pawn, datum/ai_controller/basic_controller/bot/cleanbot/controller, range) var/list/final_hunt_list = list() - final_hunt_list += controller.blackboard[BB_CLEANABLE_DECALS] - var/list/flag_list = controller.clean_flags - var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn - for(var/list_key in flag_list) - if(!(bot_pawn.janitor_mode_flags & flag_list[list_key])) + var/mob/living/basic/bot/cleanbot/bot_pawn = pawn + for(var/list_key in controller.clean_flags) + if(!(bot_pawn.janitor_mode_flags & controller.clean_flags[list_key])) continue final_hunt_list += controller.blackboard[list_key] + if(!length(final_hunt_list)) + return list() + var/list/type_filter = typecacheof(final_hunt_list) + return typecache_filter_list(oview(range, pawn), type_filter) - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets, BB_CLEAN_TARGET, final_hunt_list) - -/datum/ai_behavior/find_and_set/in_list/clean_targets - action_cooldown = 3 SECONDS - -/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/basic_controller/bot/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) - var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - for(var/atom/found_item in found) - if(QDELETED(controller.pawn)) - break - if(LAZYACCESS(ignore_list, found_item)) - continue - if(get_turf(found_item) == get_turf(controller.pawn)) - return found_item - var/list/path = get_path_to(controller.pawn, found_item, max_distance = BOT_CLEAN_PATH_LIMIT, access = controller.get_access()) - if(!length(path)) - controller.add_to_blacklist(found_item) - continue - return found_item - -/datum/ai_planning_subtree/acid_spray - -/datum/ai_planning_subtree/acid_spray/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn - if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) - return - if(controller.blackboard_key_exists(BB_ACID_SPRAY_TARGET)) - controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_ACID_SPRAY_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/spray_target, BB_ACID_SPRAY_TARGET, /mob/living/carbon/human, 5) - -/datum/ai_behavior/find_and_set/spray_target - action_cooldown = 30 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_and_set/spray_target/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn)) - if(LAZYACCESS(ignore_list, human_target)) - continue - if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) - continue - return human_target - return null - -/datum/ai_behavior/execute_clean - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/execute_clean/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) +///clean that shit bro fr fr 67 +/datum/bt_node/ai_behavior/execute_clean + var/target_key -/datum/ai_behavior/execute_clean/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/execute_clean/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/living_pawn = controller.pawn var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[living_pawn] execute_clean: target deleted") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - + if(get_dist(living_pawn, target) > 1) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[living_pawn] cleaning [target]", get_turf(target), "Cleaning") living_pawn.UnarmedAttack(target, proximity_flag = TRUE) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) +/datum/bt_node/ai_behavior/execute_clean/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) . = ..() controller.set_blackboard_key(BB_POST_CLEAN_COOLDOWN, POST_CLEAN_COOLDOWN + world.time) var/atom/target = controller.blackboard[target_key] @@ -147,56 +78,25 @@ var/list/speech_list = controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES] if(length(speech_list)) var/mob/living/living_pawn = controller.pawn - if(!QDELETED(living_pawn)) // pawn can be null at this point - living_pawn.say(pick(speech_list), forced = "ai controller") + if(!QDELETED(living_pawn)) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), pick(speech_list), forced = "ai controller") controller.clear_blackboard_key(target_key) -/datum/ai_planning_subtree/use_mob_ability/foam_area - ability_key = BB_CLEANBOT_FOAM - finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/foam_area/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) - return - return ..() - -/datum/ai_planning_subtree/befriend_janitors -/datum/ai_planning_subtree/befriend_janitors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - //we are now evil. dont befriend the janitors - if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) - return - if(controller.blackboard_key_exists(BB_FRIENDLY_JANITOR)) - controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_FRIENDLY_JANITOR, BB_FRIENDLY_MESSAGE) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/friendly_janitor, BB_FRIENDLY_JANITOR, /mob/living/carbon/human, 5) - -/datum/ai_behavior/find_and_set/friendly_janitor - action_cooldown = 30 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_and_set/friendly_janitor/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - for(var/mob/living/carbon/human/human_target in oview(search_range, living_pawn)) - if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) - continue - if(!HAS_TRAIT(human_target, TRAIT_CLEANBOT_WHISPERER)) - continue - if(living_pawn.has_ally(REF(human_target))) - continue - return human_target - return null +/// Valid if the target is a conscious human janitor-whisperer the cleanbot hasn't already befriended. +/datum/targeting_strategy/capable_human/cleanbot_whisperer/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!HAS_TRAIT(target, TRAIT_CLEANBOT_WHISPERER)) + return FALSE + return !living_mob.has_ally(REF(target)) -/datum/ai_planning_subtree/find_patrol_beacon/cleanbot -/datum/ai_planning_subtree/find_patrol_beacon/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard[BB_POST_CLEAN_COOLDOWN] >= world.time) - return - return ..() +/datum/bt_node/subtree/clean_pet_target + behavior_tree_json = "code/modules/mob/living/basic/bots/cleanbot/clean_pet_target.bt.json" +///Tells the cleanbot to go clean a target /datum/pet_command/clean command_name = "Clean" command_desc = "Command a cleanbot to clean the mess." @@ -215,11 +115,11 @@ return ..() /datum/pet_command/clean/execute_action(datum/ai_controller/basic_controller/bot/controller) - if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) - controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + var/atom/target = controller.blackboard[BB_CURRENT_PET_TARGET] + if(QDELETED(target)) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + return + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/clean_pet_target) #undef BOT_CLEAN_PATH_LIMIT #undef POST_CLEAN_COOLDOWN diff --git a/code/modules/mob/living/basic/bots/dedbot.bt.json b/code/modules/mob/living/basic/bots/dedbot.bt.json new file mode 100644 index 000000000000..04c05cdf64ed --- /dev/null +++ b/code/modules/mob/living/basic/bots/dedbot.bt.json @@ -0,0 +1,73 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/dedbot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/melee", + "vars": { + "ability_key": "BB_DEDBOT_SLASH", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/dedbot.dm b/code/modules/mob/living/basic/bots/dedbot.dm index d55570edbdcd..c81baec82c92 100644 --- a/code/modules/mob/living/basic/bots/dedbot.dm +++ b/code/modules/mob/living/basic/bots/dedbot.dm @@ -49,31 +49,20 @@ grant_actions_by_list(innate_actions) /datum/ai_controller/basic_controller/bot/dedbot + behavior_tree_json = "code/modules/mob/living/basic/bots/dedbot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = DEAD, BB_AGGRO_RANGE = 2, ) ai_movement = /datum/ai_movement/jps/bot - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/exenterate, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/find_patrol_beacon, - ) max_target_distance = AI_BOT_PATH_LENGTH - ///keys to be reset when the bot is reseted reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, ) -/datum/ai_planning_subtree/targeted_mob_ability/exenterate - ability_key = BB_DEDBOT_SLASH - finish_planning = FALSE - /datum/action/cooldown/mob_cooldown/exenterate name = "Exenterate" desc = "Disembowel every living thing in range with your blades." diff --git a/code/modules/mob/living/basic/bots/ed209/ed209.bt.json b/code/modules/mob/living/basic/bots/ed209/ed209.bt.json new file mode 100644 index 000000000000..b1fd91ac2cd0 --- /dev/null +++ b/code/modules/mob/living/basic/bots/ed209/ed209.bt.json @@ -0,0 +1,132 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/ed209", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": false, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": false, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_target_stunned", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "max_range": 9 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "approach_movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/ed209/ed209_ai.dm b/code/modules/mob/living/basic/bots/ed209/ed209_ai.dm index d699a153bb25..4305104d5fb5 100644 --- a/code/modules/mob/living/basic/bots/ed209/ed209_ai.dm +++ b/code/modules/mob/living/basic/bots/ed209/ed209_ai.dm @@ -2,18 +2,13 @@ #define SPECIAL_LINES "special_lines" /datum/ai_controller/basic_controller/bot/ed209 + behavior_tree_json = "code/modules/mob/living/basic/bots/ed209/ed209.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/secbot, BB_UNREACHABLE_LIST_COOLDOWN = 1 MINUTES, BB_ALWAYS_IGNORE_FACTION = TRUE, - ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/ranged_skirmish, - /datum/ai_planning_subtree/arrest_target/ed209, - /datum/ai_planning_subtree/find_patrol_beacon, + BB_RANGED_SKIRMISH_MIN_DISTANCE = 2, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 3 ) reset_keys = list( BB_BEACON_TARGET, @@ -26,7 +21,7 @@ . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) return - RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(on_target_set)) + RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET), PROC_REF(on_target_set)) /datum/ai_controller/basic_controller/bot/ed209/proc/on_target_set() @@ -48,18 +43,5 @@ var/list/final_list = my_bot.sheriffized ? lines_to_pick[SPECIAL_LINES] : lines_to_pick[DEFAULT_LINES] INVOKE_ASYNC(announcement, TYPE_PROC_REF(/datum/action/cooldown/bot_announcement, announce), pick(final_list)) -/datum/ai_planning_subtree/arrest_target/ed209 - arrest_behavior = /datum/ai_behavior/basic_melee_attack/interact_once/bot/ed209 - - -/datum/ai_behavior/basic_melee_attack/interact_once/bot/ed209 - action_cooldown = 0.5 SECONDS - -/datum/ai_behavior/basic_melee_attack/interact_once/bot/ed209/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - if(!(. & AI_BEHAVIOR_DELAY)) - return AI_BEHAVIOR_DELAY //this kinda sucks but we have to do this cause we need to shoot while moving to stun - - #undef DEFAULT_LINES #undef SPECIAL_LINES diff --git a/code/modules/mob/living/basic/bots/ed209/ed209_nukie_ai.dm b/code/modules/mob/living/basic/bots/ed209/ed209_nukie_ai.dm index 24bf9a203670..3d5a6e283e17 100644 --- a/code/modules/mob/living/basic/bots/ed209/ed209_nukie_ai.dm +++ b/code/modules/mob/living/basic/bots/ed209/ed209_nukie_ai.dm @@ -2,13 +2,10 @@ blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_UNREACHABLE_LIST_COOLDOWN = 1 MINUTES, + BB_RANGED_SKIRMISH_MIN_DISTANCE = 2, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 3 ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree, - /datum/ai_planning_subtree/find_patrol_beacon, - ) + behavior_tree_json = "code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.json" reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, diff --git a/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.json b/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.json new file mode 100644 index 000000000000..469fba535420 --- /dev/null +++ b/code/modules/mob/living/basic/bots/ed209/ed209_syndicate.bt.json @@ -0,0 +1,21 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/ed209/syndicate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ranged_combat", + "bindings": { + "bsjjwub2": "/datum/bt_node/subtree/bot_patrol" + } + } + ] +} diff --git a/code/modules/mob/living/basic/bots/firebot/firebot.bt.json b/code/modules/mob/living/basic/bots/firebot/firebot.bt.json new file mode 100644 index 000000000000..08156b1ddd8a --- /dev/null +++ b/code/modules/mob/living/basic/bots/firebot/firebot.bt.json @@ -0,0 +1,119 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/firebot", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/announce_fire_detected" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_interact/extinguish", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "target_source": "/datum/target_source/firebot_targets", + "targeting_strategy": "/datum/targeting_strategy/extinguishable_person", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "vision_range": 5 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "target_source": "/datum/target_source/range_turfs/firebot_hotspots", + "targeting_strategy": "/datum/targeting_strategy/burning_hotspot", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "vision_range": 5 + } + } + ] + } + ] + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/handle_firebot_speech" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm index b40f5da5abd4..8e44c73c5484 100644 --- a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm +++ b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm @@ -5,17 +5,9 @@ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_turfs, BB_UNREACHABLE_LIST_COOLDOWN = 3 MINUTES, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/extinguishing_people, - /datum/ai_planning_subtree/extinguishing_turfs, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/firebot_speech, - /datum/ai_planning_subtree/find_patrol_beacon, - ) + behavior_tree_json = "code/modules/mob/living/basic/bots/firebot/firebot.bt.json" reset_keys = list( - BB_FIREBOT_EXTINGUISH_TARGET, + BB_CURRENT_TARGET, BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, @@ -27,106 +19,106 @@ . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) return - RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_FIREBOT_EXTINGUISH_TARGET), PROC_REF(on_target_found)) -///say a silly line whenever we find someone on fire -/datum/ai_controller/basic_controller/bot/firebot/proc/on_target_found() - SIGNAL_HANDLER - if(!COOLDOWN_FINISHED(src, announcement_cooldown)) - return - var/datum/action/cooldown/bot_announcement/announcement = blackboard[BB_ANNOUNCE_ABILITY] - if(isnull(announcement)) - return - var/list/lines = blackboard[BB_FIREBOT_FIRE_DETECTED_LINES] +/datum/bt_node/ai_behavior/announce_fire_detected + +/datum/bt_node/ai_behavior/announce_fire_detected/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/firebot/controller) + if(!COOLDOWN_FINISHED(controller, announcement_cooldown)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + if(isnull(announcement)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + var/list/lines = controller.blackboard[BB_FIREBOT_FIRE_DETECTED_LINES] if(!length(lines)) - return + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED INVOKE_ASYNC(announcement, TYPE_PROC_REF(/datum/action/cooldown/bot_announcement, announce), pick(lines)) - COOLDOWN_START(src, announcement_cooldown, ANNOUNCEMENT_TIMER) - - -///subtree for extinguishing people -/datum/ai_planning_subtree/extinguishing_people + COOLDOWN_START(controller, announcement_cooldown, ANNOUNCEMENT_TIMER) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED -/datum/ai_planning_subtree/extinguishing_people/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_FIREBOT_EXTINGUISH_TARGET)) - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once/extinguish, BB_FIREBOT_EXTINGUISH_TARGET, BB_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING - var/mob/living/basic/bot/firebot/living_bot = controller.pawn - var/range = living_bot.firebot_mode_flags & FIREBOT_STATIONARY_MODE ? 1 : 5 - - if(living_bot.firebot_mode_flags & FIREBOT_EXTINGUISH_PEOPLE) - controller.queue_behavior(/datum/ai_behavior/bot_search/people_on_fire, BB_FIREBOT_EXTINGUISH_TARGET, controller.blackboard[BB_FIREBOT_CAN_EXTINGUISH], range) +/// Firebot skips blacklisting unreachable targets while stationary, matching the old set_if_can_reach bypass. +/datum/ai_controller/basic_controller/bot/firebot/note_unreachable_target(atom/target) + var/mob/living/basic/bot/firebot/bot_pawn = pawn + if(bot_pawn.firebot_mode_flags & FIREBOT_STATIONARY_MODE) + return + return ..() -///behavior for finding people on fire -/datum/ai_behavior/bot_search/people_on_fire +/// Gathers nearby living mobs to extinguish; empty unless people-extinguishing is on, range clamped to adjacent tiles when stationary. +/datum/target_source/firebot_targets + +/datum/target_source/firebot_targets/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/mob/living/basic/bot/firebot/bot_pawn = pawn + if(!(bot_pawn.firebot_mode_flags & FIREBOT_EXTINGUISH_PEOPLE)) + return list() + if(bot_pawn.firebot_mode_flags & FIREBOT_STATIONARY_MODE) + range = 1 + var/list/candidates = list() + for(var/mob/living/candidate in oview(range, pawn)) + candidates += candidate + return candidates + +/// Valid if the mob is on fire (or anyone, while emagged) and is a type this firebot is allowed to extinguish. +/datum/targeting_strategy/extinguishable_person/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return FALSE + var/mob/living/basic/bot/firebot/bot_pawn = living_mob + if(!target_mob.on_fire && !(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + return FALSE + return is_type_in_list(target_mob, controller.blackboard[BB_FIREBOT_CAN_EXTINGUISH]) + + +/// Gathers turfs in range; empty unless flame-extinguishing is enabled. +/datum/target_source/range_turfs/firebot_hotspots + +/datum/target_source/range_turfs/firebot_hotspots/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/mob/living/basic/bot/firebot/bot_pawn = pawn + if(!(bot_pawn.firebot_mode_flags & FIREBOT_EXTINGUISH_FLAMES)) + return list() + return ..(pawn, controller, range) + +/// Valid if the turf is an open turf with an active fire. +/datum/targeting_strategy/burning_hotspot/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!isopenturf(target)) + return FALSE + var/turf/open/open_turf = target + return !!open_turf.active_hotspot -/datum/ai_behavior/bot_search/people_on_fire/valid_target(datum/ai_controller/basic_controller/bot/controller, mob/living/my_target) - var/mob/living/basic/bot/living_bot = controller.pawn - return (my_target.on_fire || (living_bot.bot_access_flags & BOT_COVER_EMAGGED)) -///subtree for finding turfs to extinguish -/datum/ai_planning_subtree/extinguishing_turfs -/datum/ai_planning_subtree/extinguishing_turfs/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_FIREBOT_EXTINGUISH_TARGET)) - return +/datum/bt_node/ai_behavior/bot_interact/extinguish - var/mob/living/basic/bot/firebot/living_bot = controller.pawn - var/should_bypass_blacklist = living_bot.firebot_mode_flags & FIREBOT_STATIONARY_MODE - - if(living_bot.firebot_mode_flags & FIREBOT_EXTINGUISH_FLAMES) - controller.queue_behavior(/datum/ai_behavior/search_burning_turfs, BB_FIREBOT_EXTINGUISH_TARGET, should_bypass_blacklist) - -///behavior to find burning turfs -/datum/ai_behavior/search_burning_turfs - action_cooldown = 2 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/search_burning_turfs/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, bypass_add_blacklist = FALSE) - var/mob/living/living_pawn = controller.pawn - var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - - for(var/turf/possible_turf as anything in RANGE_TURFS(5, living_pawn)) - if(QDELETED(living_pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(!isopenturf(possible_turf)) - continue - var/turf/open/open_turf = possible_turf - if(!open_turf.active_hotspot) - continue - if(LAZYACCESS(ignore_list, possible_turf)) - continue - if(controller.set_if_can_reach(key = target_key, target = possible_turf, bypass_add_to_blacklist = bypass_add_blacklist)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -///behavior to extinguish mobs or turfs -/datum/ai_behavior/basic_melee_attack/interact_once/extinguish - -/datum/ai_behavior/basic_melee_attack/interact_once/extinguish/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - var/atom/target = controller.blackboard[BB_FIREBOT_EXTINGUISH_TARGET] +/datum/bt_node/ai_behavior/bot_interact/extinguish/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) + . = ..() + // if we couldn't reach OR we emagged a living target, blacklist them + var/atom/target = controller.blackboard[target_key] var/mob/living/basic/bot/living_bot = controller.pawn - - //if we couldnt path, or we successfully burnt someone, ignore them for a bit! if(!succeeded || (isliving(target) && (living_bot.bot_access_flags & BOT_COVER_EMAGGED))) controller.add_to_blacklist(target) - return ..() -///subtree to make us say funny idle lines -/datum/ai_planning_subtree/firebot_speech - ///chance we spout lines + +/datum/bt_node/ai_behavior/handle_firebot_speech + time_between_perform = 20 SECONDS var/speech_prob = 3 -/datum/ai_planning_subtree/firebot_speech/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard[BB_FIREBOT_EXTINGUISH_TARGET] || !SPT_PROB(speech_prob, seconds_per_tick)) - return +/datum/bt_node/ai_behavior/handle_firebot_speech/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(speech_prob, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/mob/living/basic/bot/living_bot = controller.pawn var/list/idle_lines = (living_bot.bot_access_flags & BOT_COVER_EMAGGED) ? controller.blackboard[BB_FIREBOT_EMAGGED_LINES] : controller.blackboard[BB_FIREBOT_IDLE_LINES] - controller.queue_behavior(/datum/ai_behavior/bot_speech, idle_lines, BB_ANNOUNCE_ABILITY) + if(!length(idle_lines)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement?.announce(pick(idle_lines)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED #undef ANNOUNCEMENT_TIMER diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.json b/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.json new file mode 100644 index 000000000000..2e6d19f16c4e --- /dev/null +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot.bt.json @@ -0,0 +1,191 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/honkbot", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/honkbot_slip" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CLOWN_FRIEND" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/play_with_clown", + "vars": { + "target_key": "BB_CLOWN_FRIEND" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CLOWN_FRIEND", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SLIPPERY_TARGET", + "target_source": "/datum/target_source/honkbot_slippery", + "targeting_strategy": "/datum/targeting_strategy/can_see", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "vision_range": 5, + "time_between_perform": "5 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SLIP_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/can_see/slip_victim", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "vision_range": 5 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CLOWN_FRIEND", + "target_source": "/datum/target_source/oview_single_type/living_mob", + "targeting_strategy": "/datum/targeting_strategy/clown_friend", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "vision_range": 5, + "time_between_perform": "5 SECONDS" + } + } + ] + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] +} diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm index fbe91b7cc4de..a96ee6309ddc 100644 --- a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm @@ -55,7 +55,7 @@ return honkbot_sounds /mob/living/basic/bot/secbot/honkbot/proc/pre_slip() - return (prob(70) && ai_controller?.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return (prob(70) && ai_controller?.blackboard_key_exists(BB_CURRENT_TARGET)) /mob/living/basic/bot/secbot/honkbot/proc/post_slip() INVOKE_ASYNC(src, TYPE_PROC_REF(/mob/living/basic/bot, speak), HONKBOT_VOICED_HONK_SAD) diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm index 3a4d022087d9..a6b01c481a1b 100644 --- a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm @@ -4,16 +4,7 @@ BB_UNREACHABLE_LIST_COOLDOWN = 1 MINUTES, BB_ALWAYS_IGNORE_FACTION = TRUE, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/use_mob_ability/random_honk, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/arrest_target, - /datum/ai_planning_subtree/slip_victims, - /datum/ai_planning_subtree/play_with_clowns, - /datum/ai_planning_subtree/find_patrol_beacon, - ) + behavior_tree_json = "code/modules/mob/living/basic/bots/honkbots/honkbot.bt.json" reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, @@ -24,15 +15,10 @@ . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) return - RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_SLIP_TARGET), PROC_REF(on_clear_target)) + // on_clear_target intentionally removed it caused grab-then-immediately-release bug + // Cleanup is handled by on_stop_pulling instead RegisterSignal(new_pawn, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(on_stop_pulling)) -/datum/ai_controller/basic_controller/bot/honkbot/proc/on_clear_target(datum/source) - SIGNAL_HANDLER - - var/mob/living/living_pawn = pawn - living_pawn.stop_pulling() - /datum/ai_controller/basic_controller/bot/honkbot/proc/on_stop_pulling(datum/source) SIGNAL_HANDLER @@ -43,128 +29,115 @@ add_to_blacklist(slip_target) clear_blackboard_key(BB_SLIP_TARGET) -/datum/ai_planning_subtree/play_with_clowns/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/clown_target = controller.blackboard[BB_CLOWN_FRIEND] - if(QDELETED(clown_target)) - var/list/my_list = controller.blackboard[BB_CLOWNS_LIST] - controller.queue_behavior(/datum/ai_behavior/bot_search/clown_friends, BB_CLOWN_FRIEND, my_list) - return - controller.queue_behavior(/datum/ai_behavior/play_with_clown, BB_CLOWN_FRIEND) - return SUBTREE_RETURN_FINISH_PLANNING -/datum/ai_behavior/bot_search/clown_friends -/datum/ai_behavior/bot_search/clown_friends/valid_target(datum/ai_controller/basic_controller/bot/controller, mob/living/my_target) - if(HAS_TRAIT(my_target, TRAIT_PERCEIVED_AS_CLOWN)) - return TRUE - if(!istype(my_target, /mob/living/silicon/robot)) - return FALSE - var/mob/living/silicon/robot/robot_target = my_target - return istype(robot_target.model, /obj/item/robot_model/clown) +/datum/bt_node/subtree/honkbot_slip + behavior_tree_json = "code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.json" -/datum/ai_behavior/play_with_clown - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION -/datum/ai_behavior/play_with_clown/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) -/datum/ai_behavior/play_with_clown/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/living_target = controller.blackboard[target_key] - if(QDELETED(living_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/living_pawn = controller.pawn - var/datum/action/honk_ability = controller.blackboard[BB_HONK_ABILITY] - honk_ability?.Trigger() - living_pawn.manual_emote("celebrates with [living_target]!") - living_pawn.emote("flip") - living_pawn.emote("beep") - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED +/datum/bt_node/ai_behavior/use_mob_ability/random_honk -/datum/ai_behavior/play_with_clown/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - var/mob/living/living_target = controller.blackboard[target_key] - if(QDELETED(living_target)) - return - controller.add_to_blacklist(living_target) - controller.clear_blackboard_key(target_key) +/datum/bt_node/ai_behavior/use_mob_ability/random_honk/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(5, seconds_per_tick)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return ..() -/datum/ai_planning_subtree/slip_victims/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(!living_pawn.has_gravity()) - return - var/atom/slippery_item = controller.blackboard[BB_SLIPPERY_TARGET] - if(QDELETED(slippery_item) || !can_see(controller.pawn, slippery_item, 5)) - controller.clear_blackboard_key(BB_SLIP_TARGET) - controller.clear_blackboard_key(BB_SLIPPERY_TARGET) - controller.queue_behavior(/datum/ai_behavior/bot_search, BB_SLIPPERY_TARGET, controller.blackboard[BB_SLIPPERY_ITEMS]) - return - var/mob/living/living_target = controller.blackboard[BB_SLIP_TARGET] +/// Valid if the target is a visible human who isn't buckled and has gravity someone a slip can actually knock over. +/datum/targeting_strategy/can_see/slip_victim/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/carbon/human/candidate = target + if(!istype(candidate)) + return FALSE + return !candidate.buckled && candidate.has_gravity() - if(QDELETED(living_target)) - var/static/list/to_slip = typecacheof(list(/mob/living/carbon/human)) - controller.queue_behavior(/datum/ai_behavior/bot_search/slip_target, BB_SLIP_TARGET, to_slip) - return - if(living_pawn.pulling == living_target) - controller.queue_behavior(/datum/ai_behavior/drag_to_slip, BB_SLIP_TARGET, BB_SLIPPERY_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING +// Positions the pulled victim onto the slippery item by stepping away, then releases. +/datum/bt_node/ai_behavior/release_and_slip + var/victim_key - controller.queue_behavior(/datum/ai_behavior/drag_target, BB_SLIP_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/release_and_slip/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/victim = controller.blackboard[victim_key] + var/mob/living/our_mob = controller.pawn + if(QDELETED(victim) || our_mob.pulling != victim) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[our_mob] release_and_slip: not pulling victim") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[our_mob] releasing [victim]!", get_turf(our_mob), "HONK!") + var/list/possible_dirs = GLOB.alldirs.Copy() + possible_dirs -= get_dir(our_mob, victim) + for(var/direction in possible_dirs) + var/turf/possible_turf = get_step(our_mob, direction) + if(possible_turf.is_blocked_turf(source_atom = our_mob)) + possible_dirs -= direction + if(length(possible_dirs)) + step(our_mob, pick(possible_dirs)) + our_mob.stop_pulling() + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/bot_search/slip_target -/datum/ai_behavior/bot_search/slip_target/valid_target(datum/ai_controller/basic_controller/bot/controller, mob/living/my_target) - return (!my_target.buckled && my_target.has_gravity()) +/// Gathers nearby atoms matching the slippery-item typepaths stored in BB_SLIPPERY_ITEMS. +/datum/target_source/honkbot_slippery -/datum/ai_behavior/drag_to_slip - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 0 +/datum/target_source/honkbot_slippery/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/slippery_items = controller.blackboard[BB_SLIPPERY_ITEMS] + if(!length(slippery_items)) + return list() + return typecache_filter_list(oview(range, pawn), typecacheof(slippery_items)) -/datum/ai_behavior/drag_to_slip/setup(datum/ai_controller/controller, slip_target, slippery_target) +/// Valid only if the target is within line of sight (not just within range). +/datum/targeting_strategy/can_see/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) . = ..() - var/atom/target = controller.blackboard[slippery_target] - if(QDELETED(target)) + if(!.) return FALSE - set_movement_target(controller, target) + return can_see(living_mob, target, vision_range) -/datum/ai_behavior/drag_to_slip/perform(seconds_per_tick, datum/ai_controller/controller, slip_target, slippery_target) - var/mob/living/our_pawn = controller.pawn - var/atom/living_target = controller.blackboard[slip_target] +/// Valid if the target is a conscious clown (by trait, or a borg running the clown model). +/datum/targeting_strategy/clown_friend/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/mob/living/target_mob = target + if(!isliving(target_mob) || IS_UNCONSCIOUS_OR_CRIT(target_mob)) + return FALSE + if(HAS_TRAIT(target_mob, TRAIT_PERCEIVED_AS_CLOWN)) + return TRUE + if(istype(target_mob, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/robot_target = target_mob + return istype(robot_target.model, /obj/item/robot_model/clown) + return FALSE + +/datum/bt_node/ai_behavior/play_with_clown + var/target_key + +/datum/bt_node/ai_behavior/play_with_clown/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags + var/mob/living/living_target = controller.blackboard[target_key] if(QDELETED(living_target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/list/possible_dirs = GLOB.alldirs.Copy() - possible_dirs -= get_dir(our_pawn, living_target) - for(var/direction in possible_dirs) - var/turf/possible_turf = get_step(our_pawn, direction) - if(possible_turf.is_blocked_turf(source_atom = our_pawn)) - possible_dirs -= direction - step(our_pawn, pick(possible_dirs)) - our_pawn.stop_pulling() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + if(get_dist(controller.pawn, living_target) > 1) + return AI_BEHAVIOR_INSTANT + var/mob/living/living_pawn = controller.pawn + living_pawn.manual_emote("celebrates with [living_target]!") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), "flip") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), "beep") + return start_async() -/datum/ai_behavior/drag_to_slip/finish_action(datum/ai_controller/basic_controller/bot/controller, success, slip_target, slippery_target) - . = ..() - if(success) - var/mob/living/living_pawn = controller.pawn - living_pawn.emote("flip") - var/atom/slipped_victim = controller.blackboard[slip_target] - if(!isnull(slipped_victim)) - controller.add_to_blacklist(slipped_victim) - controller.clear_blackboard_key(slip_target) - controller.clear_blackboard_key(slippery_target) - -/datum/ai_planning_subtree/use_mob_ability/random_honk - ability_key = BB_HONK_ABILITY - -/datum/ai_planning_subtree/use_mob_ability/random_honk/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(5, seconds_per_tick)) +/datum/bt_node/ai_behavior/play_with_clown/perform_async(datum/ai_controller/controller) + var/datum/action/honk_ability = controller.blackboard[BB_HONK_ABILITY] + var/result = honk_ability?.Trigger() + if(!async_still_valid()) return - return ..() + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) +/datum/bt_node/ai_behavior/play_with_clown/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) + . = ..() + var/mob/living/living_target = controller.blackboard[target_key] + if(!isnull(living_target)) + controller.add_to_blacklist(living_target) + controller.clear_blackboard_key(target_key) diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.json b/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.json new file mode 100644 index 000000000000..b54d73dc62b0 --- /dev/null +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot_slip.bt.json @@ -0,0 +1,77 @@ +{ + "dm_type": "/datum/bt_node/subtree/honkbot_slip", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/can_see_target", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SLIPPERY_TARGET", + "range": 5 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/can_see_target", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SLIP_TARGET", + "range": 5 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_has_gravity", + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SLIP_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grab_target", + "vars": { + "target_key": "BB_SLIP_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_grabbing_target", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_SLIP_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SLIPPERY_TARGET", + "required_dist": 0, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/release_and_slip", + "vars": { + "victim_key": "BB_SLIP_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "flip" + } + } + ] + } + } + } +} diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.json b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.json new file mode 100644 index 000000000000..6dd0c7cc92c8 --- /dev/null +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.json @@ -0,0 +1,109 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/hygienebot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_WASH_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wash_target", + "vars": { + "target_key": "BB_WASH_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_WASH_TARGET", + "required_dist": 0, + "finish_on_arrival": false, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "cooldown_key": "BB_TRASH_TALK_COOLDOWN", + "cooldown_duration": "4 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/commence_trashtalk", + "vars": { + "target_key": "BB_WASH_TARGET" + } + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/hygiene_wash", + "vars": { + "target_key": "BB_WASH_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/capable_human/washable_human", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "vision_range": 5, + "time_between_perform": "5 SECONDS" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm index 1d0c689bff4b..8dc088e012ca 100644 --- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm @@ -2,6 +2,7 @@ #define BOT_ANGER_THRESHOLD 5 /datum/ai_controller/basic_controller/bot/hygienebot + behavior_tree_json = "code/modules/mob/living/basic/bots/hygienebot/hygienebot.bt.json" blackboard = list( BB_SALUTE_MESSAGES = list( "salutes", @@ -9,14 +10,6 @@ ), BB_WASH_FRUSTRATION = 0, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/handle_trash_talk, - /datum/ai_planning_subtree/wash_people, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon, - ) reset_keys = list( BB_WASH_TARGET, BB_BEACON_TARGET, @@ -24,115 +17,68 @@ BB_BOT_SUMMON_TARGET, ) -/datum/ai_planning_subtree/handle_trash_talk -/datum/ai_planning_subtree/handle_trash_talk/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_WASH_TARGET)) - return - controller.queue_behavior(/datum/ai_behavior/commence_trashtalk, BB_WASH_TARGET) -/datum/ai_behavior/commence_trashtalk - action_cooldown = 4 SECONDS +/datum/bt_node/ai_behavior/commence_trashtalk + var/target_key -/datum/ai_behavior/commence_trashtalk/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/commence_trashtalk/perform(seconds_per_tick, datum/ai_controller/controller) if(!controller.blackboard_key_exists(target_key)) - return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/frustration_count = controller.blackboard[BB_WASH_FRUSTRATION] controller.set_blackboard_key(BB_WASH_FRUSTRATION, min(frustration_count + 1, BOT_FRUSTRATION_LIMIT)) if(controller.blackboard[BB_WASH_FRUSTRATION] < BOT_ANGER_THRESHOLD) - return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_WASH_THREATS])) - return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY - - -/datum/ai_planning_subtree/wash_people - -/datum/ai_planning_subtree/wash_people/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/bot_pawn = controller.pawn - - var/atom/wash_target = controller.blackboard[BB_WASH_TARGET] - if(QDELETED(wash_target)) - controller.queue_behavior(/datum/ai_behavior/find_valid_wash_targets, BB_WASH_TARGET, bot_pawn.bot_access_flags) - return - - if(get_dist(bot_pawn, wash_target) < 9) - controller.queue_behavior(/datum/ai_behavior/wash_target, BB_WASH_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.clear_blackboard_key(BB_WASH_TARGET) //delete if too far - -/datum/ai_behavior/find_valid_wash_targets - action_cooldown = 5 SECONDS - -/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, our_access_flags) - . = ..() - var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - var/atom/found_target - for(var/mob/living/carbon/human/wash_potential in oview(5, controller.pawn)) - - if(found_target) - break - - if(isnull(wash_potential.mind) || wash_potential.stat != CONSCIOUS) - continue - - if(LAZYACCESS(ignore_list, wash_potential)) - continue - - if(our_access_flags & BOT_COVER_EMAGGED) - controller.add_to_blacklist(wash_potential) - found_target = wash_potential - break - - for(var/atom/clothing in wash_potential.get_equipped_items(INCLUDE_HELD|INCLUDE_PROSTHETICS)) - if(GET_ATOM_BLOOD_DNA_LENGTH(clothing)) - found_target = wash_potential - break + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(isnull(found_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(target_key, found_target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/find_valid_wash_targets/finish_action(datum/ai_controller/controller, succeeded, target_key) +/// Valid if the target is a conscious human with bloodied clothing (or anyone, while emagged). +/datum/targeting_strategy/capable_human/washable_human/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) . = ..() - if(!succeeded) - return + if(!.) + return FALSE + var/mob/living/basic/bot/bot_pawn = living_mob + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + return TRUE + var/mob/living/carbon/human/human_target = target + for(var/atom/clothing in human_target.get_equipped_items(INCLUDE_HELD|INCLUDE_PROSTHETICS)) + if(GET_ATOM_BLOOD_DNA_LENGTH(clothing)) + return TRUE + return FALSE + +/// Finds someone to wash and announces it; while emagged the target is blacklisted so the bot washes each person only once. +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/hygiene_wash + +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/hygiene_wash/on_target_found(datum/ai_controller/basic_controller/bot/controller, atom/target, datum/targeting_strategy/strategy) + var/mob/living/basic/bot/bot_pawn = controller.pawn + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + controller.add_to_blacklist(target) var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement.announce(pick(controller.blackboard[BB_WASH_FOUND])) -/datum/ai_behavior/wash_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 0 -/datum/ai_behavior/wash_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) +/datum/bt_node/ai_behavior/wash_target + var/target_key -/datum/ai_behavior/wash_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) - . = ..() +/datum/bt_node/ai_behavior/wash_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/mob/living/carbon/human/unclean_target = controller.blackboard[target_key] var/mob/living/basic/living_pawn = controller.pawn if(QDELETED(unclean_target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(get_dist(living_pawn, unclean_target) > 0) + return AI_BEHAVIOR_INSTANT + living_pawn.melee_attack(unclean_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(living_pawn.loc == get_turf(unclean_target)) - living_pawn.melee_attack(unclean_target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/wash_target/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/wash_target/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) var/wash_frustration = controller.blackboard[BB_WASH_FRUSTRATION] - controller.clear_blackboard_key(BB_WASH_FRUSTRATION) + controller.set_blackboard_key(BB_WASH_FRUSTRATION, 0) if(!succeeded || wash_frustration <= BOT_ANGER_THRESHOLD) return var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.bt.json b/code/modules/mob/living/basic/bots/medbot/medbot.bt.json new file mode 100644 index 000000000000..695f0cebe8ca --- /dev/null +++ b/code/modules/mob/living/basic/bots/medbot/medbot.bt.json @@ -0,0 +1,109 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/medbot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": false, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": false, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": false, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_medical_flag", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "flag": "MEDBOT_DECLARE_CRIT" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/medbot_find_and_announce_crit" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_medical_flag", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true, + "flag": "MEDBOT_TIPPED_MODE" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/medbot_treat_patient" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_PATIENT_IN_CRIT", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/crit_patient", + "vision_range": 7 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/medbot_patient", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob/medbot_patient", + "targeting_strategy": "/datum/targeting_strategy/treatable_patient", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "must_be_reachable": true, + "reach_distance": 20, + "vision_range": 7 + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_medical_flag", + "vars": { + "flag": "MEDBOT_SPEAK_MODE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/handle_medbot_speech", + "vars": { + "announce_key": "BB_ANNOUNCE_ABILITY" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm index 2dfb4c2fed0a..355cd17cb10b 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm @@ -12,7 +12,7 @@ light_power = 0.8 light_color = "#99ccff" pass_flags = PASSMOB | PASSFLAPS - status_flags = (CANPUSH | CANSTUN) + status_flags = CANSTUN ai_controller = /datum/ai_controller/basic_controller/bot/medbot custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 6.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2.5) diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm index 8b53bc619466..453877085c09 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -1,17 +1,18 @@ #define BOT_PATIENT_PATH_LIMIT 20 + +/// Find and treat a patient used by both the speak-mode parallel and the silent fallback branch. +/datum/bt_node/subtree/medbot_treat_patient + behavior_tree_json = "code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.json" + +/// Find a patient in hard-crit and announce them on radio. +/datum/bt_node/subtree/medbot_find_and_announce_crit + behavior_tree_json = "code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.json" + /datum/ai_controller/basic_controller/bot/medbot - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/handle_medbot_speech, - /datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit, - /datum/ai_planning_subtree/treat_wounded_target, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon/medbot, - ) + behavior_tree_json = "code/modules/mob/living/basic/bots/medbot/medbot.bt.json" ai_movement = /datum/ai_movement/jps/bot/medbot reset_keys = list( - BB_PATIENT_TARGET, + BB_CURRENT_TARGET, BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, @@ -21,7 +22,7 @@ maximum_length = BOT_PATIENT_PATH_LIMIT max_pathing_attempts = 20 -// only AI isnt allowed to move when this flag is set, sentient players can +// only AI isn't allowed to move when this flag is set, sentient players can /datum/ai_movement/jps/bot/medbot/allowed_to_move(datum/move_loop/source) var/datum/ai_controller/controller = source.extra_info var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn @@ -33,172 +34,137 @@ maximum_length = AI_BOT_PATH_LENGTH -/datum/ai_planning_subtree/treat_wounded_target -/datum/ai_planning_subtree/treat_wounded_target/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE) - controller.clear_blackboard_key(BB_PATIENT_TARGET) +/// Medbot's note_unreachable_target skips blacklisting while stationary, matching the old set_if_can_reach bypass. +/datum/ai_controller/basic_controller/bot/medbot/note_unreachable_target(atom/target) + var/mob/living/basic/bot/medbot/bot_pawn = pawn + if(bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) return - var/is_stationary = bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE - if(controller.blackboard_key_exists(BB_PATIENT_TARGET)) - controller.queue_behavior(/datum/ai_behavior/tend_to_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags, is_stationary) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_suitable_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.medical_mode_flags, bot_pawn.bot_access_flags) - -/datum/ai_behavior/find_suitable_patient - var/search_range = 7 - action_cooldown = 2 SECONDS - -/datum/ai_behavior/find_suitable_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, heal_type, mode_flags, access_flags) - search_range = (mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : initial(search_range) - var/list/ignore_keys = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - for(var/mob/living/carbon/human/treatable_target in oview(search_range, controller.pawn)) - if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD) - continue - if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS) - controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) - break - if((heal_type == HEAL_ALL_DAMAGE)) - if(treatable_target.get_total_damage() > threshold) - controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) - break - continue - if(treatable_target.get_current_damage_of_type(damagetype = heal_type) > threshold) - controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) - break - - if(controller.blackboard_key_exists(BB_PATIENT_TARGET)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - else - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return ..() + +/// Gathers nearby humans as patients; range is clamped to adjacent tiles when the medbot is in stationary mode. I should probably just make this a blackboard thing but I cannot be arsed right now. +/datum/target_source/oview_single_type/human_mob/medbot_patient -/datum/ai_behavior/find_suitable_patient/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/target_source/oview_single_type/human_mob/medbot_patient/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/mob/living/basic/bot/medbot/bot_pawn = pawn + if(bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) + range = 1 + return ..(pawn, controller, range) + +/// Valid if the patient needs the damage type this medbot heals (or is a conscious target while emagged). +/datum/targeting_strategy/treatable_patient/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) . = ..() - if(!succeeded || QDELETED(controller.pawn) ||get_dist(controller.pawn, controller.blackboard[target_key]) <= 1) + if(!.) + return FALSE + var/mob/living/carbon/human/patient = target + if(!istype(patient) || patient.stat == DEAD) + return FALSE + var/mob/living/basic/bot/medbot/bot_pawn = living_mob + if((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) && patient.stat == STABLE) + return TRUE + if(bot_pawn.damage_type_healer == HEAL_ALL_DAMAGE) + return patient.get_total_damage() > bot_pawn.heal_threshold + return patient.get_current_damage_of_type(damagetype = bot_pawn.damage_type_healer) > bot_pawn.heal_threshold + +/// Finds a patient to treat, announcing that the bot is on its way when the patient isn't already adjacent. +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/medbot_patient + +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/medbot_patient/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + if(QDELETED(controller.pawn) || get_dist(controller.pawn, target) <= 1) return var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_WAIT_SPEECH])) -/datum/ai_behavior/tend_to_patient - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH -/datum/ai_behavior/tend_to_patient/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) -/datum/ai_behavior/tend_to_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, damage_type_healer, access_flags, is_stationary) +/datum/bt_node/ai_behavior/tend_to_patient + var/target_key + +/datum/bt_node/ai_behavior/tend_to_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/mob/living/carbon/human/patient = controller.blackboard[target_key] if(QDELETED(patient) || patient.stat == DEAD) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[controller.pawn] tend_to_patient: patient gone (deleted=[QDELETED(patient)], stat=[patient?.stat])") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(check_if_healed(patient, threshold, damage_type_healer, access_flags)) + if(get_dist(controller.pawn, patient) > 1) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED //We technically failed, but we want to try again so succeed. + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + if(check_if_healed(patient, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags)) + EVLOG_TEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] tend_to_patient: [patient] is fully healed") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - var/mob/living/basic/bot/bot_pawn = controller.pawn if(patient.stat >= HARD_CRIT && prob(5)) var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_NEAR_DEATH_SPEECH])) + EVLOG_MAPTEXT(controller, EVLOG_CATEGORY_AI_BEHAVIORS, "[bot_pawn] healing [patient] (dmg=[patient.get_total_damage()])", get_turf(patient), "Heal") bot_pawn.melee_attack(patient) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -// only clear the target if they get healed -/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, threshold, damage_type_healer, access_flags, is_stationary) +/datum/bt_node/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) . = ..() + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn var/atom/target = controller.blackboard[target_key] + var/is_stationary = bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE if(!succeeded) - if(!isnull(target) && !is_stationary) controller.add_to_blacklist(target) - controller.clear_blackboard_key(target_key) return - - if(QDELETED(target) || !check_if_healed(target, threshold, damage_type_healer, access_flags)) + if(QDELETED(target) || !check_if_healed(target, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags)) return - var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] announcement?.announce(pick(controller.blackboard[BB_AFTERHEAL_SPEECH])) controller.clear_blackboard_key(target_key) -/datum/ai_behavior/tend_to_patient/proc/check_if_healed(mob/living/carbon/human/patient, threshold, damage_type_healer, access_flags) +/datum/bt_node/ai_behavior/tend_to_patient/proc/check_if_healed(mob/living/carbon/human/patient, threshold, damage_type_healer, access_flags) if(access_flags & BOT_COVER_EMAGGED) - return (patient.stat > CONSCIOUS) + return (patient.stat != STABLE) var/patient_damage = (damage_type_healer == HEAL_ALL_DAMAGE) ? patient.get_total_damage() : patient.get_current_damage_of_type(damagetype = damage_type_healer) return (patient_damage <= threshold) -/datum/ai_planning_subtree/handle_medbot_speech - var/speech_chance = 5 -/datum/ai_planning_subtree/handle_medbot_speech/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - //we cant speak! - if(!(bot_pawn.medical_mode_flags & MEDBOT_SPEAK_MODE)) - return +/datum/bt_node/ai_behavior/handle_medbot_speech + var/announce_key + time_between_perform = 20 SECONDS +/datum/bt_node/ai_behavior/handle_medbot_speech/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn var/currently_tipped = bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE - speech_chance = ((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) || currently_tipped) ? 15 : initial(speech_chance) - + var/speech_chance = ((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) || currently_tipped) ? 15 : 5 if(!SPT_PROB(speech_chance, seconds_per_tick)) - return - - controller.queue_behavior(/datum/ai_behavior/handle_medbot_speech, BB_ANNOUNCE_ABILITY, bot_pawn.mode, bot_pawn.bot_access_flags, currently_tipped) - -/datum/ai_behavior/handle_medbot_speech - action_cooldown = 20 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/handle_medbot_speech/perform(seconds_per_tick, datum/ai_controller/controller, announce_key, mode, cover_flags, currently_tipped) - . = ..() + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[announce_key] var/list/speech_to_pick_from - if(currently_tipped) speech_to_pick_from = controller.blackboard[BB_WORRIED_ANNOUNCEMENTS] - else if(cover_flags & BOT_COVER_EMAGGED) + else if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) speech_to_pick_from = controller.blackboard[BB_EMAGGED_SPEECH] - else if(mode == BOT_IDLE) + else if(bot_pawn.mode == BOT_IDLE) speech_to_pick_from = controller.blackboard[BB_IDLE_SPEECH] var/mob/living/living_pawn = controller.pawn - if(locate(/obj/item/clothing/head/costume/chicken) in living_pawn) speech_to_pick_from += MEDIBOT_VOICED_CHICKEN - if(!length(speech_to_pick_from)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - announcement.announce(pick(speech_to_pick_from)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit - target_key = BB_PATIENT_IN_CRIT - hunting_behavior = /datum/ai_behavior/announce_patient - finding_behavior = /datum/ai_behavior/find_hunt_target/patient_in_crit - hunt_targets = list(/mob/living/carbon/human) - finish_planning = FALSE -/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(!(bot_pawn.medical_mode_flags & MEDBOT_DECLARE_CRIT)) - return - return ..() -/datum/ai_behavior/find_hunt_target/patient_in_crit - -/datum/ai_behavior/find_hunt_target/patient_in_crit/valid_dinner(mob/living/source, mob/living/carbon/human/patient, radius) - if(patient.stat < UNCONSCIOUS || isnull(patient.mind)) +/// Valid if the patient is at least unconscious, has a mind, and is visible used to announce medical emergencies. +/datum/targeting_strategy/crit_patient/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) return FALSE - return can_see(source, patient, radius) + var/mob/living/carbon/human/patient = target + if(!istype(patient) || patient.stat < HARD_CRIT || isnull(patient.mind)) + return FALSE + return can_see(living_mob, patient, vision_range) -/datum/ai_behavior/announce_patient - action_cooldown = 3 MINUTES - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION +/datum/bt_node/ai_behavior/announce_patient + var/target_key + time_between_perform = 3 MINUTES -/datum/ai_behavior/announce_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) +/datum/bt_node/ai_behavior/announce_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/mob/living/living_target = controller.blackboard[target_key] if(QDELETED(living_target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED @@ -209,22 +175,8 @@ announcement.announce(text_to_announce, controller.blackboard[BB_RADIO_CHANNEL]) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/announce_patient/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/announce_patient/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) -/datum/ai_planning_subtree/find_patrol_beacon/medbot - ///travel towards beacon behavior - travel_behavior = /datum/ai_behavior/travel_towards/beacon/medbot - -/datum/ai_planning_subtree/find_patrol_beacon/medbot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn - if(bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) - return - return ..() - - -/datum/ai_behavior/travel_towards/beacon/medbot - new_movement_type = /datum/ai_movement/jps/bot/medbot/travel_to_beacon - #undef BOT_PATIENT_PATH_LIMIT diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.json b/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.json new file mode 100644 index 000000000000..48d29e9b23c6 --- /dev/null +++ b/code/modules/mob/living/basic/bots/medbot/medbot_find_and_announce_crit.bt.json @@ -0,0 +1,16 @@ +{ + "dm_type": "/datum/bt_node/subtree/medbot_find_and_announce_crit", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_PATIENT_IN_CRIT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/announce_patient", + "vars": { + "target_key": "BB_PATIENT_IN_CRIT" + } + } +} diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.json b/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.json new file mode 100644 index 000000000000..eb4da905953f --- /dev/null +++ b/code/modules/mob/living/basic/bots/medbot/medbot_treat_patient.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/bt_node/subtree/medbot_treat_patient", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/tend_to_patient", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.json b/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.json new file mode 100644 index 000000000000..7b0d49a1a865 --- /dev/null +++ b/code/modules/mob/living/basic/bots/mulebot/mulebot.bt.json @@ -0,0 +1,103 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/mulebot", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MULEBOT_TRAVEL_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_MULEBOT_TRAVEL_TARGET", + "required_dist": 0, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/handle_delivery", + "vars": { + "target_key": "BB_MULEBOT_TRAVEL_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_wire_cut", + "vars": { + "wire": "WIRE_BEACON", + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MULEBOT_TRAVEL_TARGET", + "invert": true + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_mode", + "vars": { + "mode": "BOT_DELIVER" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_delivery_beacon", + "vars": { + "target_key": "BB_MULEBOT_TRAVEL_TARGET", + "tag_key": "BB_MULEBOT_DESTINATION_BEACON" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_mode", + "vars": { + "mode": "BOT_GO_HOME" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_delivery_beacon", + "vars": { + "target_key": "BB_MULEBOT_TRAVEL_TARGET", + "tag_key": "BB_MULEBOT_HOME_BEACON" + } + } + } + ] + } + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] +} diff --git a/code/modules/mob/living/basic/bots/mulebot/mulebot.dm b/code/modules/mob/living/basic/bots/mulebot/mulebot.dm index 55d5632e5e18..9367a81d6d54 100644 --- a/code/modules/mob/living/basic/bots/mulebot/mulebot.dm +++ b/code/modules/mob/living/basic/bots/mulebot/mulebot.dm @@ -165,7 +165,7 @@ . = ..() if(mode != BOT_BLOCKED) return - var/obj/machinery/navbeacon/beacon = ai_controller.current_movement_target + var/obj/machinery/navbeacon/beacon = ai_controller.blackboard[BB_CURRENT_MOVEMENT_TARGET] if(!istype(beacon)) return var/intended_mode = beacon.location == ai_controller.blackboard[BB_MULEBOT_HOME_BEACON] ? BOT_GO_HOME : BOT_DELIVER diff --git a/code/modules/mob/living/basic/bots/mulebot/mulebot_ai.dm b/code/modules/mob/living/basic/bots/mulebot/mulebot_ai.dm index 7898ced148ce..c5fba5546191 100644 --- a/code/modules/mob/living/basic/bots/mulebot/mulebot_ai.dm +++ b/code/modules/mob/living/basic/bots/mulebot/mulebot_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/bot/mulebot + behavior_tree_json = "code/modules/mob/living/basic/bots/mulebot/mulebot.bt.json" blackboard = list( BB_SALUTE_MESSAGES = list( "blinks its light in appreciation towards", @@ -6,12 +7,6 @@ ) ai_movement = /datum/ai_movement/jps/bot/mulebot max_target_distance = AI_MULEBOT_PATH_LENGTH - planning_subtrees = list( - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/attempt_delivery, - /datum/ai_planning_subtree/find_delivery_beacon, - ) reset_keys = list( BB_BOT_SUMMON_TARGET, BB_MULEBOT_DESTINATION_BEACON, @@ -40,72 +35,15 @@ ) RegisterSignals(my_bot, content_signals, PROC_REF(update_able_to_run)) -/datum/ai_planning_subtree/find_delivery_beacon - ///what behavior do we use to seek beacons - var/find_beacon_behaviour = /datum/ai_behavior/find_delivery_beacon - -/datum/ai_planning_subtree/find_delivery_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/mulebot/bot_pawn = controller.pawn - if(bot_pawn.wires.is_cut(WIRE_BEACON)) - return - - if(!controller.blackboard_key_exists(BB_MULEBOT_TRAVEL_TARGET)) - controller.queue_behavior(find_beacon_behaviour, BB_MULEBOT_TRAVEL_TARGET) - -/datum/ai_behavior/find_delivery_beacon - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_delivery_beacon/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/basic/bot/mulebot/bot_pawn = controller.pawn - var/atom/delivery_beacon - - var/beacon_tag = null - - switch(bot_pawn.mode) - if(BOT_DELIVER) - beacon_tag = controller.blackboard[BB_MULEBOT_DESTINATION_BEACON] - if(BOT_GO_HOME) - beacon_tag = controller.blackboard[BB_MULEBOT_HOME_BEACON] - else - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - for(var/obj/machinery/navbeacon/beacon as anything in GLOB.deliverybeacons) - if(beacon.location == beacon_tag) - delivery_beacon = beacon - break - - if(isnull(delivery_beacon)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(BB_MULEBOT_TRAVEL_TARGET, delivery_beacon) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/travel_towards/delivery_beacon - new_movement_type = /datum/ai_movement/jps/bot/mulebot - -/datum/ai_planning_subtree/attempt_delivery - ///behavior we use to unload crates - var/delivery_behaviour = /datum/ai_behavior/handle_delivery - -/datum/ai_planning_subtree/attempt_delivery/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_MULEBOT_TRAVEL_TARGET)) - return +/// Loads or unloads cargo at the delivery beacon held in target_key, then heads home or idles. +/datum/bt_node/ai_behavior/handle_delivery + var/target_key + time_between_perform = 1 SECONDS - controller.queue_behavior(delivery_behaviour, BB_MULEBOT_TRAVEL_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/handle_delivery - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/handle_delivery/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/handle_delivery/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/handle_delivery/perform(seconds_per_tick, datum/ai_controller/controller) var/obj/machinery/navbeacon/beacon = controller.blackboard[target_key] + if(QDELETED(beacon)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/mob/living/basic/bot/mulebot/bot_pawn = controller.pawn var/load_direction = beacon.codes[NAVBEACON_DELIVERY_DIRECTION] // this will be the load/unload dir diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.json b/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.json new file mode 100644 index 000000000000..11f16b782b3f --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.bt.json @@ -0,0 +1,32 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/repairbot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "0.5 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/repairbot_repair_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/repairbot_find_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_salute_authority" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm index 4bb584a3de54..c752451b65b4 100644 --- a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm @@ -99,10 +99,12 @@ toolbox_color = new_color update_appearance() -/mob/living/basic/bot/repairbot/attackby(obj/item/potential_stack, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers) - if(!istype(potential_stack, /obj/item/stack)) +/mob/living/basic/bot/repairbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack)) return ..() - attempt_merge(potential_stack, user) + + attempt_merge(tool, user) + return ITEM_INTERACT_SUCCESS /mob/living/basic/bot/repairbot/proc/attempt_merge(obj/item/stack/potential_stack, mob/living/user) var/static/list/our_contents = list(/obj/item/stack/sheet/iron, /obj/item/stack/sheet/glass, /obj/item/stack/tile, /obj/item/stack/rods) diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm index 621d00338956..41c2330fdb8c 100644 --- a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm @@ -1,200 +1,158 @@ #define REPAIRBOT_SPEECH_TIMER 30 SECONDS +/// Emagged repairbot behavior: mug robots then deconstruct structures. +/datum/bt_node/subtree/repairbot_emagged + behavior_tree_json = "code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.json" + +/datum/bt_node/subtree/repairbot_repair_target + behavior_tree_json = "code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.json" + +/datum/bt_node/subtree/repairbot_find_target + behavior_tree_json = "code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.json" + /datum/ai_controller/basic_controller/bot/repairbot - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/repairbot_speech, - /datum/ai_planning_subtree/mug_robot, - /datum/ai_planning_subtree/refill_materials, - /datum/ai_planning_subtree/repairbot_deconstruction, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/replace_floors/breaches, - /datum/ai_planning_subtree/wall_girder, - /datum/ai_planning_subtree/build_girder, - /datum/ai_planning_subtree/replace_window, - /datum/ai_planning_subtree/replace_floors, - /datum/ai_planning_subtree/fix_window, - /datum/ai_planning_subtree/salute_authority, - /datum/ai_planning_subtree/find_patrol_beacon, - ) + behavior_tree_json = "code/modules/mob/living/basic/bots/repairbot/repairbot.bt.json" + + + reset_keys = list( - BB_TILELESS_FLOOR, - BB_GIRDER_TARGET, - BB_GIRDER_TO_WALL_TARGET, BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, - BB_WELDER_TARGET, - BB_WINDOW_FRAMETARGET, + BB_CURRENT_TARGET, ) - minimum_distance = 1 -///subtree to refill our stacks -/datum/ai_planning_subtree/refill_materials -/datum/ai_planning_subtree/refill_materials/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/static/list/refillable_items = typecacheof(list( - /obj/item/stack/sheet/iron, - /obj/item/stack/sheet/glass, - /obj/item/stack/tile, - )) - if(!controller.blackboard_key_exists(BB_REFILLABLE_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_search/refillable_target, BB_REFILLABLE_TARGET, refillable_items) - return - controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_REFILLABLE_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING -/datum/ai_behavior/bot_search/refillable_target - action_cooldown = 10 SECONDS +/datum/bt_node/ai_behavior/repairbot_speech -/datum/ai_behavior/bot_search/refillable_target/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) - var/static/list/desired_types = list( - /obj/item/stack/sheet/iron, - /obj/item/stack/sheet/glass, - /obj/item/stack/tile, +/datum/bt_node/ai_behavior/repairbot_speech/setup(datum/ai_controller/controller) + if(controller.blackboard[BB_REPAIRBOT_SPEECH_COOLDOWN] > world.time) + return FALSE + var/static/list/keys_to_look = list( + BB_CURRENT_TARGET, + BB_DECONSTRUCT_TARGET, ) - for(var/object_type in desired_types) - if(!istype(my_target, object_type)) - continue - var/obj/item/stack/sheet_type = locate(object_type) in controller.pawn - if(isnull(sheet_type)) - return TRUE //we dont have any of it! - if(sheet_type.amount < sheet_type.max_amount && sheet_type.can_merge(my_target)) - return TRUE + for(var/key in keys_to_look) + if(controller.blackboard_key_exists(key)) + return ..() return FALSE -/datum/ai_planning_subtree/mug_robot - -/datum/ai_planning_subtree/mug_robot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/living_bot = controller.pawn - if(!(living_bot.bot_access_flags & BOT_COVER_EMAGGED)) - return - var/static/list/robot_targets = typecacheof( - /mob/living/silicon/robot, - ) - if(!controller.blackboard_key_exists(BB_ROBOT_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_search/valid_robot, BB_ROBOT_TARGET, robot_targets) - return - if(!living_bot.pulling) - controller.queue_behavior(/datum/ai_behavior/drag_target, BB_ROBOT_TARGET) +/datum/bt_node/ai_behavior/repairbot_speech/perform(seconds_per_tick, datum/ai_controller/controller) + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + // determine speech type: emagged -> emagged speech, otherwise normal + var/list/speech_to_pick_from + if(controller.blackboard_key_exists(BB_DECONSTRUCT_TARGET)) + speech_to_pick_from = controller.blackboard[BB_REPAIRBOT_EMAGGED_SPEECH] else - controller.queue_behavior(/datum/ai_behavior/bot_interact/tip_robot, BB_ROBOT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + speech_to_pick_from = controller.blackboard[BB_REPAIRBOT_NORMAL_SPEECH] + if(!length(speech_to_pick_from)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + announcement.announce(pick(speech_to_pick_from)) + controller.set_blackboard_key(BB_REPAIRBOT_SPEECH_COOLDOWN, world.time + REPAIRBOT_SPEECH_TIMER) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/bot_search/valid_robot - action_cooldown = 10 SECONDS -/datum/ai_behavior/bot_search/valid_robot/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) - return (!HAS_TRAIT(my_target, TRAIT_MOB_TIPPED)) && can_see(controller.pawn, my_target) -/datum/ai_behavior/bot_interact/tip_robot +/datum/bt_node/ai_behavior/bot_interact/tip_robot + +/datum/bt_node/ai_behavior/bot_interact/tip_robot/setup(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + var/mob/living/pawn = controller.pawn + if(QDELETED(target) || pawn.pulling != target) + return FALSE + return ..() -/datum/ai_behavior/bot_interact/tip_robot/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/bot_interact/tip_robot/finish_action(datum/ai_controller/controller, succeeded) . = ..() if(succeeded) var/mob/living/living_pawn = controller.pawn living_pawn.stop_pulling() -///subtree to deconstruct things when we're emagged -/datum/ai_planning_subtree/repairbot_deconstruction +/datum/bt_node/ai_behavior/bot_search/valid_robot + time_between_perform = 10 SECONDS + +/datum/bt_node/ai_behavior/bot_search/valid_robot/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + if(!istype(my_target, /mob/living/silicon/robot)) + return FALSE + return (!HAS_TRAIT(my_target, TRAIT_MOB_TIPPED)) && can_see(controller.pawn, my_target) + -/datum/ai_planning_subtree/repairbot_deconstruction/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/living_bot = controller.pawn - if(!(living_bot.bot_access_flags & BOT_COVER_EMAGGED)) - return + +/datum/bt_node/ai_behavior/bot_search/deconstructable + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/bot_search/deconstructable/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + return (!(my_target.resistance_flags & INDESTRUCTIBLE) && !isgroundlessturf(my_target)) + +/datum/bt_node/ai_behavior/bot_search/deconstructable/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/static/list/things_to_deconstruct = typecacheof(list( /obj/structure/window, /turf/open/floor, /turf/closed/wall, )) - if(!controller.blackboard_key_exists(BB_DECONSTRUCT_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_search/deconstructable, BB_DECONSTRUCT_TARGET, things_to_deconstruct) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_DECONSTRUCT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + looking_for = things_to_deconstruct + return ..() -/datum/ai_behavior/bot_search/deconstructable - action_cooldown = 5 SECONDS -/datum/ai_behavior/bot_search/deconstructable/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) - return (!(my_target.resistance_flags & INDESTRUCTIBLE) && !isgroundlessturf(my_target)) -///subtree to control bot speech -/datum/ai_planning_subtree/repairbot_speech +/datum/bt_node/ai_behavior/bot_search/refillable_target + time_between_perform = 10 SECONDS -/datum/ai_planning_subtree/repairbot_speech/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard[BB_REPAIRBOT_SPEECH_COOLDOWN] > world.time) - return - var/static/list/keys_to_look = list( - BB_WELDER_TARGET, - BB_WINDOW_FRAMETARGET, - BB_TILELESS_FLOOR, - BB_BREACHED_FLOOR, - BB_GIRDER_TO_WALL_TARGET, - BB_GIRDER_TARGET, - BB_DECONSTRUCT_TARGET, +/datum/bt_node/ai_behavior/bot_search/refillable_target/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) + var/static/list/desired_types = list( + /obj/item/stack/sheet/iron, + /obj/item/stack/sheet/glass, + /obj/item/stack/tile, ) - for(var/key in keys_to_look) - if(controller.blackboard_key_exists(key)) - controller.queue_behavior(/datum/ai_behavior/repairbot_speech, key) - return + for(var/object_type in desired_types) + if(!istype(my_target, object_type)) + continue + var/obj/item/stack/sheet_type = locate(object_type) in controller.pawn + if(isnull(sheet_type)) + return TRUE //we dont have any of it! + if(sheet_type.amount < sheet_type.max_amount && sheet_type.can_merge(my_target)) + return TRUE + return FALSE -/datum/ai_behavior/repairbot_speech - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION +/datum/bt_node/ai_behavior/bot_search/refillable_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + var/static/list/refillable_items = typecacheof(list( + /obj/item/stack/sheet/iron, + /obj/item/stack/sheet/glass, + /obj/item/stack/tile, + )) + looking_for = refillable_items + return ..() -/datum/ai_behavior/repairbot_speech/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] - var/list/speech_to_pick_from = (target_key == BB_DECONSTRUCT_TARGET) ? controller.blackboard[BB_REPAIRBOT_EMAGGED_SPEECH] : controller.blackboard[BB_REPAIRBOT_NORMAL_SPEECH] - if(!length(speech_to_pick_from)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - announcement.announce(pick(speech_to_pick_from)) - controller.set_blackboard_key(BB_REPAIRBOT_SPEECH_COOLDOWN, world.time + REPAIRBOT_SPEECH_TIMER) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -///subtree to replace iron platings -/datum/ai_planning_subtree/replace_floors - ///flag we check before executing - var/required_flag = REPAIRBOT_REPLACE_TILES - ///key of our floor target - var/floor_key = BB_TILELESS_FLOOR - ///type of tile we need to replace floors - var/needed_tile_type = /obj/item/stack/tile - ///type of floors we can replace - var/list/type_of_turf = list(/turf/open/floor/plating) - ///our searching behavior - var/search_behavior = /datum/ai_behavior/bot_search/valid_plateless_turf - -/datum/ai_planning_subtree/replace_floors/New() - . = ..() - type_of_turf = typecacheof(type_of_turf) +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf + turf_search = TRUE + time_between_perform = 5 SECONDS -/datum/ai_planning_subtree/replace_floors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn - if(!(bot_pawn.repairbot_flags & required_flag)) - return - if(!locate(needed_tile_type) in bot_pawn) - return - if(controller.blackboard_key_exists(floor_key)) - controller.queue_behavior(/datum/ai_behavior/bot_interact, floor_key) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/proc/get_turf_type_filter() + return typecacheof(list(/turf/open/floor/plating)) - controller.queue_behavior(search_behavior, floor_key, type_of_turf, 5, 10, FALSE, TRUE) +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/setup(datum/ai_controller/controller) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_REPLACE_TILES)) + return FALSE + if(!locate(/obj/item/stack/tile) in bot_pawn) + return FALSE + return TRUE -/datum/ai_behavior/bot_search/valid_plateless_turf - action_cooldown = 5 SECONDS +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + looking_for = get_turf_type_filter() + return ..() -/datum/ai_behavior/bot_search/valid_plateless_turf/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/open/my_target) +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/open/my_target) var/static/list/blacklist_objects = typecacheof(list( /obj/structure/window, /obj/structure/grille, )) - for(var/atom/possible_blacklisted in my_target.contents) if(is_type_in_typecache(possible_blacklisted, blacklist_objects)) return FALSE - if(istype(my_target, /turf/open/floor/plating) && !can_see(controller.pawn, my_target, 5)) return FALSE - var/static/list/blacklist_areas = typecacheof(list( /area/space, /area/station/maintenance, @@ -202,129 +160,133 @@ var/turf_area = get_area(my_target) return !(is_type_in_typecache(turf_area, blacklist_areas)) -///subtree to fix hull breaches -/datum/ai_planning_subtree/replace_floors/breaches - floor_key = BB_BREACHED_FLOOR - needed_tile_type = /obj/item/stack/tile/iron - type_of_turf = list(/turf/open/space) - required_flag = REPAIRBOT_FIX_BREACHES - search_behavior = /datum/ai_behavior/bot_search/valid_plateless_turf/breached +/// Breach variant: searches /turf/open/space instead, requires REPAIRBOT_FIX_BREACHES flag. +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/breached -///exists as to not conflict with the base turf searching behavior cause of how the queue system works... -/datum/ai_behavior/bot_search/valid_plateless_turf/breached +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/breached/setup(datum/ai_controller/controller) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_FIX_BREACHES)) + return FALSE + if(!locate(/obj/item/stack/tile/iron) in bot_pawn) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/breached/get_turf_type_filter() + return typecacheof(list(/turf/open/space)) -///subtree to build girders -/datum/ai_planning_subtree/build_girder +/datum/bt_node/ai_behavior/bot_search/valid_girder + time_between_perform = 5 SECONDS -/datum/ai_planning_subtree/build_girder/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) +/datum/bt_node/ai_behavior/bot_search/valid_girder/setup(datum/ai_controller/controller) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_FIX_GIRDERS)) + return FALSE + var/obj/item/stack/sheet/iron/my_iron = locate() in bot_pawn + if(isnull(my_iron) || my_iron.amount < 2) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/bot_search/valid_girder/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + var/static/list/searchable_girder = typecacheof(list(/obj/structure/girder)) + looking_for = searchable_girder + return ..() + +/datum/bt_node/ai_behavior/bot_search/valid_girder/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) + if(!istype(my_target, /obj/structure/girder)) + return FALSE + return isfloorturf(my_target.loc) + + +/datum/bt_node/ai_behavior/targeted_mob_ability/build_girder + maximum_distance = 1 + +/datum/bt_node/ai_behavior/targeted_mob_ability/build_girder/setup(datum/ai_controller/controller) var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn if(!(bot_pawn.repairbot_flags & REPAIRBOT_BUILD_GIRDERS)) - return + return FALSE var/obj/item/stack/rods/my_rods = locate() in bot_pawn if(isnull(my_rods) || my_rods.amount < 2) - return - var/datum/action/cooldown/ability = controller.blackboard[BB_GIRDER_BUILD_ABILITY] + return FALSE + var/datum/action/cooldown/ability = controller.blackboard[ability_key] if(!ability?.IsAvailable()) - return - if(controller.blackboard_key_exists(BB_GIRDER_TARGET)) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/build_girder, BB_GIRDER_BUILD_ABILITY, BB_GIRDER_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + return FALSE + return ..() - var/static/list/searchable_turfs = typecacheof(list(/turf/open)) - controller.queue_behavior(/datum/ai_behavior/bot_search/valid_wall_target, BB_GIRDER_TARGET, searchable_turfs, 5, 10, FALSE, TRUE) +/datum/bt_node/ai_behavior/targeted_mob_ability/build_girder/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + +/// Search for open turfs adjacent to space (valid girder build locations). +/datum/bt_node/ai_behavior/bot_search/valid_wall_target + turf_search = TRUE + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/bot_search/valid_wall_target/setup(datum/ai_controller/controller) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_BUILD_GIRDERS)) + return FALSE + var/obj/item/stack/rods/my_rods = locate() in bot_pawn + if(isnull(my_rods) || my_rods.amount < 2) + return FALSE + return TRUE -/datum/ai_behavior/bot_search/valid_wall_target - action_cooldown = 5 SECONDS +/datum/bt_node/ai_behavior/bot_search/valid_wall_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + var/static/list/searchable_turfs = typecacheof(list(/turf/open)) + looking_for = searchable_turfs + return ..() -/datum/ai_behavior/bot_search/valid_wall_target/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/my_target) +/datum/bt_node/ai_behavior/bot_search/valid_wall_target/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/my_target) + if(!istype(my_target, /turf/open)) + return FALSE if(istype(get_area(my_target), /area/space) || isgroundlessturf(my_target) || my_target.is_blocked_turf()) return FALSE - var/static/list/blacklist_objects = list( + var/static/list/blacklist_objects = typecacheof(list( /obj/machinery/door, /obj/structure/grille, - ) - + )) for(var/atom/contents in my_target) if(is_type_in_typecache(contents, blacklist_objects)) return FALSE - var/turf/adjacent_turfs = get_adjacent_open_turfs(my_target) for(var/turf/possible_spaced_turf as anything in adjacent_turfs) if(isspaceturf(possible_spaced_turf) && istype(get_area(possible_spaced_turf), /area/space)) return TRUE return FALSE -/datum/ai_behavior/targeted_mob_ability/build_girder - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION +/datum/bt_node/ai_behavior/bot_search/valid_grille_target + time_between_perform = 5 SECONDS -/datum/ai_behavior/targeted_mob_ability/build_girder/setup(datum/ai_controller/controller, ability_key, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) +/datum/bt_node/ai_behavior/bot_search/valid_grille_target/setup(datum/ai_controller/controller) + var/mob/living/basic/bot/repairbot/bot_pawn = controller.pawn + if(!(bot_pawn.repairbot_flags & REPAIRBOT_REPLACE_WINDOWS)) return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/targeted_mob_ability/build_girder/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) - . = ..() - controller.clear_blackboard_key(target_key) + if(!locate(/obj/item/stack/sheet/glass) in bot_pawn) + return FALSE + return TRUE -///subtree to place glass on windows -/datum/ai_planning_subtree/replace_window - -/datum/ai_planning_subtree/replace_window/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/repairbot/living_pawn = controller.pawn - if(!(living_pawn.repairbot_flags & REPAIRBOT_REPLACE_WINDOWS)) - return - if(!locate(/obj/item/stack/sheet/glass) in living_pawn) - return - if(controller.blackboard_key_exists(BB_WINDOW_FRAMETARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_WINDOW_FRAMETARGET) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/bot_search/valid_grille_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/static/list/searchable_grilles = typecacheof(list(/obj/structure/grille)) - controller.queue_behavior(/datum/ai_behavior/bot_search/valid_grille_target, BB_WINDOW_FRAMETARGET, searchable_grilles) + looking_for = searchable_grilles + return ..() -/datum/ai_behavior/bot_search/valid_grille_target/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/structure/my_target) +/datum/bt_node/ai_behavior/bot_search/valid_grille_target/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/structure/my_target) + if(!istype(my_target, /obj/structure/grille)) + return FALSE if(locate(/obj/structure/window) in get_turf(my_target)) return FALSE return (!istype(get_area(my_target), /area/space)) +/datum/bt_node/ai_behavior/bot_search/valid_window_fix + time_between_perform = 5 SECONDS -///subtree to place iron on girders -/datum/ai_planning_subtree/wall_girder - -/datum/ai_planning_subtree/wall_girder/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/bot/repairbot/living_pawn = controller.pawn - if(!(living_pawn.repairbot_flags & REPAIRBOT_FIX_GIRDERS)) - return - var/obj/item/stack/sheet/iron/my_iron = locate() in living_pawn - if(isnull(my_iron) || my_iron.amount < 2) - return - if(controller.blackboard_key_exists(BB_GIRDER_TO_WALL_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_GIRDER_TO_WALL_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - var/static/list/searchable_girder = typecacheof(list(/obj/structure/girder)) - controller.queue_behavior(/datum/ai_behavior/bot_search/valid_girder, BB_GIRDER_TO_WALL_TARGET, searchable_girder) - -/datum/ai_behavior/bot_search/valid_girder - action_cooldown = 5 SECONDS - -/datum/ai_behavior/bot_search/valid_girder/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) - return isfloorturf(my_target.loc) - -///subtree to repair machines with welders -/datum/ai_planning_subtree/fix_window - -/datum/ai_planning_subtree/fix_window/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_WELDER_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_interact, BB_WELDER_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/bot_search/valid_window_fix/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) var/static/list/searchable_objects = typecacheof(list(/obj/structure/window)) - controller.queue_behavior(/datum/ai_behavior/bot_search/valid_window_fix, BB_WELDER_TARGET, searchable_objects) + looking_for = searchable_objects + return ..() -/datum/ai_behavior/bot_search/valid_window_fix - action_cooldown = 5 SECONDS - -/datum/ai_behavior/bot_search/valid_window_fix/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) +/datum/bt_node/ai_behavior/bot_search/valid_window_fix/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) + if(!istype(my_target, /obj/structure/window)) + return FALSE return (my_target.get_integrity() < my_target.max_integrity || !my_target.anchored) #undef REPAIRBOT_SPEECH_TIMER diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.json b/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.json new file mode 100644 index 000000000000..3970e4e04afb --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_emagged.bt.json @@ -0,0 +1,71 @@ +{ + "dm_type": "/datum/bt_node/subtree/repairbot_emagged", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_ROBOT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ROBOT_TARGET", + "required_dist": 0, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_interact/tip_robot", + "vars": { + "target_key": "BB_ROBOT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grab_target", + "vars": { + "target_key": "BB_ROBOT_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_DECONSTRUCT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DECONSTRUCT_TARGET", + "required_dist": 0, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_interact", + "vars": { + "target_key": "BB_DECONSTRUCT_TARGET" + } + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.json b/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.json new file mode 100644 index 000000000000..4e7a2f41aab3 --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_find_target.bt.json @@ -0,0 +1,151 @@ +{ + "dm_type": "/datum/bt_node/subtree/repairbot_find_target", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_is_emagged", + "vars": { + "invert": false + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_robot", + "vars": { + "time_between_perform": 0, + "target_key": "BB_ROBOT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/deconstructable", + "vars": { + "target_key": "BB_DECONSTRUCT_TARGET", + "minimum_distance": 1 + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "invert": true, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf/breached", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/refillable_target", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_grille_target", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_plateless_turf", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_window_fix", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_girder", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_REPAIRBOT_INTERACTION_TYPE", + "value": "REPAIRBOT_INTERACTION_INTERACT" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/cancel_current_plan" + } + ] + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_search/valid_wall_target", + "vars": { + "time_between_perform": 0, + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_REPAIRBOT_INTERACTION_TYPE", + "value": "REPAIRBOT_INTERACTION_BUILD_GIRDERS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/cancel_current_plan" + } + ] + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.json b/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.json new file mode 100644 index 000000000000..43139015fa58 --- /dev/null +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_repair_target.bt.json @@ -0,0 +1,88 @@ +{ + "dm_type": "/datum/bt_node/subtree/repairbot_repair_target", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_is_emagged", + "vars": { + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/repairbot_emagged" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bot_is_emagged", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_REPAIRBOT_INTERACTION_TYPE", + "value": "REPAIRBOT_INTERACTION_INTERACT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/bot_interact", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_REPAIRBOT_INTERACTION_TYPE", + "value": "REPAIRBOT_INTERACTION_BUILD_GIRDERS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/build_girder", + "vars": { + "ability_key": "BB_GIRDER_BUILD_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + } + } + ] + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] +} diff --git a/code/modules/mob/living/basic/bots/secbot/secbot.bt.json b/code/modules/mob/living/basic/bots/secbot/secbot.bt.json new file mode 100644 index 000000000000..e7908f713eff --- /dev/null +++ b/code/modules/mob/living/basic/bots/secbot/secbot.bt.json @@ -0,0 +1,85 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/secbot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/secbot/secbot.dm b/code/modules/mob/living/basic/bots/secbot/secbot.dm index 46b2835764c0..df9633d92656 100644 --- a/code/modules/mob/living/basic/bots/secbot/secbot.dm +++ b/code/modules/mob/living/basic/bots/secbot/secbot.dm @@ -198,7 +198,7 @@ /mob/living/basic/bot/secbot/proc/on_entered(datum/source, atom/movable/to_be_tripped) SIGNAL_HANDLER - var/mob/living/possible_target = ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/mob/living/possible_target = ai_controller.blackboard[BB_CURRENT_TARGET] if(!has_gravity() || !ismob(to_be_tripped) || !possible_target) return var/mob/living/carbon/tripped_mob = to_be_tripped diff --git a/code/modules/mob/living/basic/bots/secbot/secbot_ai.dm b/code/modules/mob/living/basic/bots/secbot/secbot_ai.dm index 6aadd73fd218..b8afdb678a31 100644 --- a/code/modules/mob/living/basic/bots/secbot/secbot_ai.dm +++ b/code/modules/mob/living/basic/bots/secbot/secbot_ai.dm @@ -1,30 +1,24 @@ /datum/ai_controller/basic_controller/bot/secbot + behavior_tree_json = "code/modules/mob/living/basic/bots/secbot/secbot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/secbot, BB_UNREACHABLE_LIST_COOLDOWN = 1 MINUTES, BB_ALWAYS_IGNORE_FACTION = TRUE, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/arrest_target, - /datum/ai_planning_subtree/find_patrol_beacon, - ) reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, ) -/datum/targeting_strategy/basic/secbot/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/secbot/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) var/datum/ai_controller/basic_controller/bot/my_controller = living_mob.ai_controller if(isnull(my_controller)) return FALSE if(!ishuman(the_target) || LAZYACCESS(my_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], the_target)) return FALSE var/mob/living/carbon/human/human_target = the_target - if(human_target.handcuffed || human_target.stat != CONSCIOUS) + if(human_target.handcuffed || IS_UNCONSCIOUS_OR_CRIT(human_target)) return FALSE if(locate(human_target) in my_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]) return TRUE @@ -37,13 +31,12 @@ my_controller.set_blackboard_key(BB_CURRENT_CRIMINAL_ASSESSMENT, assessed_threat) return (assessed_threat > THREAT_ASSESS_DANGEROUS) - /datum/ai_controller/basic_controller/bot/secbot/TryPossessPawn(atom/new_pawn) . = ..() if(. & AI_CONTROLLER_INCOMPATIBLE) return - RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(on_target_set)) - RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BASIC_MOB_CURRENT_TARGET), PROC_REF(on_clear_target)) + RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET), PROC_REF(on_target_set)) + RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_CURRENT_TARGET), PROC_REF(on_clear_target)) /datum/ai_controller/basic_controller/bot/secbot/proc/on_target_set() SIGNAL_HANDLER @@ -61,31 +54,3 @@ /datum/ai_controller/basic_controller/bot/secbot/proc/on_clear_target() SIGNAL_HANDLER clear_blackboard_key(BB_CURRENT_CRIMINAL_ASSESSMENT) - -/datum/ai_planning_subtree/arrest_target - ///what behavior do we use when arresting? - var/datum/ai_behavior/arrest_behavior = /datum/ai_behavior/basic_melee_attack/interact_once/bot - -/datum/ai_planning_subtree/arrest_target/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - var/mob/living/carbon/my_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(my_target) || !istype(my_target) || my_target.handcuffed) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - return - - var/mob/living/basic/bot/secbot/my_bot = controller.pawn - var/bot_flags = my_bot.security_mode_flags - if(my_target.IsParalyzed() && !(bot_flags & SECBOT_HANDCUFF_TARGET)) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - return - - controller.queue_behavior(arrest_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/basic_melee_attack/interact_once/bot - movement_behavior = /datum/ai_movement/basic_avoidance - -/datum/ai_behavior/basic_melee_attack/interact_once/bot/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) - var/mob/living/carbon/human/human_target = controller.blackboard[target_key] - if(!isnull(human_target) && human_target.handcuffed) - controller.remove_from_blackboard_lazylist_key(BB_BASIC_MOB_RETALIATE_LIST, human_target) - return ..() diff --git a/code/modules/mob/living/basic/bots/secbot/super_beepsky.dm b/code/modules/mob/living/basic/bots/secbot/super_beepsky.dm index 1f82db49c7a5..9e0410b467e3 100644 --- a/code/modules/mob/living/basic/bots/secbot/super_beepsky.dm +++ b/code/modules/mob/living/basic/bots/secbot/super_beepsky.dm @@ -45,7 +45,7 @@ /mob/living/basic/bot/secbot/grievous/proc/block_bullets(datum/source, obj/projectile/hitting_projectile) SIGNAL_HANDLER - if(stat != CONSCIOUS ) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return NONE if(!sword_active || !prob(block_chance)) @@ -57,7 +57,7 @@ /mob/living/basic/bot/secbot/grievous/on_entered(datum/source, atom/movable/movable_target) . = ..() - if(!ismob(movable_target) || !ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] == movable_target) + if(!ismob(movable_target) || !ai_controller.blackboard[BB_CURRENT_TARGET] == movable_target) return visible_message(span_warning("[src] flails his swords and cuts [movable_target]!")) playsound(src, 'sound/mobs/non-humanoids/beepsky/beepskyspinsabre.ogg' , 100, TRUE, -1) diff --git a/code/modules/mob/living/basic/bots/secbot/super_beepsky_ai.dm b/code/modules/mob/living/basic/bots/secbot/super_beepsky_ai.dm index effd9a98cab6..93ab2932f48c 100644 --- a/code/modules/mob/living/basic/bots/secbot/super_beepsky_ai.dm +++ b/code/modules/mob/living/basic/bots/secbot/super_beepsky_ai.dm @@ -1,16 +1,8 @@ /datum/ai_controller/basic_controller/bot/secbot/super_beepsky - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_patrol_beacon, - ) - /datum/ai_controller/basic_controller/bot/secbot/super_beepsky/on_target_set() . = ..() var/mob/living/basic/bot/secbot/grievous/super_beeps = pawn if(!super_beeps.sword_active) INVOKE_ASYNC(super_beeps.weapon, TYPE_PROC_REF(/obj/item, attack_self), super_beeps) - super_beeps.visible_message("[super_beeps] points at [blackboard[BB_BASIC_MOB_CURRENT_TARGET]]!") + super_beeps.visible_message("[super_beeps] points at [blackboard[BB_CURRENT_TARGET]]!") diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.json b/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.json new file mode 100644 index 000000000000..8d7572950518 --- /dev/null +++ b/code/modules/mob/living/basic/bots/vibebot/vibebot.bt.json @@ -0,0 +1,74 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bot/vibebot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_respond_to_summon" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_VIBEBOT_PARTY_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_VIBEBOT_PARTY_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/vibebot_party", + "vars": { + "ability_key": "BB_VIBEBOT_PARTY_ABILITY", + "target_key": "BB_VIBEBOT_PARTY_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/bot_patrol" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_VIBEBOT_PARTY_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/capable_human/party_friend", + "ignore_list_key": "BB_TEMPORARY_IGNORE_LIST", + "vision_range": 5, + "time_between_perform": "5 SECONDS" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm index 77773746b08d..0b1d3d5a580b 100644 --- a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm +++ b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm @@ -1,16 +1,11 @@ /datum/ai_controller/basic_controller/bot/vibebot + behavior_tree_json = "code/modules/mob/living/basic/bots/vibebot/vibebot.bt.json" blackboard = list( BB_UNREACHABLE_LIST_COOLDOWN = 2 MINUTES, BB_VIBEBOT_HAPPY_SONG = VIBEBOT_CHEER_SONG, BB_VIBEBOT_GRIM_SONG = VIBEBOT_GRIM_MUSIC, BB_VIBEBOT_BIRTHDAY_SONG = VIBEBOT_HAPPY_BIRTHDAY, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/find_party_friends, - /datum/ai_planning_subtree/find_patrol_beacon, - ) reset_keys = list( BB_BEACON_TARGET, BB_PREVIOUS_BEACON_TARGET, @@ -45,43 +40,36 @@ song.start_playing(pawn) addtimer(CALLBACK(song, TYPE_PROC_REF(/datum/song, stop_playing)), 10 SECONDS) //in 10 seconds, stop playing music -///subtree we use to find party friends in general -/datum/ai_planning_subtree/find_party_friends - -/datum/ai_planning_subtree/find_party_friends/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/static/list/type_to_search = typecacheof(list(/mob/living/carbon/human)) - if(!controller.blackboard_key_exists(BB_VIBEBOT_PARTY_TARGET)) - controller.queue_behavior(/datum/ai_behavior/bot_search/party_friends, BB_VIBEBOT_PARTY_TARGET, type_to_search) - return - - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party, BB_VIBEBOT_PARTY_ABILITY, BB_VIBEBOT_PARTY_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -///behavior we use to party with people -/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party - behavior_flags = AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_REQUIRE_MOVEMENT -/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/setup(datum/ai_controller/controller, ability_key, target_key) +/// Valid if the target is a conscious human who's in a bad mood or having a birthday someone who could use cheering up. +/datum/targeting_strategy/capable_human/party_friend/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) + if(!.) return FALSE - set_movement_target(controller, target) + var/mob/living/carbon/human/human_target = target + return human_target.mob_mood?.mood_level < MOOD_LEVEL_NEUTRAL || HAS_TRAIT(human_target, TRAIT_BIRTHDAY_BOY) -/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, ability_key, target_key) - var/atom/target = controller.blackboard[target_key] - controller.add_to_blacklist(target) - if(succeeded) - var/mob/living/living_pawn = controller.pawn - living_pawn.manual_emote("celebrates with [target]!") - living_pawn.emote("flip") - return ..() +/datum/bt_node/ai_behavior/vibebot_party + var/ability_key + var/target_key -///behavior that searches for party friends -/datum/ai_behavior/bot_search/party_friends - action_cooldown = 5 SECONDS +/datum/bt_node/ai_behavior/vibebot_party/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller) + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(get_dist(controller.pawn, living_target) > 1) + return AI_BEHAVIOR_INSTANT + var/datum/action/cooldown/ability = controller.blackboard[ability_key] + if(ability) + INVOKE_ASYNC(ability, TYPE_PROC_REF(/datum/action, Trigger), living_target) + var/mob/living/living_pawn = controller.pawn + living_pawn.manual_emote("celebrates with [living_target]!") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, emote), "flip") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/bot_search/party_friends/valid_target(datum/ai_controller/basic_controller/bot/controller, mob/living/carbon/human/my_target) - if(my_target.stat != CONSCIOUS || isnull(my_target.mind)) - return FALSE - return (my_target.mob_mood.mood_level < MOOD_LEVEL_NEUTRAL || HAS_TRAIT(my_target, TRAIT_BIRTHDAY_BOY)) +/datum/bt_node/ai_behavior/vibebot_party/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(!isnull(target)) + controller.add_to_blacklist(target) + controller.clear_blackboard_key(target_key) diff --git a/code/modules/mob/living/basic/clown/clown.bt.json b/code/modules/mob/living/basic/clown/clown.bt.json new file mode 100644 index 000000000000..bbfc9f477e5a --- /dev/null +++ b/code/modules/mob/living/basic/clown/clown.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/clown", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/clown/clown_ai.dm b/code/modules/mob/living/basic/clown/clown_ai.dm index dfa9b71a40eb..091e14ef167c 100644 --- a/code/modules/mob/living/basic/clown/clown_ai.dm +++ b/code/modules/mob/living/basic/clown/clown_ai.dm @@ -1,17 +1,11 @@ /datum/ai_controller/basic_controller/clown + behavior_tree_json = "code/modules/mob/living/basic/clown/clown.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_BASIC_MOB_SPEAK_LINES = null, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/blackboard, - ) /datum/ai_controller/basic_controller/clown/murder blackboard = list( diff --git a/code/modules/mob/living/basic/cult/constructs/artificer.bt.json b/code/modules/mob/living/basic/cult/constructs/artificer.bt.json new file mode 100644 index 000000000000..f0ed947ff493 --- /dev/null +++ b/code/modules/mob/living/basic/cult/constructs/artificer.bt.json @@ -0,0 +1,83 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/artificer", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1 + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/cult/constructs/construct_ai.dm b/code/modules/mob/living/basic/cult/constructs/construct_ai.dm index c7ea47a83ae6..6f333bd20c4a 100644 --- a/code/modules/mob/living/basic/cult/constructs/construct_ai.dm +++ b/code/modules/mob/living/basic/cult/constructs/construct_ai.dm @@ -5,6 +5,7 @@ * If there is no one to heal, they will run away from any non-allied mobs. */ /datum/ai_controller/basic_controller/artificer + behavior_tree_json = "code/modules/mob/living/basic/cult/constructs/artificer.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/same_faction/construct, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, @@ -12,14 +13,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_wounded_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - ) /** * Juggernauts @@ -27,19 +20,13 @@ * Juggernauts slowly walk toward non-allied mobs and pummel them to death. */ /datum/ai_controller/basic_controller/juggernaut + behavior_tree_json = "code/modules/mob/living/basic/cult/constructs/juggernaut.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /** * Proteons @@ -47,6 +34,7 @@ * Proteons perform cowardly hit-and-run attacks, fleeing melee when struck but returning to fight again. */ /datum/ai_controller/basic_controller/proteon + behavior_tree_json = "code/modules/mob/living/basic/cult/constructs/proteon.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, @@ -54,15 +42,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /** * Wraiths @@ -70,25 +49,17 @@ * Wraiths seek out the most injured non-allied mob to beat to death. */ /datum/ai_controller/basic_controller/wraith + behavior_tree_json = "code/modules/mob/living/basic/cult/constructs/wraith.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_wounded_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - /// Targeting strategy that will only allow mobs that constructs can heal. /datum/targeting_strategy/basic/same_faction/construct target_wounded_key = BB_TARGET_WOUNDED_ONLY -/datum/targeting_strategy/basic/same_faction/construct/can_attack(mob/living/living_mob, atom/the_target, vision_range, check_faction = TRUE) +/datum/targeting_strategy/basic/same_faction/construct/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) if(isconstruct(the_target) || istype(the_target, /mob/living/basic/shade)) return ..() return FALSE diff --git a/code/modules/mob/living/basic/cult/constructs/harvester.dm b/code/modules/mob/living/basic/cult/constructs/harvester.dm index 1c36453b1847..fe67813225ce 100644 --- a/code/modules/mob/living/basic/cult/constructs/harvester.dm +++ b/code/modules/mob/living/basic/cult/constructs/harvester.dm @@ -34,7 +34,7 @@ /datum/component/amputating_limbs,\ surgery_time = 0,\ surgery_verb = "slicing",\ - minimum_stat = CONSCIOUS,\ + minimum_stat = STABLE,\ ) var/datum/action/innate/seek_prey/seek = new(src) seek.Grant(src) @@ -185,7 +185,7 @@ /datum/component/amputating_limbs,\ surgery_time = 1.5 SECONDS,\ surgery_verb = "slicing",\ - minimum_stat = CONSCIOUS,\ + minimum_stat = STABLE,\ pre_hit_callback = CALLBACK(src, PROC_REF(is_cultist_handler)),\ ) AddComponent(/datum/component/damage_aura,\ diff --git a/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.json b/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.json new file mode 100644 index 000000000000..9cbc8c4bd8e4 --- /dev/null +++ b/code/modules/mob/living/basic/cult/constructs/juggernaut.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/juggernaut", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" +} diff --git a/code/modules/mob/living/basic/cult/constructs/proteon.bt.json b/code/modules/mob/living/basic/cult/constructs/proteon.bt.json new file mode 100644 index 000000000000..df911230d81f --- /dev/null +++ b/code/modules/mob/living/basic/cult/constructs/proteon.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/proteon", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_brawler_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/cult/constructs/wraith.bt.json b/code/modules/mob/living/basic/cult/constructs/wraith.bt.json new file mode 100644 index 000000000000..139ac7447819 --- /dev/null +++ b/code/modules/mob/living/basic/cult/constructs/wraith.bt.json @@ -0,0 +1,83 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/wraith", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1 + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/most_wounded", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/cult/shade.dm b/code/modules/mob/living/basic/cult/shade.dm index 41db06e178d3..a929bc26736c 100644 --- a/code/modules/mob/living/basic/cult/shade.dm +++ b/code/modules/mob/living/basic/cult/shade.dm @@ -73,9 +73,10 @@ SSblackbox.record_feedback("tally", "cult_shade_suicided", 1) ..() -/mob/living/basic/shade/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(item, /obj/item/soulstone)) - var/obj/item/soulstone/stone = item - stone.capture_shade(src, user) - else - . = ..() +/mob/living/basic/shade/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/soulstone)) + return ..() + + var/obj/item/soulstone/stone = tool + stone.capture_shade(src, user) + return ITEM_INTERACT_SUCCESS diff --git a/code/modules/mob/living/basic/cytology/vatbeast.bt.json b/code/modules/mob/living/basic/cytology/vatbeast.bt.json new file mode 100644 index 000000000000..2e5924a2fdc0 --- /dev/null +++ b/code/modules/mob/living/basic/cytology/vatbeast.bt.json @@ -0,0 +1,137 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/vatbeast", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_at_least", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_BASIC_MOB_HAS_TARGET_TIME", + "minimum": "10 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/melee", + "vars": { + "ability_key": "BB_GENERIC_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_FOOD", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_typed/from_bb_key/basic_foods" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/cytology/vatbeast.dm b/code/modules/mob/living/basic/cytology/vatbeast.dm index a52411f0d824..b0de4c1d7548 100644 --- a/code/modules/mob/living/basic/cytology/vatbeast.dm +++ b/code/modules/mob/living/basic/cytology/vatbeast.dm @@ -47,7 +47,7 @@ var/datum/action/cooldown/tentacle_slap/slapper = new (src) slapper.Grant(src) - ai_controller.set_blackboard_key(BB_TARGETED_ACTION, slapper) + ai_controller.set_blackboard_key(BB_GENERIC_ACTION, slapper) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(enjoyed_food)) /mob/living/basic/vatbeast/tamed(mob/living/tamer, obj/item/food) @@ -64,34 +64,12 @@ /// Attack people and slap them /datum/ai_controller/basic_controller/vatbeast + behavior_tree_json = "code/modules/mob/living/basic/cytology/vatbeast.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/targeted_mob_ability/vatbeast_slap, - /datum/ai_planning_subtree/basic_melee_attack_subtree - ) - -/// Only do this if we are adjacent to target and have been mad at the same guy for at least 10 seconds -/// That slap REALLY hurts -/datum/ai_planning_subtree/targeted_mob_ability/vatbeast_slap - operational_datums = list(/datum/component/ai_target_timer) - -/datum/ai_planning_subtree/targeted_mob_ability/vatbeast_slap/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[target_key] - if (!isliving(target) || !controller.pawn.Adjacent(target)) - return - var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0 - if (time_on_target < 10 SECONDS) - return - return ..() /// Ability that allows the owner to slap other mobs a short distance away. /// For vatbeats, this ability is shared with the rider. diff --git a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm index 409035c9683b..ccd0a0b04da0 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm @@ -55,7 +55,7 @@ /datum/pet_command/free, /datum/pet_command/beehive/enter, /datum/pet_command/beehive/exit, - /datum/pet_command/follow/bee, + /datum/pet_command/follow, /datum/pet_command/attack/swirl, /datum/pet_command/scatter, ) diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee.bt.json b/code/modules/mob/living/basic/farm_animals/bee/bee.bt.json new file mode 100644 index 000000000000..727a2cfd8d52 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/bee.bt.json @@ -0,0 +1,86 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bee", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_hive" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/transition_hive_status" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pollinate_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enter_exit_hive" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.85 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_HYDRO", + "target_source": "/datum/target_source/oview_single_type/hydroponics", + "targeting_strategy": "/datum/targeting_strategy/pollinatable_hydro", + "vision_range": 10, + "time_between_perform": "10 SECONDS" + } + } + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_TARGET_HOME" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_hive" + } + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm index 2a6ff31981c4..a5fdb64085f6 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm @@ -1,87 +1,9 @@ /// if we have a hive, this will be our aggro distance #define AGGRO_DISTANCE_FROM_HIVE 2 -/datum/ai_behavior/hunt_target/pollinate - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/pollinate/target_caught(mob/living/hunter, obj/machinery/hydroponics/hydro_target) - var/datum/callback/callback = CALLBACK(hunter, TYPE_PROC_REF(/mob/living/basic/bee, pollinate), hydro_target) - callback.Invoke() - -/datum/ai_behavior/find_hunt_target/pollinate - action_cooldown = 10 SECONDS - -/datum/ai_behavior/find_hunt_target/pollinate/valid_dinner(mob/living/source, obj/machinery/hydroponics/dinner, radius) - if(!dinner.can_bee_pollinate()) - return FALSE - return can_see(source, dinner, radius) - -/datum/ai_behavior/enter_exit_hive - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 10 SECONDS - -/datum/ai_behavior/enter_exit_hive/setup(datum/ai_controller/controller, target_key, attack_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/enter_exit_hive/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key) - var/obj/structure/beebox/current_home = controller.blackboard[target_key] - var/atom/attack_target = controller.blackboard[attack_key] - - if(attack_target) // forget about who we attacking when we go home - controller.clear_blackboard_key(attack_key) - - controller.ai_interact(target = current_home, combat_mode = FALSE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/inhabit_hive - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/inhabit_hive/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/inhabit_hive/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/obj/structure/beebox/potential_home = controller.blackboard[target_key] - var/mob/living/bee_pawn = controller.pawn - - if(!potential_home.habitable(bee_pawn)) //the house become full before we get to it - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target = potential_home, combat_mode = FALSE) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/inhabit_hive/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(!succeeded) - controller.clear_blackboard_key(target_key) //failed to make it our home so find another - -/datum/ai_behavior/find_and_set/bee_hive - action_cooldown = 10 SECONDS - -/datum/ai_behavior/find_and_set/bee_hive/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/valid_hives = list() - var/mob/living/bee_pawn = controller.pawn - - if(istype(bee_pawn.loc, /obj/structure/beebox)) - return bee_pawn.loc //for premade homes - - for(var/obj/structure/beebox/potential_home in oview(search_range, bee_pawn)) - if(!potential_home.habitable(bee_pawn)) - continue - valid_hives += potential_home - - if(valid_hives.len) - return pick(valid_hives) /datum/targeting_strategy/basic/bee -/datum/targeting_strategy/basic/bee/can_attack(mob/living/owner, atom/target, vision_range) +/datum/targeting_strategy/basic/bee/is_valid_target(mob/living/owner, atom/target, vision_range, datum/ai_controller/controller = null) if(!isliving(target)) return FALSE . = ..() @@ -104,14 +26,6 @@ return !(mob_target.bee_friendly()) -///pet commands -/datum/pet_command/follow/bee - ///the behavior we use to follow - follow_behavior = /datum/ai_behavior/pet_follow_friend/bee - -/datum/ai_behavior/pet_follow_friend/bee - required_distance = 0 - ///swirl around the owner in menacing fashion /datum/pet_command/attack/swirl command_name = "Swirl" @@ -138,49 +52,7 @@ /datum/pet_command/attack/swirl/execute_action(datum/ai_controller/controller) if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) return ..() - controller.queue_behavior(/datum/ai_behavior/swirl_around_target, BB_SWARM_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/swirl_around_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM - required_distance = 0 - ///chance to swirl - var/swirl_chance = 60 - -/datum/ai_behavior/swirl_around_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/swirl_around_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - var/mob/living/living_pawn = controller.pawn - - if(QDELETED(target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - - if(get_dist(target, living_pawn) > 1) - set_movement_target(controller, target) - return AI_BEHAVIOR_DELAY - - if(!SPT_PROB(swirl_chance, seconds_per_tick)) - return AI_BEHAVIOR_DELAY - - var/list/possible_turfs = list() - - for(var/turf/possible_turf in oview(2, target)) - if(possible_turf.is_blocked_turf(source_atom = living_pawn)) - continue - possible_turfs += possible_turf - - if(!length(possible_turfs)) - return AI_BEHAVIOR_DELAY - - if(isnull(controller.movement_target_source) || controller.movement_target_source == type) - set_movement_target(controller, pick(possible_turfs)) - return AI_BEHAVIOR_DELAY + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/swirl) /datum/pet_command/beehive @@ -205,9 +77,7 @@ return /datum/pet_command/beehive/execute_action(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/enter_exit_hive, BB_CURRENT_HOME) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/beehive) /datum/pet_command/beehive/enter command_name = "Enter beehive" @@ -237,11 +107,7 @@ set_command_target(parent, commander) /datum/pet_command/scatter/execute_action(datum/ai_controller/controller) - controller.queue_behavior(/datum/ai_behavior/run_away_from_target/scatter, BB_CURRENT_PET_TARGET) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/scatter) -/datum/ai_behavior/run_away_from_target/scatter - run_distance = 4 #undef AGGRO_DISTANCE_FROM_HIVE diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm index 3af1bb7e52c3..4047ad5f6a54 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm @@ -6,22 +6,7 @@ ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_valid_home, - /datum/ai_planning_subtree/enter_exit_home, - /datum/ai_planning_subtree/find_and_hunt_target/pollinate, - /datum/ai_planning_subtree/simple_find_target/bee, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/simple_find_target/bee/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/hydro_target = controller.blackboard[BB_TARGET_HYDRO] - if(hydro_target) - return SUBTREE_RETURN_FINISH_PLANNING - return ..() + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/bee/bee.bt.json" /datum/ai_controller/basic_controller/queen_bee blackboard = list( @@ -30,70 +15,14 @@ ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_valid_home, - /datum/ai_planning_subtree/enter_exit_home/queen, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - - -/datum/ai_planning_subtree/find_valid_home - -/datum/ai_planning_subtree/find_valid_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/work_bee = controller.pawn - - var/obj/structure/beebox/current_home = controller.blackboard[BB_CURRENT_HOME] - - if(QDELETED(current_home)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/bee_hive, BB_CURRENT_HOME, /obj/structure/beebox) - return - - if(work_bee in current_home.bees) - return - - controller.queue_behavior(/datum/ai_behavior/inhabit_hive, BB_CURRENT_HOME) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_planning_subtree/enter_exit_home - ///chance we go back home - var/flyback_chance = 15 - ///chance we exit the home - var/exit_chance = 35 - -/datum/ai_planning_subtree/enter_exit_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - var/obj/structure/beebox/current_home = controller.blackboard[BB_CURRENT_HOME] - - if(QDELETED(current_home)) - return - - var/mob/living/bee_pawn = controller.pawn - var/action_prob = (bee_pawn.loc == current_home) ? exit_chance : flyback_chance - - if(!SPT_PROB(action_prob, seconds_per_tick)) - return + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.json" - controller.queue_behavior(/datum/ai_behavior/enter_exit_hive, BB_CURRENT_HOME, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING -//the queen spend more time in the hive -/datum/ai_planning_subtree/enter_exit_home/queen - flyback_chance = 85 - exit_chance = 5 +/datum/bt_node/subtree/pollinate_target + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.json" -/datum/ai_planning_subtree/find_and_hunt_target/pollinate - target_key = BB_TARGET_HYDRO - hunting_behavior = /datum/ai_behavior/hunt_target/pollinate - finding_behavior = /datum/ai_behavior/find_hunt_target/pollinate - hunt_targets = list(/obj/machinery/hydroponics) - hunt_range = 10 - hunt_chance = 85 +/datum/bt_node/subtree/find_hive + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.json" -/datum/ai_planning_subtree/find_and_hunt_target/pollinate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/atom_pawn = controller.pawn - if(!isturf(atom_pawn.loc)) - return - return ..() +/datum/bt_node/subtree/transition_hive_status + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.json" diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_bt.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_bt.dm new file mode 100644 index 000000000000..62da5e54ef5a --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_bt.dm @@ -0,0 +1,132 @@ + + +/** + * Searches for a valid beebox home. Skipped if the bee is already inside its home. + * Sets BB_CURRENT_HOME when found. + */ +/datum/bt_node/ai_behavior/find_hive + +/datum/bt_node/ai_behavior/find_hive/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/bee/bee_pawn = controller.pawn + var/obj/structure/beebox/current_home = controller.blackboard[BB_CURRENT_HOME] + + if(!QDELETED(current_home)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED // already have a home; let the inhabit sequence handle it + + if(istype(bee_pawn.loc, /obj/structure/beebox)) + controller.set_blackboard_key(BB_CURRENT_HOME, bee_pawn.loc) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + for(var/obj/structure/beebox/potential_home in oview(10, bee_pawn)) + if(!potential_home.habitable(bee_pawn)) + continue + controller.set_blackboard_key(BB_TARGET_HOME, potential_home) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + +/** + * Moves to BB_CURRENT_HOME and inhabits it (ai_interact). Clears BB_CURRENT_HOME on failure. + * Must be in range (adjacent) to work. Returns FAILURE if home is gone or full. + */ +/datum/bt_node/ai_behavior/inhabit_hive + time_between_perform = 10 SECONDS + +/datum/bt_node/ai_behavior/inhabit_hive/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/bee/bee_pawn = controller.pawn + var/obj/structure/beebox/home = controller.blackboard[BB_CURRENT_HOME] + if(QDELETED(home)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!home.habitable(bee_pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!bee_pawn.Adjacent(home)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), home, FALSE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/inhabit_hive/finish_action(datum/ai_controller/controller, succeeded, ...) + . = ..() + if(!succeeded) + controller.clear_blackboard_key(BB_CURRENT_HOME) + +///Chance to leave or enter hive +/datum/bt_node/ai_behavior/enter_exit_hive + time_between_perform = 1 SECONDS + var/flyback_chance = 15 + var/exit_chance = 35 + +/datum/bt_node/ai_behavior/enter_exit_hive/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/structure/beebox/home = controller.blackboard[BB_CURRENT_HOME] + if(QDELETED(home)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/bee_pawn = controller.pawn + var/prob_to_use = (bee_pawn.loc == home) ? exit_chance : flyback_chance + + if(!SPT_PROB(prob_to_use, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(BB_WANTS_TO_TRANSITION_HIVE, TRUE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/// Queen variant: strongly prefers staying in hive. +/datum/bt_node/ai_behavior/enter_exit_hive/queen + flyback_chance = 85 + exit_chance = 5 + + +/// Pollinates the hydro tray at BB_TARGET_HYDRO. Must be adjacent. +/datum/bt_node/ai_behavior/pollinate_hydro + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/pollinate_hydro/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/machinery/hydroponics/tray = controller.blackboard[BB_TARGET_HYDRO] + var/mob/living/basic/bee/bee_pawn = controller.pawn + if(QDELETED(tray) || !bee_pawn.Adjacent(tray)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + bee_pawn.pollinate(tray) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/pollinate_hydro/finish_action(datum/ai_controller/controller, succeeded, ...) + . = ..() + controller.clear_blackboard_key(BB_TARGET_HYDRO) + + +/// Picks a random turf near BB_SWARM_TARGET and stores it in BB_SWIRL_TURF for move_to_target. +/// Succeeds when a turf is found; fails otherwise so the parent sequence fails and retries. +/datum/bt_node/ai_behavior/swirl_around_target + var/swirl_chance = 60 + +/datum/bt_node/ai_behavior/swirl_around_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[BB_SWARM_TARGET] + var/mob/living/bee_pawn = controller.pawn + if(QDELETED(target)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + if(!SPT_PROB(swirl_chance, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/list/possible_turfs = list() + for(var/turf/possible_turf in oview(2, target)) + if(!possible_turf.is_blocked_turf(source_atom = bee_pawn)) + possible_turfs += possible_turf + + if(!length(possible_turfs)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + controller.set_blackboard_key(BB_SWIRL_TURF, pick(possible_turfs)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + +/// Scatter command: runs away from BB_CURRENT_PET_TARGET then clears the command. +/datum/bt_node/subtree/pet_command/scatter + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_scatter.bt.json" + +/// Swirl command: swarm around BB_SWARM_TARGET continuously (no auto-clear). +/datum/bt_node/subtree/pet_command/swirl + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_swirl.bt.json" + +/// Beehive command: move to hive and enter/exit it. +/datum/bt_node/subtree/pet_command/beehive + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_beehive.bt.json" diff --git a/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.json b/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.json new file mode 100644 index 000000000000..f6f7442f9823 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/find_hive.bt.json @@ -0,0 +1,43 @@ +{ + "dm_type": "/datum/bt_node/subtree/find_hive", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_TARGET_HOME" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_HOME", + "invert": true + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_HOME", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/inhabit_hive" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TARGET_HOME" + } + } + ] + } + } +} diff --git a/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.json b/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.json new file mode 100644 index 000000000000..0489fac6fb78 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/pollinate_target.bt.json @@ -0,0 +1,27 @@ +{ + "dm_type": "/datum/bt_node/subtree/pollinate_target", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TARGET_HYDRO" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_HYDRO", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pollinate_hydro" + } + ] + } +} diff --git a/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.json b/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.json new file mode 100644 index 000000000000..be98e5038ef9 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/queen_bee.bt.json @@ -0,0 +1,58 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/queen_bee", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": false, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": false, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_hive" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pollinate_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/transition_hive_status" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enter_exit_hive/queen" + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_TARGET_HOME" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_hive" + } + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.json b/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.json new file mode 100644 index 000000000000..612f7d683ae8 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/bee/transition_hive_status.bt.json @@ -0,0 +1,45 @@ +{ + "dm_type": "/datum/bt_node/subtree/transition_hive_status", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_WANTS_TO_TRANSITION_HIVE", + "value": true + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_HOME", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_CURRENT_HOME" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_WANTS_TO_TRANSITION_HIVE", + "value": false + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.json b/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.json new file mode 100644 index 000000000000..4d7a67fca031 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/chicken/chick.bt.json @@ -0,0 +1,87 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/chick", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOUND_MOM", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_FOUND_MOM", + "min_distance": 2 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_MOM", + "required_dist": 1, + "finish_on_arrival": true + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.15 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/look_to_parent", + "vars": { + "parent_key": "BB_FOUND_MOM" + } + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOUND_MOM", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mom" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chick.dm b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm index 3c888145005a..b96c2ecb500d 100644 --- a/code/modules/mob/living/basic/farm_animals/chicken/chick.dm +++ b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm @@ -27,6 +27,8 @@ maxHealth = 3 pass_flags = PASSTABLE | PASSGRILLE | PASSMOB mob_size = MOB_SIZE_TINY + held_lh = 'icons/mob/inhands/pets_held_lh.dmi' + held_rh = 'icons/mob/inhands/pets_held_rh.dmi' gold_core_spawnable = FRIENDLY_SPAWN ai_controller = /datum/ai_controller/basic_controller/chick @@ -56,6 +58,7 @@ AddElement(/datum/element/pet_bonus, "chirp") AddElement(/datum/element/swabable, CELL_LINE_TABLE_CHICKEN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) + AddElement(/datum/element/can_be_held) if(!isnull(grow_as)) // we don't have a set time to grow up beyond whatever RNG dictates, and if we somehow get a client, all growth halts. AddComponent(\ @@ -71,22 +74,25 @@ /// We don't grow into a chicken if we're not conscious. /mob/living/basic/chick/proc/ready_to_grow() - return (stat == CONSCIOUS) + return !IS_UNCONSCIOUS_OR_CRIT(src) && !istype(loc, /obj/item/mob_holder) /// Variant of chick that just spawns in the holodeck so you can pet it. Doesn't grow up. /mob/living/basic/chick/permanent grow_as = null /datum/ai_controller/basic_controller/chick + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/chicken/chick.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FIND_MOM_TYPES = list(/mob/living/basic/chicken), + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Cherp.", "Cherp?", "Chirrup.", "Cheep!"), + BB_EMOTE_HEAR = list("cheeps."), + BB_EMOTE_SEE = list("pecks at the ground.","flaps her tiny wings."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/chicken/chick_peep.ogg'), + BB_SPEAK_CHANCE = 4, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/look_for_adult, - ) diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.json b/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.json new file mode 100644 index 000000000000..fc59585dab73 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/chicken", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_and_speak" +} diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm index 533f1c1a75b3..3d2bb5b09962 100644 --- a/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm +++ b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm @@ -31,6 +31,8 @@ GLOBAL_VAR_INIT(chicken_count, 0) maxHealth = 15 pass_flags = PASSTABLE | PASSMOB mob_size = MOB_SIZE_SMALL + held_lh = 'icons/mob/inhands/pets_held_lh.dmi' + held_rh = 'icons/mob/inhands/pets_held_rh.dmi' gold_core_spawnable = FRIENDLY_SPAWN ai_controller = /datum/ai_controller/basic_controller/chicken @@ -60,6 +62,8 @@ GLOBAL_VAR_INIT(chicken_count, 0) AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) AddElement(/datum/element/swabable, CELL_LINE_TABLE_CHICKEN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) AddElement(/datum/element/animal_variety, "chicken", pick("brown", "black", "white"), modify_pixels = TRUE) + AddElement(/datum/element/can_be_held) + AddComponent(\ /datum/component/egg_layer,\ /obj/item/food/egg/organic,\ @@ -90,17 +94,18 @@ GLOBAL_VAR_INIT(chicken_count, 0) ) /datum/ai_controller/basic_controller/chicken + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/chicken/chicken.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Cluck!", "BWAAAAARK BWAK BWAK BWAK!", "Bwaak bwak."), + BB_EMOTE_HEAR = list("clucks.", "croons."), + BB_EMOTE_SEE = list("pecks at the ground.", "flaps her wings viciously."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/chicken/clucks.ogg', 'sound/mobs/non-humanoids/chicken/bagawk.ogg'), + BB_SPEAK_CHANCE = 15, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/random_speech/chicken, - ) diff --git a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm index 3c6b1e81422d..0fc66e6d5d34 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm @@ -57,6 +57,7 @@ post_tipped_callback = CALLBACK(src, PROC_REF(after_cow_tipped))) AddElement(/datum/element/pet_bonus, "moo") AddElement(/datum/element/swabable, CELL_LINE_TABLE_COW, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + ADD_TRAIT(src, TRAIT_ABDUCTOR_QUICK_SCAN, INNATE_TRAIT) //cow ufo abduction ahaha setup_udder() setup_eating() . = ..() @@ -90,7 +91,7 @@ * tipper - the mob who tipped us */ /mob/living/basic/cow/proc/after_cow_tipped(mob/living/carbon/tipper) - addtimer(CALLBACK(src, PROC_REF(set_tip_react_blackboard), tipper), rand(10 SECONDS, 20 SECONDS)) + addtimer(CALLBACK(src, PROC_REF(set_tip_react_blackboard), tipper), rand(10 SECONDS, 20 SECONDS)) /* * We've been waiting long enough, we're going to tell our AI to begin pleading. diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow.bt.json b/code/modules/mob/living/basic/farm_animals/cow/cow.bt.json new file mode 100644 index 000000000000..f1a812b7e679 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/cow/cow.bt.json @@ -0,0 +1,48 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cow", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/tip_reaction" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_ai.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_ai.dm index df485197d334..ece35ce96579 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/cow_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_ai.dm @@ -1,15 +1,21 @@ /datum/ai_controller/basic_controller/cow + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/cow/cow.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_BASIC_MOB_TIP_REACTING = FALSE, BB_BASIC_MOB_TIPPER = null, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("moo?", "moo", "MOOOOOO"), + BB_EMOTE_HEAR = list("brays."), + BB_EMOTE_SEE = list("shakes her head."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/cow/cow.ogg'), + BB_SPEAK_CHANCE = 1, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/tip_reaction, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/random_speech/cow, - ) + +/// While we're tipped over, plead with nearby people instead of doing anything else. +/datum/bt_node/subtree/tip_reaction + behavior_tree_json = "code/datums/ai/basic_mobs/basic_subtrees/tip_reaction.bt.json" diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.json b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.json new file mode 100644 index 000000000000..dd56e0306bf3 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.json @@ -0,0 +1,96 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cow/moonicorn", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/tip_reaction" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm index 25879d07d5c0..5ee91b3a913f 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm @@ -40,27 +40,24 @@ APPLY_FACTION_AND_ALLIES_FROM(src, tamer) /datum/ai_controller/basic_controller/cow/moonicorn + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items/moonicorn, BB_BASIC_MOB_TIP_REACTING = FALSE, BB_BASIC_MOB_TIPPER = null, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/tip_reaction, - /datum/ai_planning_subtree/random_speech/cow, - //finds someone to kill - /datum/ai_planning_subtree/simple_find_target, - //...or something to eat, possibly. both types of target handled by melee attack subtree - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/basic_melee_attack_subtree, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("moo?", "moo", "MOOOOOO"), + BB_EMOTE_HEAR = list("brays."), + BB_EMOTE_SEE = list("shakes her head."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/cow/cow.ogg'), + BB_SPEAK_CHANCE = 1, + ), ) ///moonicorns will not attack people holding something that could tame them. /datum/targeting_strategy/basic/allow_items/moonicorn -/datum/targeting_strategy/basic/allow_items/moonicorn/can_attack(mob/living/living_mob, atom/the_target, vision_range) +/datum/targeting_strategy/basic/allow_items/moonicorn/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) . = ..() if(!.) return FALSE diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.json b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.json new file mode 100644 index 000000000000..300def27d686 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cow/wisdom", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/tip_reaction" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm index 4452ae3100f2..dc42b03791f7 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm @@ -12,6 +12,10 @@ /mob/living/basic/cow/wisdom/Initialize(mapload, granted_wisdom, granted_experience = 500, milked_reagent = null) src.milked_reagent = milked_reagent . = ..() + ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, list( + BB_EMOTE_SAY = GLOB.wisdoms, + BB_SPEAK_CHANCE = 15, + )) src.granted_wisdom = granted_wisdom if(!granted_wisdom) src.granted_wisdom = pick(GLOB.skill_types) @@ -28,20 +32,17 @@ return ..() /datum/ai_controller/basic_controller/cow/wisdom + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.bt.json" //don't give a targeting strategy blackboard = list( BB_BASIC_MOB_TIP_REACTING = FALSE, BB_BASIC_MOB_TIPPER = null, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/tip_reaction, - /datum/ai_planning_subtree/random_speech/cow/wisdom, + BB_BASIC_MOB_SPEAK_LINES = null, ) ///Give intense wisdom to the attacker if they're being friendly about it /mob/living/basic/cow/wisdom/attack_hand(mob/living/carbon/user, list/modifiers) - if(!stat && !user.combat_mode) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !user.combat_mode) to_chat(user, span_nicegreen("[src] whispers you some intense wisdoms and then disappears!")) user.mind?.adjust_experience(granted_wisdom, granted_experience) do_smoke(1, src, get_turf(src)) diff --git a/code/modules/mob/living/basic/farm_animals/deer/deer.bt.json b/code/modules/mob/living/basic/farm_animals/deer/deer.bt.json new file mode 100644 index 000000000000..a3620e19a8d3 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/deer/deer.bt.json @@ -0,0 +1,372 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/deer", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_STATIONARY_CAUSE", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/stop_and_stare" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_DEER_NEXT_REST_TIMER" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_DEER_TREEHOME" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEER_TREEHOME", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/deer_rest" + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DEER_PLAYFRIEND" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEER_PLAYFRIEND", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_DEER_PLAY_COOLDOWN", + "cooldown_duration": "10 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/deer_play" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_DEER_TREE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEER_TREE_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/deer_mark", + "vars": { + "target_key": "BB_DEER_TREE_TARGET", + "cooldown_key": "BB_DEER_MARK_COOLDOWN" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DEER_GRASS_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEER_GRASS_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/deer_graze", + "vars": { + "target_key": "BB_DEER_GRASS_TARGET", + "cooldown_key": "BB_DEER_GRAZE_COOLDOWN" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DEER_WATER_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEER_WATER_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/deer_drink", + "vars": { + "target_key": "BB_DEER_WATER_TARGET", + "cooldown_key": "BB_DEER_DRINK_COOLDOWN" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech_blackboard" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_DEER_PLAY_COOLDOWN" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.03 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_DEER_WANTS_TO_PLAY", + "value": true + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DEER_WANTS_TO_PLAY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DEER_PLAYFRIEND", + "target_source": "/datum/target_source/oview_single_type/deer_animals", + "targeting_strategy": "/datum/targeting_strategy/playable_deer" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_DEER_WANTS_TO_PLAY", + "value": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_DEER_TREEHOME" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_DEER_TREE_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_DEER_MARK_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DEER_TREE_TARGET", + "target_source": "/datum/target_source/oview_single_type/flora_tree", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_DEER_GRASS_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_DEER_GRAZE_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DEER_GRASS_TARGET", + "target_source": "/datum/target_source/range_turfs/typecache_visible/deer_grass", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_DEER_WATER_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_DEER_DRINK_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DEER_WATER_TARGET", + "target_source": "/datum/target_source/range_turfs/typecache_visible/deer_water", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_STATIONARY_CAUSE", + "target_source": "/datum/target_source/oview_typed/from_bb_key/stationary_targets", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm b/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm index a654e0c29d0e..0506827d589f 100644 --- a/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm @@ -1,156 +1,95 @@ /datum/ai_controller/basic_controller/deer + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/deer/deer.bt.json" blackboard = list( BB_STATIONARY_MOVE_TO_TARGET = TRUE, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Weeeeeeee?", "Weeee", "WEOOOOOOOOOO"), + BB_EMOTE_HEAR = list("brays."), + BB_EMOTE_SEE = list("shakes her head."), + BB_SPEAK_CHANCE = 1, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/deer, - /datum/ai_planning_subtree/stare_at_thing, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/rest_at_home, - /datum/ai_planning_subtree/play_with_friends, - /datum/ai_planning_subtree/find_and_hunt_target/mark_territory, - /datum/ai_planning_subtree/find_and_hunt_target/graze, - /datum/ai_planning_subtree/find_and_hunt_target/drink_water, - ) - - -///subtree to go around drinking water -/datum/ai_planning_subtree/find_and_hunt_target/drink_water - target_key = BB_DEER_WATER_TARGET - finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types - hunting_behavior = /datum/ai_behavior/hunt_target/drink_water - hunt_targets = list(/turf/open/water) - hunt_range = 7 - hunt_chance = 5 - -/datum/ai_behavior/hunt_target/drink_water - always_reset_target = TRUE - hunt_cooldown = 20 SECONDS - - -/datum/ai_behavior/hunt_target/drink_water/target_caught(mob/living/hunter, atom/hunted) - var/static/list/possible_emotes = list("drinks the water!", "dances in the water!", "splashes around happily!") - hunter.manual_emote(pick(possible_emotes)) - - -///subtree to go around grazing -/datum/ai_planning_subtree/find_and_hunt_target/graze - target_key = BB_DEER_GRASS_TARGET - finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types - hunting_behavior = /datum/ai_behavior/hunt_target/eat_grass - hunt_targets = list(/turf/open/floor/grass, /turf/open/misc/grass) - hunt_range = 7 - hunt_chance = 45 - - -/datum/ai_behavior/hunt_target/eat_grass +/// Munches grass with a happy little emote. +/datum/bt_node/ai_behavior/hunt_target/deer_graze always_reset_target = TRUE hunt_cooldown = 15 SECONDS - -/datum/ai_behavior/hunt_target/eat_grass/target_caught(mob/living/hunter, atom/hunted) +/datum/bt_node/ai_behavior/hunt_target/deer_graze/target_caught(mob/living/hunter, atom/hunted) var/static/list/possible_emotes = list("eats the grass!", "munches down the grass!", "chews on the grass!") hunter.manual_emote(pick(possible_emotes)) -///subtree to go around playing with other deers -/datum/ai_planning_subtree/play_with_friends - - -/datum/ai_planning_subtree/play_with_friends/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/static/list/emote_list = list("plays with", "dances with", "celebrates with") - var/static/list/friend_types = typecacheof(list(/mob/living/basic/deer)) - if(controller.blackboard_key_exists(BB_DEER_PLAYFRIEND)) - controller.queue_behavior(/datum/ai_behavior/emote_on_target, BB_DEER_PLAYFRIEND, emote_list) - if(SPT_PROB(3, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/find_hunt_target/valid_deer, BB_DEER_PLAYFRIEND, friend_types) - return SUBTREE_RETURN_FINISH_PLANNING - - -/datum/ai_behavior/emote_on_target/deer_play - - -/datum/ai_behavior/emote_on_target/deer_play/run_emote(mob/living/living_pawn, atom/target, list/emote_list) - . = ..() - living_pawn.spin(spintime = 4, speed = 1) - - -/datum/ai_behavior/find_hunt_target/valid_deer/valid_dinner(mob/living/source, mob/living/deer, radius, datum/ai_controller/controller, seconds_per_tick) - if(deer.stat == DEAD) - return FALSE - if(!can_see(source, deer, radius)) - return FALSE - deer.ai_controller?.set_blackboard_key(BB_DEER_PLAYFRIEND, source) - return can_see(source, deer, radius) - +/// Splashes happily in the water. +/datum/bt_node/ai_behavior/hunt_target/deer_drink + always_reset_target = TRUE + hunt_cooldown = 20 SECONDS -///subtree to mark trees as territories -/datum/ai_planning_subtree/find_and_hunt_target/mark_territory - target_key = BB_DEER_TREE_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target - hunting_behavior = /datum/ai_behavior/hunt_target/mark_territory - hunt_targets = list(/obj/structure/flora/tree) - hunt_range = 7 - hunt_chance = 75 +/datum/bt_node/ai_behavior/hunt_target/deer_drink/target_caught(mob/living/hunter, atom/hunted) + var/static/list/possible_emotes = list("drinks the water!", "dances in the water!", "splashes around happily!") + hunter.manual_emote(pick(possible_emotes)) -/datum/ai_behavior/hunt_target/mark_territory +/// Claims a tree as home territory. +/datum/bt_node/ai_behavior/hunt_target/deer_mark always_reset_target = TRUE hunt_cooldown = 15 SECONDS - -/datum/ai_behavior/hunt_target/mark_territory/target_caught(mob/living/hunter, atom/hunted) +/datum/bt_node/ai_behavior/hunt_target/deer_mark/target_caught(mob/living/hunter, atom/hunted) hunter.manual_emote("marks [hunted] with its hooves!") - hunter.ai_controller.set_blackboard_key(BB_DEER_TREEHOME, hunted) - - -/datum/ai_planning_subtree/find_and_hunt_target/mark_territory/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_DEER_TREEHOME)) //already found our home, abort! - return - return ..() - + hunter.ai_controller?.set_blackboard_key(BB_DEER_TREEHOME, hunted) -/datum/ai_planning_subtree/rest_at_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_DEER_RESTING] > world.time) //we're resting for now, nothing more to do - return SUBTREE_RETURN_FINISH_PLANNING - if(!controller.blackboard_key_exists(BB_DEER_TREEHOME) || controller.blackboard[BB_DEER_NEXT_REST_TIMER] > world.time) - return - controller.queue_behavior(/datum/ai_behavior/return_home, BB_DEER_TREEHOME) - -/datum/ai_behavior/return_home - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - ///minimum time till next rest +/// Rests by its home tree, then won't feel the need to rest again for a while. +/datum/bt_node/ai_behavior/deer_rest + /// How long we stand resting once we arrive (kept RUNNING via the cooldown). + var/rest_duration = 15 SECONDS + /// Minimum/maximum time before we want to rest again. var/minimum_time = 2 MINUTES - ///maximum time till next rest var/maximum_time = 4 MINUTES + ///ID for the rest timer + var/timerid + ///Are we napping? + var/sleeping - -/datum/ai_behavior/return_home/setup(datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/deer_rest/setup(datum/ai_controller/controller) . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - var/list/possible_turfs = get_adjacent_open_turfs(target) - shuffle_inplace(possible_turfs) - for(var/turf/possible_turf as anything in possible_turfs) - if(!possible_turf.is_blocked_turf()) - set_movement_target(controller, possible_turf) - return TRUE - return FALSE - - -/datum/ai_behavior/return_home/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + timerid = addtimer(CALLBACK(src, PROC_REF(finish_action), controller, TRUE), rest_duration, TIMER_UNIQUE | TIMER_STOPPABLE) + +/datum/bt_node/ai_behavior/deer_rest/perform(seconds_per_tick, datum/ai_controller/controller) + if(sleeping) + return AI_BEHAVIOR_DELAY var/mob/living/living_pawn = controller.pawn var/static/list/possible_emotes = list("rests its legs...", "yawns and naps...", "curls up and rests...") living_pawn.manual_emote(pick(possible_emotes)) - controller.set_blackboard_key(BB_DEER_RESTING, world.time + 15 SECONDS) + sleeping = TRUE + return AI_BEHAVIOR_DELAY + +/datum/bt_node/ai_behavior/deer_rest/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + deltimer(timerid) + timerid = null + sleeping = FALSE controller.set_blackboard_key(BB_DEER_NEXT_REST_TIMER, world.time + rand(minimum_time, maximum_time)) + +/// Plays with another deer, spinning around them. +/datum/bt_node/ai_behavior/deer_play + /// Blackboard key holding the deer we're playing with. + var/friend_key = BB_DEER_PLAYFRIEND + +/datum/bt_node/ai_behavior/deer_play/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/friend = controller.blackboard[friend_key] + if(QDELETED(friend)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/static/list/possible_emotes = list("plays with", "dances with", "celebrates with") + var/mob/living/living_pawn = controller.pawn + living_pawn.manual_emote("[pick(possible_emotes)] [friend]!") + living_pawn.spin(spintime = 4, speed = 1) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/deer_play/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(friend_key) diff --git a/code/modules/mob/living/basic/farm_animals/goat/goat.bt.json b/code/modules/mob/living/basic/farm_animals/goat/goat.bt.json new file mode 100644 index 000000000000..dcbaa93499f0 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/goat/goat.bt.json @@ -0,0 +1,26 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/goat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/forage_and_retaliate" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/goat/goat_ai.dm b/code/modules/mob/living/basic/farm_animals/goat/goat_ai.dm index af409c5763dc..2a6edae756ae 100644 --- a/code/modules/mob/living/basic/farm_animals/goat/goat_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/goat/goat_ai.dm @@ -1,23 +1,15 @@ /// Goats are normally content to sorta hang around and crunch any plant in sight, but they will go ape on someone who attacks them. /datum/ai_controller/basic_controller/goat + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/goat/goat.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("EHEHEHEHEH", "eh?"), + BB_EMOTE_HEAR = list("brays."), + BB_EMOTE_SEE = list("shakes their head.", "stamps a foot.", "glares around."), + BB_SPEAK_CHANCE = 3, + ), ) ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/capricious_retaliate, // Capricious like Capra, get it? - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/goat, - ) - -/datum/ai_planning_subtree/random_speech/goat - speech_chance = 3 - emote_hear = list("brays.") - emote_see = list("shakes their head.", "stamps a foot.", "glares around.") - speak = list("EHEHEHEHEH", "eh?") diff --git a/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.json b/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.json new file mode 100644 index 000000000000..f7fe2284a28d --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.json @@ -0,0 +1,11 @@ +{ + "dm_type": "/datum/bt_node/subtree/forage_for_goose_food", + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_FOOD", + "targeting_strategy": "/datum/targeting_strategy/goose_edible", + "target_source": "/datum/target_source/oview_items", + "vision_range": 1 + } +} diff --git a/code/modules/mob/living/basic/farm_animals/goose/goose.bt.json b/code/modules/mob/living/basic/farm_animals/goose/goose.bt.json new file mode 100644 index 000000000000..3319b58f849b --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/goose/goose.bt.json @@ -0,0 +1,38 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/goose", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/forage_and_retaliate", + "bindings": { + "q8w3rtv1": "/datum/bt_node/subtree/forage_for_goose_food" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/goose/goose.dm b/code/modules/mob/living/basic/farm_animals/goose/goose.dm index aa25ca7e9d7d..935b3af656e2 100644 --- a/code/modules/mob/living/basic/farm_animals/goose/goose.dm +++ b/code/modules/mob/living/basic/farm_animals/goose/goose.dm @@ -30,8 +30,6 @@ gold_core_spawnable = HOSTILE_SPAWN /// Do we actually destroy food we eat? var/conserve_food = FALSE - /// Unfortunately, geese want to eat every item - var/static/list/item_typecache = typecacheof(/obj/item) /mob/living/basic/goose/Initialize(mapload) . = ..() @@ -42,8 +40,6 @@ RegisterSignal(src, COMSIG_MOB_PRE_EAT, PROC_REF(on_tried_gobbling)) RegisterSignal(src, COMSIG_MOB_ATE, PROC_REF(on_gobbled)) - ai_controller.set_blackboard_key(BB_BASIC_FOODS, item_typecache) - /mob/living/basic/goose/death(gibbed) if (!gibbed && length(contents)) var/turf/drop_turf = drop_location() @@ -157,8 +153,7 @@ remove_status_effect(/datum/status_effect/goose_choking) // We're going to cough it out /mob/living/basic/goose/vomit/proc/stop_deadchat_plays() - var/initial_behaviour = initial(ai_controller?.idle_behavior) - ai_controller?.idle_behavior = SSidle_ai_behaviors.idle_behaviors[initial_behaviour] + ai_controller?.clear_blackboard_key(BB_DISABLE_IDLE) /mob/living/basic/goose/vomit/deadchat_plays(mode = ANARCHY_MODE, cooldown = 12 SECONDS) var/list/goose_inputs = list( @@ -172,6 +167,6 @@ return // Stop automated movement, retain the other behaviour so you can lead the horse to plastic and have it drink. - ai_controller?.idle_behavior = null + ai_controller?.set_blackboard_key(BB_DISABLE_IDLE, TRUE) #undef GOOSE_SATIATED diff --git a/code/modules/mob/living/basic/farm_animals/goose/goose_ai.dm b/code/modules/mob/living/basic/farm_animals/goose/goose_ai.dm index 94202a90df32..43412f191643 100644 --- a/code/modules/mob/living/basic/farm_animals/goose/goose_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/goose/goose_ai.dm @@ -1,83 +1,38 @@ /// Geese like to eat random objects and kill themselves, and occasionally get pissed off for no reason /datum/ai_controller/basic_controller/goose + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/goose/goose.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_SEARCH_RANGE = 1, BB_EAT_FOOD_COOLDOWN = 10 SECONDS, - BB_EAT_EMOTES = list() + BB_EAT_EMOTES = list(), + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Honk!"), + BB_EMOTE_HEAR = list("honks.", "honks loudly.", "honks aggressively."), + BB_EMOTE_SEE = list("flaps.", "preens.", "glares around."), + BB_SPEAK_CHANCE = 3, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/goose - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/goose, - /datum/ai_planning_subtree/capricious_retaliate, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/find_food/goose, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Goose who doesn't randomly retaliate but does still try to die by eating random items /datum/ai_controller/basic_controller/goose/calm + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_SEARCH_RANGE = 1, BB_EAT_FOOD_COOLDOWN = 0.5 SECONDS, // Uh oh - BB_EAT_EMOTES = list() + BB_EAT_EMOTES = list(), + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Honk!"), + BB_EMOTE_HEAR = list("honks.", "honks loudly.", "honks aggressively."), + BB_EMOTE_SEE = list("flaps.", "preens.", "glares around."), + BB_SPEAK_CHANCE = 3, + ), ) - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/goose, - /datum/ai_planning_subtree/use_mob_ability/goose_vomit, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/find_food/goose, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/// Walk more if we're choking or vomiting -/datum/idle_behavior/idle_random_walk/goose - -/datum/idle_behavior/idle_random_walk/goose/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - walk_chance = controller.blackboard[BB_GOOSE_PANICKED] ? 100 : 25 // I think this sets it for every goose but that's fine because it'll reset it before using it - return ..() - -/// Only look for things geese will try to eat -/datum/ai_planning_subtree/find_food/goose - finding_behavior = /datum/ai_behavior/find_and_set/in_list/goose_food - -/datum/ai_planning_subtree/find_food/goose/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (controller.blackboard[BB_GOOSE_PANICKED]) - return // Don't look for food while choking or vomiting - return ..() - -/// Only set things geese will try to eat -/datum/ai_behavior/find_and_set/in_list/goose_food - -/datum/ai_behavior/find_and_set/in_list/goose_food/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) - if(!length(found)) - return - - var/list/filtered = list() - for (var/obj/item/thing as anything in found) - if (IS_EDIBLE(thing) || thing.has_material_type(/datum/material/plastic)) - filtered += thing - - if(length(filtered)) - return pick(filtered) - -/// Use this ability only if we roll a dice correctly -/datum/ai_planning_subtree/use_mob_ability/goose_vomit - -/datum/ai_planning_subtree/use_mob_ability/goose_vomit/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/trigger_prob = controller.blackboard[BB_GOOSE_VOMIT_CHANCE] || 0 - if (prob(trigger_prob)) - return ..() -/datum/ai_planning_subtree/random_speech/goose - speech_chance = 3 - emote_hear = list("honks.", "honks loudly.", "honks aggressively.") - emote_see = list("flaps.", "preens.", "glares around.") - speak = list("Honk!") +/// Geese are picky: they only forage for edible items and plastic, and only when it's right next to them. +/datum/bt_node/subtree/forage_for_goose_food + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/goose/forage_for_goose_food.bt.json" diff --git a/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.json b/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.json new file mode 100644 index 000000000000..c6069c547145 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/goose/goose_calm.bt.json @@ -0,0 +1,30 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/goose/calm", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/forage_and_retaliate", + "bindings": { + "q8w3rtv1": "/datum/bt_node/subtree/forage_for_goose_food", + "z4n9bk7p": "/datum/bt_node/subtree/pick_retaliate_target" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.json b/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.json new file mode 100644 index 000000000000..f40bcabba1d4 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.json @@ -0,0 +1,189 @@ +{ + "dm_type": "/datum/bt_node/subtree/bar_gorilla_combat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 10, + "require_reach": false + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/mob_stat_at_least", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_CURRENT_TARGET", + "min_stat": "SOFT_CRIT" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_monkey_target_disposal", + "target_source": "/datum/target_source/oview_single_type/disposal_unit", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grab_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_monkey_target_disposal", + "required_dist": 1, + "finish_on_arrival": true, + "movement_type": "/datum/ai_movement/jps" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/stuff_in_disposal", + "vars": { + "attack_target_key": "BB_CURRENT_TARGET", + "disposal_target_key": "BB_monkey_target_disposal" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "can_attack_turfs": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/drop_all_held_items" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance_from_key", + "vars": { + "chance_key": "BB_EMOTE_CHANCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/run_emote", + "vars": { + "emote_key": "BB_EMOTE_KEY" + } + } + } + ] + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.json b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.json new file mode 100644 index 000000000000..f49bf37a5daf --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.json @@ -0,0 +1,130 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/gorilla", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "can_attack_turfs": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance_from_key", + "vars": { + "chance_key": "BB_EMOTE_CHANCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/run_emote", + "vars": { + "emote_key": "BB_EMOTE_KEY" + } + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm index 6fb3c5ea9aff..9caa17b06ac1 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm @@ -1,6 +1,12 @@ /// Where do we draw gorilla held overlays? #define GORILLA_HANDS_LAYER 1 + + +GLOBAL_LIST_INIT(strippable_gorilla_items, create_strippable_list(list( + /datum/strippable_item/hand/left, + /datum/strippable_item/hand/right))) + /** * Like a bigger monkey * They make a lot of noise and punch limbs off unconscious folks @@ -67,6 +73,8 @@ ) AddComponent(/datum/component/personal_crafting) AddComponent(/datum/component/basic_inhands, y_offset = -1) + AddElement(/datum/element/strippable, GLOB.strippable_gorilla_items) + ai_controller?.set_blackboard_key(BB_BASIC_FOODS, typecacheof(gorilla_food)) /mob/living/basic/gorilla/examine(mob/user) @@ -164,6 +172,43 @@ ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT) AddComponent(/datum/component/crate_carrier) +/// big fucking gorilla version of pun pun. be afraid. +/mob/living/basic/gorilla/bar + name = "Pun Pun" //C A N O N, allegedly + desc = "The bar's monkey. Something has gone horribly right." + icon = 'icons/mob/simple/bargorilla.dmi' + faction = list(FACTION_NEUTRAL, FACTION_MONKEY, FACTION_JUNGLE) + unique_name = FALSE + ai_controller = /datum/ai_controller/monkey/pun_pun + pass_flags = parent_type::pass_flags | PASSTABLE //he cant serve the GUESTS otherwise okay? + + +/mob/living/basic/gorilla/bar/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_PAWN_POSSESSED_BY_AI_CONTROLLER, PROC_REF(on_possessed_by_ai_controller)) + gorrilify_punpun_ai() + + if(!GLOB.the_one_and_only_punpun) + GLOB.the_one_and_only_punpun = src + +/// Signal handler for when an ai controller possesses us, reapplies our gorilla-specific ai tweaks if it's a monkey controller +/mob/living/basic/gorilla/bar/proc/on_possessed_by_ai_controller(datum/source, datum/ai_controller/source_controller) + SIGNAL_HANDLER + if(!istype(source_controller, /datum/ai_controller/monkey)) + return + gorrilify_punpun_ai() + +/mob/living/basic/gorilla/bar/proc/gorrilify_punpun_ai() + ai_controller?.override_blackboard_key(BB_SONG_LINES, GORILLA_SONG) + ai_controller?.override_blackboard_key(BB_EMOTE_KEY, "ooga") + ai_controller?.override_blackboard_key(BB_EMOTE_CHANCE, 50) + ai_controller?.set_behavior_tree_override(SUBPLAN_ID_MONKEY_COMBAT, /datum/bt_node/subtree/bar_gorilla_combat) + +/mob/living/basic/gorilla/bar/Destroy() + if(GLOB.the_one_and_only_punpun == src) + GLOB.the_one_and_only_punpun = null + return ..() + /// A version of the gorilla achieved by reaching enough genetic damage as a monkey /mob/living/basic/gorilla/genetics name = "Lab Gorilla" diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm index ba9584f4bc19..16b279461e34 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm @@ -1,30 +1,16 @@ /// Pretty basic, just click people to death. Also hunt and eat bananas. /datum/ai_controller/basic_controller/gorilla + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/gorilla/gorilla.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_TREAT_UNCONSCIOUS_AS_HARDCRIT = TRUE, BB_EMOTE_KEY = "ooga", BB_EMOTE_CHANCE = 40, ) ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/run_emote, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/gorilla, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - ) - -/datum/ai_planning_subtree/attack_obstacle_in_path/gorilla - attack_behaviour = /datum/ai_behavior/attack_obstructions/gorilla - -/datum/ai_behavior/attack_obstructions/gorilla - can_attack_turfs = TRUE /datum/ai_controller/basic_controller/gorilla/lesser blackboard = list( @@ -32,3 +18,6 @@ BB_EMOTE_KEY = "ooga", BB_EMOTE_CHANCE = 60, ) + +/datum/bt_node/subtree/bar_gorilla_combat + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/gorilla/bar_gorilla_combat.bt.json" diff --git a/code/modules/mob/living/basic/farm_animals/pig.bt.json b/code/modules/mob/living/basic/farm_animals/pig.bt.json new file mode 100644 index 000000000000..5cb9d774e7be --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/pig.bt.json @@ -0,0 +1,26 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/pig", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_brawler_combat" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm index 39008cab8674..6ba56fe5a19a 100644 --- a/code/modules/mob/living/basic/farm_animals/pig.dm +++ b/code/modules/mob/living/basic/farm_animals/pig.dm @@ -59,18 +59,17 @@ visible_message(span_notice("[src] snorts respectfully.")) /datum/ai_controller/basic_controller/pig + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/pig.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("oink?", "oink", "snurf"), + BB_EMOTE_HEAR = list("snorts."), + BB_EMOTE_SEE = list("sniffs around."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/pig/pig1.ogg', 'sound/mobs/non-humanoids/pig/pig2.ogg'), + BB_SPEAK_CHANCE = 3, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/pig, - ) diff --git a/code/modules/mob/living/basic/farm_animals/pony.bt.json b/code/modules/mob/living/basic/farm_animals/pony.bt.json new file mode 100644 index 000000000000..88a87c3274e3 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/pony.bt.json @@ -0,0 +1,43 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/pony", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_TAMED", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_skittish_combat" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_brawler_combat" + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm index 3e9ad63ad399..ad34e95fabb3 100644 --- a/code/modules/mob/living/basic/farm_animals/pony.dm +++ b/code/modules/mob/living/basic/farm_animals/pony.dm @@ -65,12 +65,13 @@ AddElement(/datum/element/ridable, /datum/component/riding/creature/pony) visible_message(span_notice("[src] snorts happily.")) new /obj/effect/temp_visual/heart(loc) - - ai_controller.replace_planning_subtrees(list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/random_speech/pony/tamed, - )) + var/static/list/tamed_emotes = list( + BB_EMOTE_HEAR = list("snorts."), + BB_EMOTE_SEE = list("snorts."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/pony/snort.ogg'), + BB_SPEAK_CHANCE = 3, + ) + ai_controller.override_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, tamed_emotes) if(unique_tamer) my_owner = WEAKREF(tamer) @@ -124,21 +125,19 @@ . |= SHOVE_CAN_STAGGER /datum/ai_controller/basic_controller/pony + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/pony.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("whinnies!"), + BB_EMOTE_SEE = list("horses around."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/pony/whinny01.ogg', 'sound/mobs/non-humanoids/pony/whinny02.ogg', 'sound/mobs/non-humanoids/pony/whinny03.ogg'), + BB_SPEAK_CHANCE = 3, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/pony, - ) // A stronger horse is required for our strongest cowboys. /mob/living/basic/pony/syndicate diff --git a/code/modules/mob/living/basic/farm_animals/rabbit.bt.json b/code/modules/mob/living/basic/farm_animals/rabbit.bt.json new file mode 100644 index 000000000000..5414e42ca1bb --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/rabbit.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/rabbit", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_and_speak" +} diff --git a/code/modules/mob/living/basic/farm_animals/rabbit.dm b/code/modules/mob/living/basic/farm_animals/rabbit.dm index 42c3f0832222..75caf1a6991c 100644 --- a/code/modules/mob/living/basic/farm_animals/rabbit.dm +++ b/code/modules/mob/living/basic/farm_animals/rabbit.dm @@ -55,21 +55,23 @@ AddElement(/datum/element/pet_bonus, "hop") AddElement(/datum/element/animal_variety, icon_prefix, pick("brown", "black", "white"), TRUE) AddElement(/datum/element/can_be_held) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_RABBIT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) if(prob(20)) // bunny name = "bunny" /datum/ai_controller/basic_controller/rabbit + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/rabbit.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Mrrp.", "CHIRP!", "Mrrp?"), + BB_EMOTE_HEAR = list("hops."), + BB_EMOTE_SEE = list("hops around.", "bounces up and down."), + BB_SPEAK_CHANCE = 10, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/rabbit, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - ) /// The easter subtype of rabbits, will lay eggs and say Eastery catchphrases. @@ -88,7 +90,7 @@ . = ..() //passed to the egg_layer component as how many eggs it gets when it eats something. var/eggs_added_from_eating = rand(1, 4) - var/list/feed_messages = list("[p_they()] nibbles happily.", "[p_they()] noms happily.") + var/list/feed_messages = list("[p_they()] nibble[p_s()] happily.", "[p_they()] nom[p_s()] happily.") AddComponent(/datum/component/egg_layer,\ /obj/item/surprise_egg,\ list(/obj/item/food/grown/carrotlike/carrot),\ @@ -100,11 +102,15 @@ ) /datum/ai_controller/basic_controller/rabbit/easter - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/rabbit/easter, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - ) + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Hop into Easter!", "Come get your eggs!", "Prizes for everyone!"), + BB_EMOTE_HEAR = list("hops."), + BB_EMOTE_SEE = list("hops around.", "bounces up and down."), + BB_SPEAK_CHANCE = 10, + ), + ) /// Same deal as the standard easter subtype, but these ones are able to brave the cold of space with their handy gas mask. @@ -120,8 +126,12 @@ unsuitable_cold_damage = 0 // Zero because we are meant to survive in space. /datum/ai_controller/basic_controller/rabbit/easter/space - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/rabbit/easter/space, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - ) + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Hmph mmph mmmph!", "Mmphe mmphe mmphe!", "Hmm mmm mmm!"), + BB_EMOTE_HEAR = list("hops."), + BB_EMOTE_SEE = list("hops around.", "bounces up and down."), + BB_SPEAK_CHANCE = 10, + ), + ) diff --git a/code/modules/mob/living/basic/farm_animals/sheep.bt.json b/code/modules/mob/living/basic/farm_animals/sheep.bt.json new file mode 100644 index 000000000000..d1d6c6a99733 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/sheep.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/sheep", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/skittish_and_speak" +} diff --git a/code/modules/mob/living/basic/farm_animals/sheep.dm b/code/modules/mob/living/basic/farm_animals/sheep.dm index bbbf7e588753..64da53715800 100644 --- a/code/modules/mob/living/basic/farm_animals/sheep.dm +++ b/code/modules/mob/living/basic/farm_animals/sheep.dm @@ -43,6 +43,7 @@ item_harvest_sound = 'sound/items/handling/surgery/scalpel1.ogg', \ ) AddElement(/datum/element/ai_retaliate) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_SHEEP, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) RegisterSignal(src, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_sacrificed)) /mob/living/basic/sheep/update_overlays() @@ -80,14 +81,16 @@ update_appearance(UPDATE_ICON) /datum/ai_controller/basic_controller/sheep + behavior_tree_json = "code/modules/mob/living/basic/farm_animals/sheep.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("baaa", "baaaAAAAAH!", "baaah"), + BB_EMOTE_HEAR = list("bleats."), + BB_EMOTE_SEE = list("shakes her head.", "stares into the distance."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/sheep/sheep1.ogg', 'sound/mobs/non-humanoids/sheep/sheep2.ogg', 'sound/mobs/non-humanoids/sheep/sheep3.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/sheep, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - ) diff --git a/code/modules/mob/living/basic/festivus_pole.bt.json b/code/modules/mob/living/basic/festivus_pole.bt.json new file mode 100644 index 000000000000..c3d84d6af3f2 --- /dev/null +++ b/code/modules/mob/living/basic/festivus_pole.bt.json @@ -0,0 +1,122 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/festivus_pole", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/use_ability_on_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "ability_key": "BB_FESTIVE_APC" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": "10" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/apc", + "targeting_strategy": "/datum/targeting_strategy/chargeable_apc", + "vision_range": 6 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/festivus_pole.dm b/code/modules/mob/living/basic/festivus_pole.dm index 19a3cd2bf583..593f0e34fb7d 100644 --- a/code/modules/mob/living/basic/festivus_pole.dm +++ b/code/modules/mob/living/basic/festivus_pole.dm @@ -51,19 +51,13 @@ grant_actions_by_list(list(/datum/action/cooldown/mob_cooldown/charge_apc = BB_FESTIVE_APC)) /datum/ai_controller/basic_controller/festivus_pole + behavior_tree_json = "code/modules/mob/living/basic/festivus_pole.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_LOW_PRIORITY_HUNTING_TARGET = null, // APCs ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/find_and_hunt_target/look_for_apcs, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /mob/living/basic/festivus/attack_hand(mob/living/carbon/human/user, list/modifiers) . = ..() @@ -84,41 +78,4 @@ if(apc_target.cell) apc_target.cell.give(FESTIVUS_RECHARGE_VALUE) -/datum/ai_planning_subtree/find_and_hunt_target/look_for_apcs - hunting_behavior = /datum/ai_behavior/hunt_target/apcs - hunt_targets = list(/obj/machinery/power/apc) - hunt_range = 6 - - -/datum/ai_planning_subtree/find_and_hunt_target/look_for_apcs - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/apcs - hunting_behavior = /datum/ai_behavior/hunt_target/apcs - hunt_targets = list(/obj/machinery/power/apc) - hunt_range = 6 - -/datum/ai_behavior/hunt_target/apcs - hunt_cooldown = 15 SECONDS - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/apcs/target_caught(mob/living/basic/hunter, obj/machinery/power/apc/hunted) - var/datum/action/cooldown/mob_cooldown/charge_ability = hunter.ai_controller.blackboard[BB_FESTIVE_APC] - if(isnull(charge_ability)) - return - charge_ability.Activate(hunted) - - -/datum/ai_behavior/find_hunt_target/apcs - -/datum/ai_behavior/find_hunt_target/apcs/valid_dinner(mob/living/source, obj/machinery/power/apc/dinner, radius) - if(istype(dinner, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/apc_target = dinner - if(!apc_target.cell) - return FALSE - var/obj/item/stock_parts/power_store/cell/apc_cell = apc_target.cell - if(apc_cell.charge == apc_cell.maxcharge) //if its full charge we no longer feed it - return FALSE - - return can_see(source, dinner, radius) - #undef FESTIVUS_RECHARGE_VALUE diff --git a/code/modules/mob/living/basic/guardian/guardian.dm b/code/modules/mob/living/basic/guardian/guardian.dm index 9c2592f97485..644a59dfb266 100644 --- a/code/modules/mob/living/basic/guardian/guardian.dm +++ b/code/modules/mob/living/basic/guardian/guardian.dm @@ -177,7 +177,7 @@ to_chat(src, span_warning("Invalid name, please try again.")) return guardian_rename() to_chat(src, span_notice("Your new name [span_name(new_name)] anchors itself in your mind.")) - fully_replace_character_name(null, new_name) + fully_replace_character_name(null, new_name, log_new_name = TRUE) /// Picks a random name as a suggestion /mob/living/basic/guardian/proc/generate_random_name() @@ -311,7 +311,7 @@ return to_chat(summoner, span_bolddanger("[name] is under attack! You take damage!")) summoner.visible_message(span_bolddanger("Blood sprays from [summoner] as [src] takes damage!")) - if(summoner.stat == UNCONSCIOUS || summoner.stat == HARD_CRIT) + if(summoner.stat >= HARD_CRIT) to_chat(summoner, span_bolddanger("Your head pounds, you can't take the strain of sustaining [src] in this condition!")) summoner.adjust_organ_loss(ORGAN_SLOT_BRAIN, amount * 0.5) diff --git a/code/modules/mob/living/basic/heretic/flesh_stalker.dm b/code/modules/mob/living/basic/heretic/flesh_stalker.dm index 5cee158463ac..c7c9eb77c54f 100644 --- a/code/modules/mob/living/basic/heretic/flesh_stalker.dm +++ b/code/modules/mob/living/basic/heretic/flesh_stalker.dm @@ -25,18 +25,10 @@ /// Changes shape and lies in wait when it has no target, uses EMP and attacks once it does /datum/ai_controller/basic_controller/stalker + behavior_tree_json = "code/modules/mob/living/basic/heretic/stalker.bt.json" ai_traits = CAN_ACT_IN_STASIS blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/shapechange_ambush, - /datum/ai_planning_subtree/use_mob_ability, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/heretic/flesh_worm.dm b/code/modules/mob/living/basic/heretic/flesh_worm.dm index d796560ed6d5..612471959275 100644 --- a/code/modules/mob/living/basic/heretic/flesh_worm.dm +++ b/code/modules/mob/living/basic/heretic/flesh_worm.dm @@ -39,7 +39,7 @@ /datum/component/amputating_limbs,\ surgery_time = 0 SECONDS,\ surgery_verb = "tears",\ - minimum_stat = CONSCIOUS,\ + minimum_stat = STABLE,\ snip_chance = 10,\ target_zones = GLOB.arm_zones,\ ) diff --git a/code/modules/mob/living/basic/heretic/raw_prophet.bt.json b/code/modules/mob/living/basic/heretic/raw_prophet.bt.json new file mode 100644 index 000000000000..66da01c6cf66 --- /dev/null +++ b/code/modules/mob/living/basic/heretic/raw_prophet.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/raw_prophet", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_ranged_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/heretic/raw_prophet.dm b/code/modules/mob/living/basic/heretic/raw_prophet.dm index 8c5da925c064..9773fdc5d6cd 100644 --- a/code/modules/mob/living/basic/heretic/raw_prophet.dm +++ b/code/modules/mob/living/basic/heretic/raw_prophet.dm @@ -85,16 +85,9 @@ /// Walk and attack people, blind them when we can /datum/ai_controller/basic_controller/raw_prophet + behavior_tree_json = "code/modules/mob/living/basic/heretic/raw_prophet.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/heretic/rust_walker.bt.json b/code/modules/mob/living/basic/heretic/rust_walker.bt.json new file mode 100644 index 000000000000..4f69b8a51d9c --- /dev/null +++ b/code/modules/mob/living/basic/heretic/rust_walker.bt.json @@ -0,0 +1,126 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/rust_walker", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_turf_has_trait", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "trait": "TRAIT_RUSTY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/rust" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/heretic/rust_walker.dm b/code/modules/mob/living/basic/heretic/rust_walker.dm index a2cdfc432505..24e826cfdc51 100644 --- a/code/modules/mob/living/basic/heretic/rust_walker.dm +++ b/code/modules/mob/living/basic/heretic/rust_walker.dm @@ -54,39 +54,9 @@ /// Converts unconverted terrain, sprays pocket sand around /datum/ai_controller/basic_controller/rust_walker + behavior_tree_json = "code/modules/mob/living/basic/heretic/rust_walker.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/rust - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/use_mob_ability/rust_walker, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/// Moves a lot if healthy and on rust (to find more tiles to rust) or unhealthy and not on rust (to find healing rust) -/// Still moving in random directions though we're not really seeking it out -/datum/idle_behavior/idle_random_walk/rust - -/datum/idle_behavior/idle_random_walk/rust/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/our_mob = controller.pawn - var/turf/our_turf = get_turf(our_mob) - if (HAS_TRAIT(our_turf, TRAIT_RUSTY)) - walk_chance = (our_mob.health < our_mob.maxHealth) ? 10 : 50 - else - walk_chance = (our_mob.health < our_mob.maxHealth) ? 50 : 10 - return ..() - -/// Use if we're not stood on rust right now -/datum/ai_planning_subtree/use_mob_ability/rust_walker - -/datum/ai_planning_subtree/use_mob_ability/rust_walker/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/turf/our_turf = get_turf(controller.pawn) - if (HAS_TRAIT(our_turf, TRAIT_RUSTY)) - return - return ..() diff --git a/code/modules/mob/living/basic/heretic/stalker.bt.json b/code/modules/mob/living/basic/heretic/stalker.bt.json new file mode 100644 index 000000000000..aabce4504221 --- /dev/null +++ b/code/modules/mob/living/basic/heretic/stalker.bt.json @@ -0,0 +1,147 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/stalker", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_inside_mob", + "vars": { + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability/shapeshift" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_inside_mob", + "vars": { + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": false, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_at_least", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_BASIC_MOB_HAS_TARGET_TIME", + "minimum": "8 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability/shapeshift" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/heretic/star_gazer.bt.json b/code/modules/mob/living/basic/heretic/star_gazer.bt.json new file mode 100644 index 000000000000..80167946253b --- /dev/null +++ b/code/modules/mob/living/basic/heretic/star_gazer.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/star_gazer", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm index 6ea05af3665f..56ce4ab8259f 100644 --- a/code/modules/mob/living/basic/heretic/star_gazer.dm +++ b/code/modules/mob/living/basic/heretic/star_gazer.dm @@ -131,7 +131,7 @@ target.apply_damage(damage = 5, damagetype = BURN) var/datum/targeting_strategy/target_confirmer = GET_TARGETING_STRATEGY(ai_controller.blackboard[BB_TARGETING_STRATEGY]) for(var/mob/living/nearby_mob in range(1, src)) - if(target == nearby_mob || !target_confirmer?.can_attack(src, nearby_mob)) + if(target == nearby_mob || !target_confirmer?.is_valid_target(src, nearby_mob)) continue nearby_mob.apply_status_effect(/datum/status_effect/star_mark) nearby_mob.apply_damage(10) @@ -364,7 +364,7 @@ if(victim == our_master?.resolve()) continue var/mob/living/living_victim = victim - if(living_victim.stat > CONSCIOUS) + if(living_victim.stat != STABLE) playsound(living_victim, 'sound/effects/supermatter.ogg', 80, TRUE) living_victim.visible_message( span_danger("You see [living_victim] engulfed in the scorching wrath of the cosmos. \ @@ -402,30 +402,10 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/attack_obstacle_in_path/pet_target/star_gazer, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/star_gazer, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/attack_obstacle_in_path/star_gazer - attack_behaviour = /datum/ai_behavior/attack_obstructions/star_gazer - -/datum/ai_planning_subtree/attack_obstacle_in_path/pet_target/star_gazer - attack_behaviour = /datum/ai_behavior/attack_obstructions/star_gazer - -/datum/ai_behavior/attack_obstructions/star_gazer - action_cooldown = 0.4 SECONDS - can_attack_turfs = TRUE - can_attack_dense_objects = TRUE + behavior_tree_json = "code/modules/mob/living/basic/heretic/star_gazer.bt.json" /datum/pet_command/attack/star_gazer speech_commands = list("attack", "sic", "kill", "slash them") command_feedback = "stares!" pointed_reaction = "stares intensely!" refuse_reaction = "..." - attack_behaviour = /datum/ai_behavior/basic_melee_attack diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.json b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.json new file mode 100644 index 000000000000..fb3ed54b6eb3 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.json @@ -0,0 +1,54 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/ice_demon", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/ice_demon_flee_from_fire" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/ice_demon_combat" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.json b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.json new file mode 100644 index 000000000000..7ab3b485eb52 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.json @@ -0,0 +1,18 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/ice_demon/afterimage", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/ice_demon_flee_from_fire" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm index 642061ed782d..155896915cc8 100644 --- a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/ice_demon + behavior_tree_json = "code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_RANGED_SKIRMISH_MAX_DISTANCE = 7, @@ -9,104 +10,14 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/flee_target/ice_demon, - /datum/ai_planning_subtree/ranged_skirmish/ice_demon, - /datum/ai_planning_subtree/maintain_distance/cover_minimum_distance, - /datum/ai_planning_subtree/teleport_away_from_target, - /datum/ai_planning_subtree/find_and_hunt_target/teleport_destination, - /datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages, - ) - -/datum/ai_planning_subtree/teleport_away_from_target - ability_key = BB_DEMON_TELEPORT_ABILITY - -/datum/ai_planning_subtree/find_and_hunt_target/teleport_destination - target_key = BB_TELEPORT_DESTINATION - hunting_behavior = /datum/ai_behavior/hunt_target/use_ability_on_target/demon_teleport - finding_behavior = /datum/ai_behavior/find_valid_teleport_location - hunt_targets = list(/turf/open) - hunt_range = 3 - finish_planning = FALSE - -/datum/ai_planning_subtree/find_and_hunt_target/teleport_destination/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - if(controller.blackboard_key_exists(BB_ESCAPE_DESTINATION)) - controller.clear_blackboard_key(BB_TELEPORT_DESTINATION) - return - var/datum/action/cooldown/ability = controller.blackboard[BB_DEMON_TELEPORT_ABILITY] - if(!ability?.IsAvailable()) - return - return ..() - -/datum/ai_behavior/find_valid_teleport_location - -/datum/ai_behavior/find_valid_teleport_location/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range) - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - var/list/possible_turfs = list() - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - for(var/turf/open/potential_turf in oview(hunt_range, target)) //we check for turfs around the target - if(potential_turf.is_blocked_turf()) - continue - if(!can_see(target, potential_turf, hunt_range)) - continue - possible_turfs += potential_turf - - if(!length(possible_turfs)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(hunting_target_key, pick(possible_turfs)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/hunt_target/use_ability_on_target/demon_teleport - hunt_cooldown = 2 SECONDS - ability_key = BB_DEMON_TELEPORT_ABILITY - behavior_flags = NONE - -/datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages - ability_key = BB_DEMON_CLONE_ABILITY - -/datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.health / living_pawn.maxHealth > 0.5) //only use this ability when under half health - return - return ..() - -/datum/ai_planning_subtree/flee_target/ice_demon - -/datum/ai_planning_subtree/flee_target/ice_demon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) - return - if(!iscarbon(target)) - return - var/mob/living/carbon/human_target = target - - for(var/obj/held_item in human_target.held_items) - if(!is_type_in_list(held_item, controller.blackboard[BB_LIST_SCARY_ITEMS])) - continue - if(!held_item.light_on) - continue - var/datum/action/cooldown/slip_ability = controller.blackboard[BB_DEMON_SLIP_ABILITY] - if(slip_ability?.IsAvailable()) - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_DEMON_SLIP_ABILITY) - return ..() +/datum/ai_controller/basic_controller/ice_demon/afterimage + behavior_tree_json = "code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_afterimage.bt.json" -/datum/ai_planning_subtree/ranged_skirmish/ice_demon - min_range = 0 +/// Flees from a target holding a lit scary item, slipping them on the way out. +/datum/bt_node/subtree/ice_demon_flee_from_fire + behavior_tree_json = "code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.json" -/datum/ai_controller/basic_controller/ice_demon/afterimage - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/flee_target/ice_demon, //even the afterimages are afraid of flames! - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) +/datum/bt_node/subtree/ice_demon_combat + behavior_tree_json = "code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.json" diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.json b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.json new file mode 100644 index 000000000000..9a5556b1ece4 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_combat.bt.json @@ -0,0 +1,127 @@ +{ + "dm_type": "/datum/bt_node/subtree/ice_demon_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 1 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_DEMON_TELEPORT_ABILITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_furthest_turf_from_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "set_key": "BB_ESCAPE_DESTINATION", + "range": 7 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_DEMON_TELEPORT_ABILITY", + "target_key": "BB_ESCAPE_DESTINATION" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_health_below", + "vars": { + "health_threshold": 75 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_DEMON_CLONE_ABILITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_DEMON_CLONE_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_DEMON_TELEPORT_ABILITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_valid_teleport_location", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "set_key": "BB_TELEPORT_DESTINATION", + "range": 3 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_DEMON_TELEPORT_ABILITY", + "target_key": "BB_TELEPORT_DESTINATION" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "max_range": 9 + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.json b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.json new file mode 100644 index 000000000000..ee77b66b8cb2 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_flee_from_fire.bt.json @@ -0,0 +1,37 @@ +{ + "dm_type": "/datum/bt_node/subtree/ice_demon_flee_from_fire", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_holding_lit_item", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET", + "item_types_key": "BB_LIST_SCARY_ITEMS" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_DEMON_SLIP_ABILITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_DEMON_SLIP_ABILITY" + } + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.json b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.json new file mode 100644 index 000000000000..d8de7a1f0a6b --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.json @@ -0,0 +1,284 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/ice_whelp", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WHELP_WIDESPREAD_FIRE", + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 3 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WHELP_STRAIGHTLINE_FIRE", + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 7 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TARGET_CANNIBAL" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_CANNIBAL", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_TARGET_CANNIBAL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TARGET_CANNIBAL" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TARGET_ROCK" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_ROCK", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_TARGET_ROCK", + "combat_mode": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_WHELP_SCULPT_COOLDOWN", + "cooldown_duration": "5 MINUTES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TARGET_ROCK" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TARGET_TREE" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_TREE", + "required_dist": 2 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_WHELP_STRAIGHTLINE_FIRE", + "target_key": "BB_TARGET_TREE", + "maximum_distance": 2 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_WHELP_BURN_COOLDOWN", + "cooldown_duration": "2 MINUTES" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_CANNIBAL", + "target_source": "/datum/target_source/oview_single_type/ice_whelp", + "targeting_strategy": "/datum/targeting_strategy/dead_mob/not_pulled", + "vision_range": 10 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_WHELP_SCULPT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_ROCK", + "target_source": "/datum/target_source/oview_single_type/icy_rock", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "d" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_WHELP_STRAIGHTLINE_FIRE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TARGET_TREE", + "target_source": "/datum/target_source/oview_single_type/flora_tree", + "targeting_strategy": "/datum/targeting_strategy/non_stump_tree", + "vision_range": 9 + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm index 7d4c74747a33..66e99d25bd2d 100644 --- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm @@ -47,7 +47,6 @@ AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY) grant_actions_by_list(innate_actions) - ai_controller.set_blackboard_key(BB_TARGETED_ACTION, ai_controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE]) /mob/living/basic/mining/ice_whelp/early_melee_attack(atom/target, list/modifiers, ignore_cooldown) . = ..() diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_abilities.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_abilities.dm index f406dda9088d..1c76f0c6c5a1 100644 --- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_abilities.dm +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_abilities.dm @@ -33,7 +33,7 @@ owner.Shake(pixelshiftx = 1, pixelshifty = 0, duration = forecast_delay) forecast_overlay.setDir(get_dir(owner, target_turf)) owner.add_overlay(forecast_overlay) - var/succeeded = do_after(owner, delay = forecast_delay, target = owner, hidden = TRUE) + var/succeeded = do_after(owner, delay = forecast_delay, target = owner, cog_icon = null) owner.cut_overlay(forecast_overlay) if (succeeded) playsound(owner.loc, fire_sound, 200, TRUE) diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm index 34a4b3631363..c84ad2f62222 100644 --- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm @@ -5,154 +5,4 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/ice_whelp, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/ice_whelp, - /datum/ai_planning_subtree/sculpt_statues, - /datum/ai_planning_subtree/find_and_hunt_target/corpses/ice_whelp, - /datum/ai_planning_subtree/burn_trees, - ) - -/// Cancel melee attacks when we have our breath weapon -/datum/ai_planning_subtree/basic_melee_attack_subtree/ice_whelp - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/ice_whelp - -/// Cancel melee attacks when we have our breath weapon -/datum/ai_behavior/basic_melee_attack/ice_whelp - -/datum/ai_behavior/basic_melee_attack/ice_whelp/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/datum/action/cooldown/breath_weapon = controller.blackboard[BB_TARGETED_ACTION] - if (breath_weapon?.IsAvailable()) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - return ..() - -/// Find other tasty dragons -/datum/ai_planning_subtree/find_and_hunt_target/corpses/ice_whelp - target_key = BB_TARGET_CANNIBAL - finding_behavior = /datum/ai_behavior/find_hunt_target/corpses/dragon_corpse - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise - hunt_targets = list(/mob/living/basic/mining/ice_whelp) - hunt_range = 10 - -/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse - -/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse/valid_dinner(mob/living/source, mob/living/dinner, radius) - if(dinner.pulledby) //someone already got him before us - return FALSE - return ..() - -/// Eat other dragons -/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key) - var/mob/living/target = controller.blackboard[target_key] - if(QDELETED(target) || target.stat != DEAD || target.pulledby) //we were too slow - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - return ..() - -/datum/ai_behavior/cannibalize/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -///subtree to find icy rocks and create sculptures out of them -/datum/ai_planning_subtree/sculpt_statues - -/datum/ai_planning_subtree/sculpt_statues/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_TARGET_ROCK)) - controller.queue_behavior(/datum/ai_behavior/sculpt_statue, BB_TARGET_ROCK) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_TARGET_ROCK, /obj/structure/flora/rock/icy) - -/datum/ai_behavior/sculpt_statue - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 5 MINUTES - -/datum/ai_behavior/sculpt_statue/setup(datum/ai_controller/controller, target_key) - . = ..() - var/obj/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/sculpt_statue/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - if(!controller.ai_interact(target = target_key, combat_mode = FALSE)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/sculpt_statue/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/// Only use ability if we are within range -/datum/ai_planning_subtree/targeted_mob_ability/ice_whelp - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/ice_whelp - -/datum/ai_planning_subtree/targeted_mob_ability/ice_whelp/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action) - var/atom/target = controller.blackboard[target_key] - var/range_to_target = get_dist(controller.pawn, target) - return range_to_target < /datum/action/cooldown/mob_cooldown/fire_breath/ice::fire_range - -/// Select appropriate ability based on range -/datum/ai_behavior/targeted_mob_ability/ice_whelp - -/datum/ai_behavior/targeted_mob_ability/ice_whelp/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) - var/mob/living/target = controller.blackboard[target_key] - if (isnull(target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/dist_to_target = get_dist(controller.pawn, target) - var/datum/action/cooldown/mob_cooldown/fire_breath/ice/short_range_ability = controller.blackboard[BB_WHELP_WIDESPREAD_FIRE] - if (isnull(short_range_ability) || dist_to_target > short_range_ability.fire_range) - controller.set_blackboard_key(BB_TARGETED_ACTION, controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE]) - return ..() - - controller.set_blackboard_key(BB_TARGETED_ACTION, controller.blackboard[BB_WHELP_WIDESPREAD_FIRE]) - return ..() - -///subtree to look for trees and burn them with our flamethrower -/datum/ai_planning_subtree/burn_trees - -/datum/ai_planning_subtree/burn_trees/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/action/cooldown/using_action = controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE] - if (!using_action?.IsAvailable()) - return - - if(controller.blackboard_key_exists(BB_TARGET_TREE)) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees, BB_WHELP_STRAIGHTLINE_FIRE, BB_TARGET_TREE) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/set_target_tree, BB_TARGET_TREE) - -/datum/ai_behavior/set_target_tree - -/datum/ai_behavior/set_target_tree/perform(seconds_per_tick, datum/ai_controller/controller, tree_key) - var/mob/living_pawn = controller.pawn - var/list/possible_trees = list() - - for(var/obj/structure/flora/tree/possible_tree in oview(9, living_pawn)) - if(istype(possible_tree, /obj/structure/flora/tree/stump)) //no leaves to burn - continue - possible_trees += possible_tree - - if(!length(possible_trees)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(tree_key, pick(possible_trees)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 2 - action_cooldown = 2 MINUTES - -/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees/setup(datum/ai_controller/controller, ability_key, target_key) - . = ..() - var/obj/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) + behavior_tree_json = "code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.bt.json" diff --git a/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.json b/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.json new file mode 100644 index 000000000000..25907280dd25 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.json @@ -0,0 +1,40 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/polar", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat", + "bindings": { + "bbrbyj7y": "/datum/bt_node/ai_behavior/random_speech/bear" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enrage" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm b/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm index 5d838db8fe8a..a13bdc5faec6 100644 --- a/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm +++ b/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm @@ -53,6 +53,7 @@ faction = list(FACTION_NEUTRAL) /datum/ai_controller/basic_controller/polar + behavior_tree_json = "code/modules/mob/living/basic/icemoon/polar_bear/polar.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, @@ -60,12 +61,3 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/enrage, - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/bear, - ) diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.json b/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.json new file mode 100644 index 000000000000..930b82e9049a --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/wolf/wolf.bt.json @@ -0,0 +1,120 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/wolf", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf_ai.dm b/code/modules/mob/living/basic/icemoon/wolf/wolf_ai.dm index 28618bfebcaf..a30270070c97 100644 --- a/code/modules/mob/living/basic/icemoon/wolf/wolf_ai.dm +++ b/code/modules/mob/living/basic/icemoon/wolf/wolf_ai.dm @@ -16,27 +16,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk + behavior_tree_json = "code/modules/mob/living/basic/icemoon/wolf/wolf.bt.json" - //reinforcements needs to be skipped over entirely on tamed wolves because it causes them to attack their owner and then themselves - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/call_reinforcements/wolf, - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/call_reinforcements/wolf - -/datum/ai_planning_subtree/call_reinforcements/wolf/decide_to_call(datum/ai_controller/controller) - //only call reinforcements if the person who just smacked us isn't a friend to avoid hitting them once, then killing ourselves if we've been tamed - if (controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && istype(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET], /mob)) - return !(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] in controller.blackboard[BB_FRIENDS_LIST]) - else - return FALSE diff --git a/code/modules/mob/living/basic/illusion/escape.bt.json b/code/modules/mob/living/basic/illusion/escape.bt.json new file mode 100644 index 000000000000..5fbf7dc4fb3e --- /dev/null +++ b/code/modules/mob/living/basic/illusion/escape.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/illusion/escape", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pick_retaliate_target" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/illusion/illlusion_ai.dm b/code/modules/mob/living/basic/illusion/illlusion_ai.dm index 48270fc25838..60580df19f0b 100644 --- a/code/modules/mob/living/basic/illusion/illlusion_ai.dm +++ b/code/modules/mob/living/basic/illusion/illlusion_ai.dm @@ -2,42 +2,20 @@ /// For the time being however, the AI is very simple and doesn't rely on any advanced tactics. Just go to thing it was assigned to attack and attack it (if assigned, else wander around) /// However, the action we undergo is based on the subtype of illusion we are and that's done on the mob subtype level. /datum/ai_controller/basic_controller/illusion + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_traits = DEFAULT_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Escape subtype of illusions are made to flee from threats rather than attack them. They do not undergo any retaliation behavior. /// We also want to account for the possibility of new threats attacking us and fleeing from those too, more randomness is ideal. /datum/ai_controller/basic_controller/illusion/escape - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, // we don't need the special illusion one here - BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - ai_traits = DEFAULT_AI_FLAGS - ai_movement = /datum/ai_movement/basic_avoidance - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - ) + behavior_tree_json = "code/modules/mob/living/basic/illusion/escape.bt.json" /// Retaliate subtypes of escape illusions can fight back against threats that attack them, making them more dangerous. /datum/ai_controller/basic_controller/illusion/escape/retaliate - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/mob/living/basic/illusion/retaliate.bt.json" diff --git a/code/modules/mob/living/basic/illusion/illusion.dm b/code/modules/mob/living/basic/illusion/illusion.dm index 97a61e653f0c..3ae32ca3a483 100644 --- a/code/modules/mob/living/basic/illusion/illusion.dm +++ b/code/modules/mob/living/basic/illusion/illusion.dm @@ -26,7 +26,7 @@ /// Prob of getting a clone on attack var/multiply_chance = 0 /// The blackboard key we want to set for our target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET + var/target_key = BB_CURRENT_TARGET /mob/living/basic/illusion/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/basic/illusion/retaliate.bt.json b/code/modules/mob/living/basic/illusion/retaliate.bt.json new file mode 100644 index 000000000000..a9e61267291d --- /dev/null +++ b/code/modules/mob/living/basic/illusion/retaliate.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/illusion/escape/retaliate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/pick_retaliate_target" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/jungle/human_trap.bt.json b/code/modules/mob/living/basic/jungle/human_trap.bt.json new file mode 100644 index 000000000000..ac0c6499b8c4 --- /dev/null +++ b/code/modules/mob/living/basic/jungle/human_trap.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/human_trap", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_ranged_combat" +} diff --git a/code/modules/mob/living/basic/jungle/leaper/leaper.bt.json b/code/modules/mob/living/basic/jungle/leaper/leaper.bt.json new file mode 100644 index 000000000000..2dda6824c590 --- /dev/null +++ b/code/modules/mob/living/basic/jungle/leaper/leaper.bt.json @@ -0,0 +1,122 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/leaper", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_LEAPER_BUBBLE", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_LEAPER_FLOP", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_LEAPER_VOLLEY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_LEAPER_SUMMON", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/jungle/leaper/leaper_ai.dm b/code/modules/mob/living/basic/jungle/leaper/leaper_ai.dm index 1e7a6dee29ee..27c517ff4825 100644 --- a/code/modules/mob/living/basic/jungle/leaper/leaper_ai.dm +++ b/code/modules/mob/living/basic/jungle/leaper/leaper_ai.dm @@ -5,40 +5,7 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/targeted_mob_ability/pointed_bubble, - /datum/ai_planning_subtree/targeted_mob_ability/flop, - /datum/ai_planning_subtree/targeted_mob_ability/volley, - /datum/ai_planning_subtree/targeted_mob_ability/summon, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/go_for_swim, - ) - -/datum/ai_planning_subtree/targeted_mob_ability/pointed_bubble - ability_key = BB_LEAPER_BUBBLE - finish_planning = FALSE - -/datum/ai_planning_subtree/targeted_mob_ability/flop - ability_key = BB_LEAPER_FLOP - finish_planning = FALSE - -/datum/ai_planning_subtree/targeted_mob_ability/flop/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(isclosedturf(current_target) || isspaceturf(current_target)) - return - return ..() - -/datum/ai_planning_subtree/targeted_mob_ability/volley - ability_key = BB_LEAPER_VOLLEY - finish_planning = FALSE - -/datum/ai_planning_subtree/targeted_mob_ability/summon - ability_key = BB_LEAPER_SUMMON - finish_planning = FALSE + behavior_tree_json = "code/modules/mob/living/basic/jungle/leaper/leaper.bt.json" /datum/pet_command/use_ability/flop command_name = "Flop" diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.json b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.json new file mode 100644 index 000000000000..333cf00241ca --- /dev/null +++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.json @@ -0,0 +1,96 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mega_arachnid", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/mega_arachnid_combat" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SURVEILLANCE_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SURVEILLANCE_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_SURVEILLANCE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_SURVEILLANCE_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/climb_tree" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SURVEILLANCE_TARGET", + "target_source": "/datum/target_source/oview_typed/surveillance_equipment", + "targeting_strategy": "/datum/targeting_strategy/working_machine", + "vision_range": 7 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CLIMBED_TREE", + "target_source": "/datum/target_source/oview_single_type/flora_tree", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + ] +} diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm index fbe737eb2b2a..15a47f283cab 100644 --- a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm +++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm @@ -1,79 +1,15 @@ /datum/ai_controller/basic_controller/mega_arachnid + behavior_tree_json = "code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_BASIC_MOB_FLEE_DISTANCE = 5, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/arachnid_restrain, - /datum/ai_planning_subtree/basic_melee_attack_subtree/mega_arachnid, - /datum/ai_planning_subtree/flee_target/mega_arachnid, - /datum/ai_planning_subtree/climb_trees, - /datum/ai_planning_subtree/find_and_hunt_target/destroy_surveillance, - ) - -///destroy surveillance objects to boost our stealth -/datum/ai_planning_subtree/find_and_hunt_target/destroy_surveillance - target_key = BB_SURVEILLANCE_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/find_active_surveillance - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target - hunt_targets = list(/obj/machinery/camera, /obj/machinery/light) - hunt_range = 7 - -/datum/ai_behavior/find_hunt_target/find_active_surveillance - -/datum/ai_behavior/find_hunt_target/find_active_camera/valid_dinner(mob/living/source, obj/machinery/dinner, radius) - if(dinner.machine_stat & BROKEN) - return FALSE - - return can_see(source, dinner, radius) - -///spray slippery acid as we flee! -/datum/ai_planning_subtree/flee_target/mega_arachnid - flee_behaviour = /datum/ai_behavior/run_away_from_target/mega_arachnid - -/datum/ai_planning_subtree/flee_target/mega_arachnid/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return - var/datum/action/cooldown/slip_acid = controller.blackboard[BB_ARACHNID_SLIP] - - if(!QDELETED(slip_acid) && slip_acid.IsAvailable()) - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_ARACHNID_SLIP) - - return ..() - -/datum/ai_behavior/run_away_from_target/mega_arachnid - clear_failed_targets = FALSE - -///only engage in melee combat against cuffed targets, otherwise keep throwing restraints at them -/datum/ai_planning_subtree/basic_melee_attack_subtree/mega_arachnid - ///minimum health our target must be before we can attack them - var/minimum_health = 50 - -/datum/ai_planning_subtree/basic_melee_attack_subtree/mega_arachnid/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(!ishuman(target)) - return ..() - - var/mob/living/carbon/human_target = target - if(!human_target.legcuffed && human_target.health > minimum_health) - return - return ..() +/datum/target_source/oview_typed/surveillance_equipment + typecache = list(/obj/machinery/camera = TRUE, /obj/machinery/light = TRUE) -/datum/ai_planning_subtree/targeted_mob_ability/arachnid_restrain - ability_key = BB_ARACHNID_RESTRAIN -/// only fire ability at humans if they are not cuffed -/datum/ai_planning_subtree/targeted_mob_ability/arachnid_restrain/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[target_key] - if(!ishuman(target)) - return - var/mob/living/carbon/human_target = target - if(human_target.legcuffed) - return - return ..() +/datum/bt_node/subtree/mega_arachnid_combat + behavior_tree_json = "code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.json" diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.json b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.json new file mode 100644 index 000000000000..bb4cdb6b8655 --- /dev/null +++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_combat.bt.json @@ -0,0 +1,147 @@ +{ + "dm_type": "/datum/bt_node/subtree/mega_arachnid_combat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_ARACHNID_SLIP" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_ARACHNID_SLIP" + } + } + } + ] + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_type", + "vars": { + "target_type": "/mob/living/carbon/human" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_legcuffed", + "vars": { + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_ARACHNID_RESTRAIN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_ARACHNID_RESTRAIN", + "target_key": "BB_CURRENT_TARGET" + } + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_type", + "vars": { + "target_type": "/mob/living/carbon/human", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_legcuffed", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_health_below_fraction", + "vars": { + "key": "BB_CURRENT_TARGET", + "fraction": 0.5 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling.bt.json b/code/modules/mob/living/basic/jungle/seedling/seedling.bt.json new file mode 100644 index 000000000000..8fff5f8770d2 --- /dev/null +++ b/code/modules/mob/living/basic/jungle/seedling/seedling.bt.json @@ -0,0 +1,286 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/seedling", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/override_id_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_reagent", + "vars": { + "invert": false, + "key": "BB_WATERCAN_TARGET", + "reagent_type": "/datum/reagent/water" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HYDROPLANT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HYDROPLANT_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_HYDROPLANT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_HYDROPLANT_TARGET" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_SOLARBEAM_ABILITY", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BEAMABLE_HYDROPLANT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BEAMABLE_HYDROPLANT_TARGET", + "required_dist": 2 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_SOLARBEAM_ABILITY", + "target_key": "BB_BEAMABLE_HYDROPLANT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_BEAMABLE_HYDROPLANT_TARGET" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/item_inside_pawn", + "vars": { + "key": "BB_WATERCAN_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_reagent", + "vars": { + "invert": true, + "key": "BB_WATERCAN_TARGET", + "reagent_type": "/datum/reagent/water" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + } + } + ] + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/item_inside_pawn", + "vars": { + "invert": true, + "key": "BB_WATERCAN_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_WATERCAN_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_WATERCAN_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_WATERCAN_TARGET" + } + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/item_inside_pawn", + "vars": { + "invert": true, + "key": "BB_WATERCAN_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_WATERCAN_TARGET", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_single_type/watering_can" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/item_inside_pawn", + "vars": { + "key": "BB_WATERCAN_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_reagent", + "vars": { + "invert": true, + "key": "BB_WATERCAN_TARGET", + "reagent_type": "/datum/reagent/water" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview", + "targeting_strategy": "/datum/targeting_strategy/water_dispenser" + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HYDROPLANT_TARGET", + "target_source": "/datum/target_source/oview", + "targeting_strategy": "/datum/targeting_strategy/treatable_hydro", + "time_between_perform": "5 SECONDS" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_SOLARBEAM_ABILITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_BEAMABLE_HYDROPLANT_TARGET", + "target_source": "/datum/target_source/oview", + "targeting_strategy": "/datum/targeting_strategy/beamable_hydro", + "time_between_perform": "4 SECONDS" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling.dm b/code/modules/mob/living/basic/jungle/seedling/seedling.dm index 49acc32e5ee0..da8a28c08a74 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling.dm @@ -138,12 +138,12 @@ combatant_state = state update_appearance() -/mob/living/basic/seedling/attackby(obj/item/can, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers) - if(istype(can, /obj/item/reagent_containers/cup/watering_can) && isnull(held_can)) - can.forceMove(src) - return +/mob/living/basic/seedling/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/reagent_containers/cup/watering_can) || held_can) + return ..() - return ..() + tool.forceMove(src) + return ITEM_INTERACT_SUCCESS /mob/living/basic/seedling/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) if(istype(arrived, /obj/item/reagent_containers/cup/watering_can)) diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm index fe4f3204f26c..18705d45e71b 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm @@ -5,167 +5,15 @@ BB_WEEDLEVEL_THRESHOLD = 3, BB_WATERLEVEL_THRESHOLD = 90, ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_and_hunt_target/watering_can, - /datum/ai_planning_subtree/find_and_hunt_target/fill_watercan, - /datum/ai_planning_subtree/find_and_hunt_target/treat_hydroplants, - /datum/ai_planning_subtree/find_and_hunt_target/beamable_hydroplants, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/watering_can - target_key = BB_WATERCAN_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target - hunt_targets = list(/obj/item/reagent_containers/cup/watering_can) - hunt_range = 7 - -/datum/ai_planning_subtree/find_and_hunt_target/watering_can/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(locate(/obj/item/reagent_containers/cup/watering_can) in living_pawn) //we already have what we came for! - return - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/treat_hydroplants - target_key = BB_HYDROPLANT_TARGET - finding_behavior = /datum/ai_behavior/find_and_set/treatable_hydro - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant - hunt_targets = list(/obj/machinery/hydroponics) - hunt_range = 7 - -/datum/ai_behavior/find_and_set/treatable_hydro - action_cooldown = 5 SECONDS - -/datum/ai_behavior/find_and_set/treatable_hydro/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/possible_trays = list() - var/mob/living/living_pawn = controller.pawn - var/waterlevel_threshold = controller.blackboard[BB_WATERLEVEL_THRESHOLD] - var/weedlevel_threshold = controller.blackboard[BB_WEEDLEVEL_THRESHOLD] - var/watering_can = locate(/obj/item/reagent_containers/cup/watering_can) in living_pawn - - for(var/obj/machinery/hydroponics/hydro in oview(search_range, controller.pawn)) - if(isnull(hydro.myseed)) - continue - if(hydro.waterlevel < waterlevel_threshold && watering_can) - possible_trays += hydro - continue - if(hydro.weedlevel > weedlevel_threshold || hydro.plant_status == HYDROTRAY_PLANT_DEAD) - possible_trays += hydro - continue - - if(possible_trays.len) - return pick(possible_trays) - -/datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant - hunt_cooldown = 2 SECONDS - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant/target_caught(mob/living/living_pawn, obj/machinery/hydroponics/hydro_target) - if(QDELETED(hydro_target) || QDELETED(hydro_target.myseed)) - return - - if(hydro_target.plant_status == HYDROTRAY_PLANT_DEAD) - living_pawn.manual_emote("weeps...") //weep over the dead plants - return ..() - - -/datum/ai_planning_subtree/find_and_hunt_target/beamable_hydroplants - target_key = BB_BEAMABLE_HYDROPLANT_TARGET - finding_behavior = /datum/ai_behavior/find_and_set/beamable_hydroplants - hunting_behavior = /datum/ai_behavior/hunt_target/use_ability_on_target/solarbeam - hunt_targets = list(/obj/machinery/hydroponics) - hunt_range = 7 - -/datum/ai_planning_subtree/find_and_hunt_target/beamable_hydroplants/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/action/cooldown/solar_ability = controller.blackboard[BB_SOLARBEAM_ABILITY] - if(QDELETED(solar_ability) || !solar_ability.IsAvailable()) - return - return ..() - -/datum/ai_behavior/hunt_target/use_ability_on_target/solarbeam - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 2 - action_cooldown = 1 MINUTES - ability_key = BB_SOLARBEAM_ABILITY - -/datum/ai_behavior/hunt_target/use_ability_on_target/solarbeam/setup(datum/ai_controller/controller, target_key, ability_key) - . = ..() - var/obj/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/find_and_set/beamable_hydroplants - action_cooldown = 15 SECONDS - -/datum/ai_behavior/find_and_set/beamable_hydroplants/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/possible_trays = list() - - for(var/obj/machinery/hydroponics/hydro in oview(search_range, controller.pawn)) - if(isnull(hydro.myseed)) - continue - if(hydro.plant_health < hydro.myseed.endurance) - possible_trays += hydro - - if(possible_trays.len) - return pick(possible_trays) - -/datum/ai_planning_subtree/find_and_hunt_target/fill_watercan - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/suitable_dispenser - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/water_source - hunt_targets = list(/obj/structure/sink, /obj/structure/reagent_dispensers) - hunt_range = 7 - -/datum/ai_planning_subtree/find_and_hunt_target/fill_watercan/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/obj/item/reagent_containers/can = locate(/obj/item/reagent_containers/cup/watering_can) in living_pawn - - if(isnull(can)) - return - if(locate(/datum/reagent/water) in can.reagents.reagent_list) - return - - return ..() - -/datum/ai_behavior/find_hunt_target/suitable_dispenser - -/datum/ai_behavior/find_hunt_target/suitable_dispenser/valid_dinner(mob/living/source, obj/structure/water_source, radius) - if(!(locate(/datum/reagent/water) in water_source.reagents.reagent_list)) - return FALSE - - return can_see(source, water_source, radius) - -/datum/ai_behavior/hunt_target/interact_with_target/water_source - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - always_reset_target = TRUE - hunt_cooldown = 5 SECONDS + behavior_tree_json = "code/modules/mob/living/basic/jungle/seedling/seedling.bt.json" /datum/ai_controller/basic_controller/seedling/meanie blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/seedling_rapid, - /datum/ai_planning_subtree/targeted_mob_ability/solarbeam, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/targeted_mob_ability/seedling_rapid - ability_key = BB_RAPIDSEEDS_ABILITY - finish_planning = FALSE - -/datum/ai_planning_subtree/targeted_mob_ability/solarbeam - ability_key = BB_SOLARBEAM_ABILITY - finish_planning = FALSE + behavior_tree_json = "code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.json" ///pet commands /datum/pet_command/use_ability/solarbeam diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.json b/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.json new file mode 100644 index 000000000000..d050e7bf8f0b --- /dev/null +++ b/code/modules/mob/living/basic/jungle/seedling/seedling_meanie.bt.json @@ -0,0 +1,106 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/seedling/meanie", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_RAPIDSEEDS_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_SOLARBEAM_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/jungle/venus_human_trap.dm b/code/modules/mob/living/basic/jungle/venus_human_trap.dm index 3d2538b3c5c1..b3dc776c33b9 100644 --- a/code/modules/mob/living/basic/jungle/venus_human_trap.dm +++ b/code/modules/mob/living/basic/jungle/venus_human_trap.dm @@ -256,18 +256,11 @@ vines -= vine /datum/ai_controller/basic_controller/human_trap + behavior_tree_json = "code/modules/mob/living/basic/jungle/human_trap.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/continue_planning, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) #undef FINAL_BUD_GROWTH_ICON diff --git a/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.json b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.json new file mode 100644 index 000000000000..e3d26ddf95ba --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.json @@ -0,0 +1,116 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/basilisk", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_trait", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true, + "key": "TRAIT_OVERWATCHED" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "min_distance": 2 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "max_range": 9 + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm index 3d6380d25be7..388b8d1cb8db 100644 --- a/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm +++ b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm @@ -67,7 +67,7 @@ /// We got hit by something hot, go into heat mode /mob/living/basic/mining/basilisk/proc/heat_up() - if (stat != CONSCIOUS || has_status_effect(/datum/status_effect/basilisk_overheat)) + if (IS_UNCONSCIOUS_OR_CRIT(src) || has_status_effect(/datum/status_effect/basilisk_overheat)) return apply_status_effect(/datum/status_effect/basilisk_overheat) @@ -76,17 +76,10 @@ ranged_attacks.projectile_type = projectile_type /datum/ai_controller/basic_controller/basilisk + behavior_tree_json = "code/modules/mob/living/basic/lavaland/basilisk/basilisk.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_AGGRO_RANGE = 5, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/ranged_skirmish, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm b/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm index 04961f7b2b52..d8b1c4b37dd2 100644 --- a/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm +++ b/code/modules/mob/living/basic/lavaland/basilisk/basilisk_overheat.dm @@ -14,7 +14,7 @@ /datum/status_effect/basilisk_overheat/on_apply() . = ..() - if (!. || !istype(owner, /mob/living/basic/mining/basilisk) || owner.stat != CONSCIOUS) + if (!. || !istype(owner, /mob/living/basic/mining/basilisk) || IS_UNCONSCIOUS_OR_CRIT(owner)) return FALSE var/mob/living/basic/mining/basilisk/hot_stuff = owner hot_stuff.visible_message(span_warning("[hot_stuff] is getting fired up!")) @@ -40,7 +40,7 @@ hot_stuff.remove_movespeed_modifier(/datum/movespeed_modifier/basilisk_overheat) UnregisterSignal(hot_stuff, list(COMSIG_LIVING_DEATH, COMSIG_ATOM_EXPOSE_REAGENTS, COMSIG_ATOM_BULLET_ACT)) - if (hot_stuff.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(hot_stuff)) return hot_stuff.visible_message(span_notice("[hot_stuff] seems to have cooled down.")) var/obj/effect/particle_effect/fluid/smoke/poof = new(get_turf(hot_stuff)) diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.json b/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.json new file mode 100644 index 000000000000..224a83c4cc1e --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.json @@ -0,0 +1,143 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bileworm", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_BILEWORM_DEVOUR" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/mob_stat_at_least", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_BASIC_MOB_EXECUTION_TARGET", + "min_stat": "UNCONSCIOUS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_BILEWORM_DEVOUR", + "target_key": "BB_BASIC_MOB_EXECUTION_TARGET", + "maximum_distance": 16 + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": false, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_BILEWORM_RESURFACE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bileworm_should_resurface", + "vars": { + "target_key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute", + "vars": { + "ability_key": "BB_BILEWORM_RESURFACE", + "target_key": "BB_CURRENT_TARGET" + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_BILEWORM_SPEW_BILE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_plan_execute", + "vars": { + "ability_key": "BB_BILEWORM_SPEW_BILE", + "target_key": "BB_CURRENT_TARGET" + } + } + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm index d3a45a89a762..a0241fa8dabc 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm @@ -29,7 +29,7 @@ to_chat(burrower, span_warning("Couldn't burrow anywhere near the target!")) if(burrower.ai_controller?.ai_status == AI_STATUS_ON) //this is a valid reason to give up on a target - burrower.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + burrower.ai_controller.clear_blackboard_key(BB_CURRENT_TARGET) return if (istype(burrower, /mob/living/basic/mining/bileworm) && !force) @@ -50,7 +50,7 @@ jump_damaged = FALSE RegisterSignal(worm, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) // Not in an if check direclty to reduce duplicate code - var/jump_result = do_after(worm, jump_length, worm, hidden = TRUE, extra_checks = CALLBACK(src, PROC_REF(damage_check))) + var/jump_result = do_after(worm, jump_length, worm, cog_icon = null, extra_checks = CALLBACK(src, PROC_REF(damage_check))) UnregisterSignal(worm, COMSIG_ATOM_WAS_ATTACKED) if (worm.icon_state == null) worm.icon_state = old_icon_state @@ -81,7 +81,7 @@ #undef BILEWORM_JUMP_FRAMES /datum/action/cooldown/mob_cooldown/resurface/proc/burrow_again(mob/living/burrower, atom/target) - if (!QDELETED(burrower) && !burrower.stat) + if (!QDELETED(burrower) && !IS_UNCONSCIOUS_OR_CRIT(burrower)) // Burrow immediatelly after being stunned out of the first jump to avoid chainstuns burrow(burrower, target, force = TRUE) @@ -275,7 +275,7 @@ to_chat(owner, span_warning("That's not food!")) return var/mob/living/living_target = target_atom - if(living_target.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(living_target)) to_chat(owner, span_warning("No way you're eating that while it's still kicking! It should at least be unconscious first.")) return burrow_and_devour(owner, living_target) diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm index cd2450018cf3..98be1f45896f 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm @@ -1,54 +1,25 @@ /datum/ai_controller/basic_controller/bileworm + behavior_tree_json = "code/modules/mob/living/basic/lavaland/bileworm/bileworm.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/bileworm, BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, BB_BILEWORM_FLEE_DISTANCE = 3, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/bileworm_attack, - /datum/ai_planning_subtree/bileworm_execute, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_TREAT_UNCONSCIOUS_AS_HARDCRIT = TRUE, ) /datum/targeting_strategy/basic/bileworm ignore_sight = TRUE -/datum/ai_planning_subtree/bileworm_attack - -/datum/ai_planning_subtree/bileworm_attack/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - - var/datum/action/cooldown/mob_cooldown/resurface = controller.blackboard[BB_BILEWORM_RESURFACE] - var/datum/action/cooldown/mob_cooldown/bile = controller.blackboard[BB_BILEWORM_SPEW_BILE] - - if(resurface?.IsAvailable() && (controller.blackboard[BB_BILEWORM_SCARED] || get_dist(controller.pawn, controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) <= controller.blackboard[BB_BILEWORM_FLEE_DISTANCE])) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_RESURFACE, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - if(bile?.IsAvailable()) - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_SPEW_BILE, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING //focus on the fight - -/datum/ai_planning_subtree/bileworm_execute - -/datum/ai_planning_subtree/bileworm_execute/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - var/atom/movable/target = controller.blackboard[BB_BASIC_MOB_EXECUTION_TARGET] - if(QDELETED(target) || !isliving(target)) - return - - var/datum/action/cooldown/mob_cooldown/devour = controller.blackboard[BB_BILEWORM_DEVOUR] - - if(!(devour?.IsAvailable())) - return - - var/mob/living/living_target = target - if(living_target.stat < UNCONSCIOUS) - return - - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target, BB_BILEWORM_DEVOUR, BB_BASIC_MOB_EXECUTION_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING //focus on devouring this fool +/// Passes when the worm should burrow and reposition: it's been scared, or its target has gotten within flee distance. +/datum/bt_node/decorator/bileworm_should_resurface + /// Blackboard key holding the atom we want to keep our distance from. + var/target_key = BB_CURRENT_TARGET + +/datum/bt_node/decorator/bileworm_should_resurface/check_condition(datum/ai_controller/controller) + if(controller.blackboard[BB_BILEWORM_SCARED]) + return TRUE + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + return get_dist(controller.pawn, target) <= controller.blackboard[BB_BILEWORM_FLEE_DISTANCE] diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm index 41a1a0f228a3..a82efd68cf67 100644 --- a/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm @@ -7,7 +7,7 @@ background_icon_state = "bg_demon" overlay_icon_state = "bg_demon_border" click_to_activate = TRUE - cooldown_time = 5 SECONDS + cooldown_time = 3 SECONDS melee_cooldown_time = 0 /// How far does our beam go? var/beam_range = 10 @@ -62,7 +62,7 @@ demon.icon_state = demon.firing_icon_state demon.update_appearance(UPDATE_OVERLAYS) - do_after(owner, delay = beam_duration, target = owner, hidden = TRUE, extra_checks = CALLBACK(src, PROC_REF(beam_charge_check))) + do_after(owner, delay = beam_duration, target = owner, cog_icon = null, extra_checks = CALLBACK(src, PROC_REF(beam_charge_check))) UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED) extinguish_laser() StartCooldown() diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.json b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.json new file mode 100644 index 000000000000..ea42be930a4d --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.json @@ -0,0 +1,122 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/brimdemon", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/brimbeam", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 9 + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_cardinal", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "minimum_distance": 2, + "maximum_distance": 9 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm index b60d7ab4a906..78af06d27424 100644 --- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm @@ -9,41 +9,11 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/no_target - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, - /datum/ai_planning_subtree/move_to_cardinal/brimdemon, - /datum/ai_planning_subtree/targeted_mob_ability/brimbeam, - ) - -/datum/ai_planning_subtree/move_to_cardinal/brimdemon - move_behaviour = /datum/ai_behavior/move_to_cardinal/brimdemon - -/datum/ai_behavior/move_to_cardinal/brimdemon - minimum_distance = 2 - -/datum/ai_behavior/move_to_cardinal/brimdemon/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - if(!succeeded) - return - var/mob/living/target = controller.blackboard[target_key] - var/datum/action/cooldown/ability = controller.blackboard[BB_TARGETED_ACTION] - if(QDELETED(target) || QDELETED(controller.pawn) || !ability?.IsAvailable()) - return - ability.InterceptClickOn(clicker = controller.pawn, target = target) - -/datum/ai_planning_subtree/targeted_mob_ability/brimbeam - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/brimbeam - -/datum/ai_behavior/targeted_mob_ability/brimbeam - /// Don't shoot if too far away - var/max_target_distance = 9 + behavior_tree_json = "code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.bt.json" -/datum/ai_behavior/targeted_mob_ability/brimbeam/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) +/// Brimdemon's beam only fires when the target is lined up on a cardinal direction. +/datum/bt_node/ai_behavior/targeted_mob_ability/brimbeam/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/target = controller.blackboard[target_key] - if (QDELETED(target) || !(get_dir(controller.pawn, target) in GLOB.cardinals) || get_dist(controller.pawn, target) > max_target_distance) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(QDELETED(target) || !(get_dir(controller.pawn, target) in GLOB.cardinals)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED return ..() diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.json b/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.json new file mode 100644 index 000000000000..9f009491b52a --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.json @@ -0,0 +1,93 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/babygrub", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/dig_away_from_danger" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/grub_eat_target", + "bindings": { + "bjwb8dxm": "BB_ORE_TARGET", + "bj13tsp3": "BB_ORE_TARGET", + "b6t594ql": "BB_ORE_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_FOUND_MOM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_MOM", + "required_dist": 1, + "finish_on_arrival": true + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mom" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.json b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.json new file mode 100644 index 000000000000..f445db7953f8 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.json @@ -0,0 +1,238 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/goldgrub", + "type": "selector", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] + }, + { + "type": "subtree", + "subtype": "", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/dig_away_from_danger" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/burrow_through_ground" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/grub_eat_target", + "bindings": { + "b6t594ql": "BB_BOULDER_TARGET", + "bj13tsp3": "BB_BOULDER_TARGET", + "bjwb8dxm": "BB_BOULDER_TARGETBB_BOULDER_TARGET" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/grub_eat_target", + "bindings": { + "b6t594ql": "BB_ORE_TARGET", + "bj13tsp3": "BB_ORE_TARGET", + "bjwb8dxm": "BB_ORE_TARGET" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/grub_eat_target", + "bindings": { + "b6t594ql": "BB_VENT_TARGET", + "bj13tsp3": "BB_VENT_TARGET", + "bjwb8dxm": "BB_VENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_TARGET_MINERAL_WALL" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_MINING_COOLDOWN", + "cooldown_duration": "4 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/mine_wall", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grab_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_boulder" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore_vent" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MINING_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mineral_wall", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_grub_egg" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm index 02273b82b061..f40de29f9b41 100644 --- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm @@ -93,7 +93,7 @@ /mob/living/basic/mining/goldgrub/proc/block_bullets(datum/source, obj/projectile/hitting_projectile) SIGNAL_HANDLER - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return NONE /// Reflects PKA/PKC shots and plasma cutter beams, unless they have high armor penetration @@ -198,7 +198,7 @@ ) /mob/living/basic/mining/goldgrub/baby/proc/ready_to_grow() - return (stat == CONSCIOUS && !is_jaunting(src)) + return (!IS_UNCONSCIOUS_OR_CRIT(src) && !is_jaunting(src)) /obj/item/food/egg/green/grub_egg name = "grub egg" diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm index 0f0cbbed3da4..e50003731f04 100644 --- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm @@ -1,5 +1,5 @@ -#define BURROW_RANGE 5 /datum/ai_controller/basic_controller/goldgrub + ai_traits = parent_type::ai_traits | RUN_WHILE_UNWATCHED blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -8,20 +8,7 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/burrow_through_ground, - /datum/ai_planning_subtree/dig_away_from_danger, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores, - /datum/ai_planning_subtree/find_and_hunt_target/break_boulders, - /datum/ai_planning_subtree/find_and_hunt_target/harvest_vents, - /datum/ai_planning_subtree/find_and_hunt_target/baby_egg, - /datum/ai_planning_subtree/mine_walls, - ) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.bt.json" /datum/ai_controller/basic_controller/babygrub blackboard = list( @@ -33,34 +20,77 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/dig_away_from_danger, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/look_for_adult, - ) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/goldgrub/babygrub.bt.json" + +/datum/pet_command/grub_spit + command_name = "Spit" + radial_icon = 'icons/obj/ore.dmi' + radial_icon_state = "uranium" + command_desc = "Ask your grub pet to spit out its ores." + speech_commands = list("spit", "ores") -/datum/ai_planning_subtree/burrow_through_ground +/datum/pet_command/grub_spit/execute_action(datum/ai_controller/controller) + var/datum/action/cooldown/spit_ability = controller.blackboard[BB_SPIT_ABILITY] + if(!spit_ability?.IsAvailable()) + return + controller.set_blackboard_key(BB_PET_ACTIVE_ABILITY, spit_ability) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/untargeted_ability) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) -/datum/ai_planning_subtree/burrow_through_ground/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(is_jaunting(controller.pawn) && controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - controller.queue_behavior(/datum/ai_behavior/burrow_through_ground, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/pet_command/grub_spit/retrieve_command_text(atom/living_pet, atom/target) + return "signals [living_pet] to spit its ores!" -/datum/ai_behavior/burrow_through_ground - action_cooldown = 10 SECONDS -/datum/ai_behavior/burrow_through_ground/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - if(!is_jaunting(controller.pawn) || QDELETED(target)) +#define BURROW_RANGE 5 + + +/** + * Burrows underground (or stays underground) when danger is present or a storm is approaching. + * Returns RUNNING while underground+storm (blocks everything). + * Returns FAILURE when no action needed so the selector passes through. + */ +/datum/bt_node/ai_behavior/dig_away_from_danger + +/datum/bt_node/ai_behavior/dig_away_from_danger/perform(seconds_per_tick, datum/ai_controller/controller) + var/currently_underground = is_jaunting(controller.pawn) + var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] + var/datum/action/cooldown/dig_ability = controller.blackboard[BB_BURROW_ABILITY] + + if(currently_underground && storm_approaching) + return AI_BEHAVIOR_DELAY // Stay underground while storm approaches RUNNING blocks everything + + if(!dig_ability?.IsAvailable()) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/has_target = controller.blackboard_key_exists(BB_CURRENT_TARGET) + + if(currently_underground && !has_target) + // No target/danger while underground emerge + INVOKE_ASYNC(dig_ability, TYPE_PROC_REF(/datum/action, Trigger)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + if(storm_approaching || has_target) + // Go underground to escape + INVOKE_ASYNC(dig_ability, TYPE_PROC_REF(/datum/action, Trigger)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + +/** + * While jaunting (underground) with a target, moves the grub's phased form further away. + * Returns FAILURE when not jaunting so the selector passes through. + */ +/datum/bt_node/ai_behavior/burrow_through_ground + time_between_perform = 10 SECONDS + +/datum/bt_node/ai_behavior/burrow_through_ground/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/living_pawn = controller.pawn - var/atom/movable/phased = living_pawn.loc + var/atom/target = controller.blackboard[BB_CURRENT_TARGET] + if(!is_jaunting(living_pawn) || QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/atom/movable/phased = living_pawn.loc var/list/turfs_list = RANGE_TURFS(BURROW_RANGE, phased) var/current_max_distance = 0 var/turf/selected_turf @@ -68,12 +98,10 @@ for(var/turf/possible_turf as anything in turfs_list) if(!ismineralturf(possible_turf) && !isasteroidturf(possible_turf)) continue - var/distance_to_target = get_dist(possible_turf, target) if(distance_to_target > current_max_distance) current_max_distance = distance_to_target selected_turf = possible_turf - if(distance_to_target == BURROW_RANGE) break @@ -81,152 +109,106 @@ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED phased.forceMove(selected_turf) - return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY - -///consume food! -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores - target_key = BB_ORE_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/hunt_ores - finding_behavior = /datum/ai_behavior/find_hunt_target/hunt_ores - hunt_targets = list(/obj/item/stack/ore) - hunt_chance = 90 - hunt_range = 9 - -/datum/ai_behavior/find_hunt_target/hunt_ores - -/datum/ai_behavior/find_hunt_target/hunt_ores/valid_dinner(mob/living/basic/source, obj/item/stack/ore/target, radius) - var/list/forbidden_ore = source.ai_controller.blackboard[BB_ORE_IGNORE_TYPES] - - if(is_type_in_list(target, forbidden_ore)) - return FALSE - - if(!isturf(target.loc)) - return FALSE - - var/obj/item/pet_target = source.ai_controller.blackboard[BB_CURRENT_PET_TARGET] - if(target == pet_target) //we are currently fetching this ore for master, dont eat it! - return FALSE - - return can_see(source, target, radius) - -/datum/ai_behavior/hunt_target/interact_with_target/hunt_ores - always_reset_target = TRUE - -///break boulders so that we can find more food! -/datum/ai_planning_subtree/find_and_hunt_target/harvest_vents - target_key = BB_VENT_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target //We call the ore vent's produce_boulder() proc here to produce a single boulder. - finding_behavior = /datum/ai_behavior/find_hunt_target/harvest_vents - hunt_targets = list(/obj/structure/ore_vent) - hunt_chance = 25 - hunt_range = 15 - -/datum/ai_behavior/find_hunt_target/harvest_vents - -/datum/ai_behavior/find_hunt_target/harvest_vents/valid_dinner(mob/living/basic/source, obj/structure/target, radius) - if(target in source) - return FALSE - - var/turf/vent_turf = target.drop_location() - var/counter = 0 - for(var/obj/item/boulder in vent_turf.contents) - counter++ - if(counter > MAX_BOULDERS_PER_VENT) //Too many items currently on the vent - return FALSE - - return can_see(source, target, radius) - -///break boulders so that we can find more food! -/datum/ai_planning_subtree/find_and_hunt_target/break_boulders - target_key = BB_BOULDER_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target //We process boulders once every tap, so we dont need to do anything special here - finding_behavior = /datum/ai_behavior/find_hunt_target/break_boulders - hunt_targets = list(/obj/item/boulder) - hunt_chance = 100 //If we can, we should always break boulders. - hunt_range = 9 - -/datum/ai_behavior/find_hunt_target/break_boulders - -/datum/ai_behavior/find_hunt_target/break_boulders/valid_dinner(mob/living/basic/source, obj/item/boulder/target, radius) - if(target in source) - return FALSE - - var/obj/item/pet_target = source.ai_controller.blackboard[BB_CURRENT_PET_TARGET] - if(target == pet_target) //we are currently fetching this ore for master, dont eat it! - return FALSE - return can_see(source, target, radius) - -///find our child's egg and pull it! -/datum/ai_planning_subtree/find_and_hunt_target/baby_egg - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/grub_egg - finding_behavior = /datum/ai_behavior/find_hunt_target - hunt_targets = list(/obj/item/food/egg/green/grub_egg) - hunt_chance = 75 - hunt_range = 9 - -/datum/ai_planning_subtree/find_and_hunt_target/baby_egg - -/datum/ai_planning_subtree/find_and_hunt_target/baby_egg/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.pulling) //we are already pulling something - return - return ..() - -/datum/ai_behavior/hunt_target/grub_egg - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/grub_egg/target_caught(mob/living/hunter, obj/item/target) - hunter.start_pulling(target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -///only dig away if storm is coming or if humans are around -/datum/ai_planning_subtree/dig_away_from_danger +/// Finds an ore pile. Sets BB_ORE_TARGET. Skips forbidden types and fetch targets. +/datum/bt_node/ai_behavior/find_ore + time_between_perform = 5 SECONDS + var/range = 9 -/datum/ai_planning_subtree/dig_away_from_danger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/currently_underground = is_jaunting(controller.pawn) - var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] - - //dont do anything until the storm passes - if(currently_underground && storm_approaching) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/find_ore/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/grub_pawn = controller.pawn + var/list/forbidden = controller.blackboard[BB_ORE_IGNORE_TYPES] + var/pet_target = controller.blackboard[BB_CURRENT_PET_TARGET] + for(var/obj/item/stack/ore/candidate in oview(range, grub_pawn)) + if(is_type_in_list(candidate, forbidden) || !isturf(candidate.loc)) + continue + if(candidate == pet_target) + continue + if(!can_see(grub_pawn, candidate, range)) + continue + controller.set_blackboard_key(BB_ORE_TARGET, candidate) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/// Finds a boulder the goldgrub can break. Sets BB_BOULDER_TARGET. +/datum/bt_node/ai_behavior/find_boulder + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/find_boulder/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/grub_pawn = controller.pawn + var/pet_target = controller.blackboard[BB_CURRENT_PET_TARGET] + for(var/obj/item/boulder/candidate in oview(9, grub_pawn)) + if(candidate == pet_target) + continue + if(!can_see(grub_pawn, candidate, 9)) + continue + controller.set_blackboard_key(BB_BOULDER_TARGET, candidate) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/datum/action/cooldown/dig_ability = controller.blackboard[BB_BURROW_ABILITY] +/// Finds an ore vent to harvest. Sets BB_VENT_TARGET. +/datum/bt_node/ai_behavior/find_ore_vent + time_between_perform = 10 SECONDS - if(!dig_ability.IsAvailable()) - return +/datum/bt_node/ai_behavior/find_ore_vent/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(25, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/grub_pawn = controller.pawn + for(var/obj/structure/ore_vent/candidate in oview(15, grub_pawn)) + if(candidate in grub_pawn) + continue + var/turf/vent_turf = candidate.drop_location() + var/counter = 0 + var/too_many = FALSE + for(var/obj/item/boulder in vent_turf.contents) + counter++ + if(counter > MAX_BOULDERS_PER_VENT) + too_many = TRUE + break + if(too_many || !can_see(grub_pawn, candidate, 15)) + continue + controller.set_blackboard_key(BB_VENT_TARGET, candidate) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/has_target = controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) +/// Finds a grub egg to protect/pull. Sets BB_LOW_PRIORITY_HUNTING_TARGET. +/datum/bt_node/ai_behavior/find_grub_egg + time_between_perform = 10 SECONDS - //a storm is coming or someone is nearby, its time to escape - if(currently_underground) - if(has_target) - return - controller.queue_behavior(/datum/ai_behavior/use_mob_ability/burrow, BB_BURROW_ABILITY) - return SUBTREE_RETURN_FINISH_PLANNING - if(storm_approaching || has_target) - controller.queue_behavior(/datum/ai_behavior/use_mob_ability/burrow, BB_BURROW_ABILITY) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/find_grub_egg/perform(seconds_per_tick, datum/ai_controller/controller) + if(!SPT_PROB(75, seconds_per_tick)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/grub_pawn = controller.pawn + if(grub_pawn.pulling) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + for(var/obj/item/food/egg/green/grub_egg/egg in oview(9, grub_pawn)) + if(!can_see(grub_pawn, egg, 9)) + continue + controller.set_blackboard_key(BB_LOW_PRIORITY_HUNTING_TARGET, egg) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_behavior/use_mob_ability/burrow - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION +/// Eats/interacts with a target at the given blackboard key. Must be adjacent. +/datum/bt_node/ai_behavior/grub_eat + var/target_key + time_between_perform = 3 SECONDS -/datum/pet_command/grub_spit - command_name = "Spit" - radial_icon = 'icons/obj/ore.dmi' - radial_icon_state = "uranium" - command_desc = "Ask your grub pet to spit out its ores." - speech_commands = list("spit", "ores") +/datum/bt_node/ai_behavior/grub_eat/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!controller.pawn.Adjacent(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target, FALSE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/pet_command/grub_spit/execute_action(datum/ai_controller/controller) - var/datum/action/cooldown/spit_ability = controller.blackboard[BB_SPIT_ABILITY] - if(!spit_ability?.IsAvailable()) - return - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_SPIT_ABILITY) - controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/grub_eat/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) -/datum/pet_command/grub_spit/retrieve_command_text(atom/living_pet, atom/target) - return "signals [living_pet] to spit its ores!" +/datum/bt_node/subtree/grub_eat_target + behavior_tree_json = "code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.json" #undef BURROW_RANGE diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.json b/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.json new file mode 100644 index 000000000000..13dc419c1094 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/grub_eat_target.bt.json @@ -0,0 +1,44 @@ +{ + "dm_type": "/datum/bt_node/subtree/grub_eat_target", + "bindings": { + "b6t594ql": { + "label": "target_key", + "default": "null" + }, + "bj13tsp3": { + "label": "target_key", + "default": "" + }, + "bjwb8dxm": { + "label": "target_key", + "default": "" + } + }, + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "$b6t594ql" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "$bj13tsp3", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/grub_eat", + "vars": { + "target_key": "$bjwb8dxm" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.json b/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.json new file mode 100644 index 000000000000..e6a5703bc924 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.bt.json @@ -0,0 +1,171 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/goliath", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/goliath_tentacles", + "vars": { + "ability_key": "BB_GOLIATH_TENTACLES", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_GOLIATH_HOLE_TARGET", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/goliath_find_diggable_turf", + "vars": { + "target_key": "BB_GOLIATH_HOLE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_GOLIATH_HOLE_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/goliath_dig", + "vars": { + "target_key": "BB_GOLIATH_HOLE_TARGET" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": true + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm index f382721ef909..e851cfdeabe8 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm @@ -102,24 +102,30 @@ if (tentacles.cooldown_time > 1 SECONDS) tentacles.cooldown_time -= 1 SECONDS -/mob/living/basic/mining/goliath/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) - if (!istype(attacking_item, /obj/item/goliath_saddle)) +/mob/living/basic/mining/goliath/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (!istype(tool, /obj/item/goliath_saddle)) return ..() + if (!tameable) balloon_alert(user, "doesn't fit!") - return + return ITEM_INTERACT_BLOCKING + if (saddled) balloon_alert(user, "already saddled!") - return + return ITEM_INTERACT_BLOCKING + if (!HAS_TRAIT(src, TRAIT_TAMED)) balloon_alert(user, "too rowdy!") - return + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "affixing saddle...") if (!do_after(user, delay = 5.5 SECONDS, target = src)) - return + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "ready to ride") - qdel(attacking_item) + qdel(tool) make_rideable() + return ITEM_INTERACT_SUCCESS /mob/living/basic/mining/goliath/proc/make_rideable() saddled = TRUE diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm index 60dfd7a15d07..639d60fe530f 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm @@ -9,117 +9,52 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/goliath, - /datum/ai_planning_subtree/goliath_find_diggable_turf, - /datum/ai_planning_subtree/goliath_dig, - ) - -/datum/ai_planning_subtree/basic_melee_attack_subtree/goliath - operational_datums = list(/datum/component/ai_target_timer) - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/goliath + behavior_tree_json = "code/modules/mob/living/basic/lavaland/goliath/goliath.bt.json" -/// Go for the tentacles if they're available -/datum/ai_behavior/basic_melee_attack/goliath +/// Use tentacles on the current target only after tracking them for MIN_TIME_TO_TENTACLE, and only if not already leg-grappled +/datum/bt_node/ai_behavior/targeted_mob_ability/goliath_tentacles + var/min_target_time = MIN_TIME_TO_TENTACLE -/datum/ai_behavior/basic_melee_attack/goliath/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key, health_ratio_key) - var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0 - if (time_on_target < MIN_TIME_TO_TENTACLE) - return ..() +/datum/bt_node/ai_behavior/targeted_mob_ability/goliath_tentacles/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/target = controller.blackboard[target_key] - // Interrupt attack chain to use tentacles, unless the target is already tentacled - if (ismecha(target) || (isliving(target) && !target.get_item_by_slot(ITEM_SLOT_LEGCUFFED))) - var/datum/action/cooldown/using_action = controller.blackboard[BB_GOLIATH_TENTACLES] - if (using_action?.IsAvailable()) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - return ..() - -/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles - ability_key = BB_GOLIATH_TENTACLES - operational_datums = list(/datum/component/ai_target_timer) - -/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[target_key] - if (!(isliving(target) || ismecha(target)) || (isliving(target) && target.get_item_by_slot(ITEM_SLOT_LEGCUFFED))) - return // Target can be an item or already grabbed, we don't want to tentacle those + if(!(isliving(target) || ismecha(target))) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(isliving(target) && target.get_item_by_slot(ITEM_SLOT_LEGCUFFED)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0 - if (time_on_target < MIN_TIME_TO_TENTACLE) - return // We need to spend some time acquiring our target first + if(time_on_target < min_target_time) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED return ..() -/// If we got nothing better to do, find a turf we can search for tasty roots and such -/datum/ai_planning_subtree/goliath_find_diggable_turf - -/datum/ai_planning_subtree/goliath_find_diggable_turf/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - controller.queue_behavior(/datum/ai_behavior/goliath_find_diggable_turf) - -/datum/ai_behavior/goliath_find_diggable_turf - action_cooldown = 2 SECONDS - /// Where do we store the target data +/// Randomly picks a nearby undig asteroid turf to dig and stores it in target_key +/datum/bt_node/ai_behavior/goliath_find_diggable_turf + time_between_perform = 2 SECONDS var/target_key = BB_GOLIATH_HOLE_TARGET - /// How far do we look for turfs? var/scan_range = 3 -/datum/ai_behavior/goliath_find_diggable_turf/perform(seconds_per_tick, datum/ai_controller/controller) - var/turf/target_turf = controller.blackboard[target_key] - if (is_valid_turf(target_turf)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - +/datum/bt_node/ai_behavior/goliath_find_diggable_turf/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/pawn = controller.pawn var/list/nearby_turfs = RANGE_TURFS(scan_range, pawn) - var/turf/check_turf = pick(nearby_turfs) // This isn't an efficient search algorithm but we don't need it to be - if (!is_valid_turf(check_turf)) - // Otherwise they won't perform idle wanderin + var/turf/open/misc/asteroid/check_turf = pick(nearby_turfs) + if(!istype(check_turf) || check_turf.dug) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED controller.set_blackboard_key(target_key, check_turf) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/// Return true if this is a turf we can dig -/datum/ai_behavior/goliath_find_diggable_turf/proc/is_valid_turf(turf/check_turf) - if (!isasteroidturf(check_turf)) - return FALSE - var/turf/open/misc/asteroid/floor = check_turf - return !floor.dug - -/datum/ai_planning_subtree/goliath_dig - /// Where did we store the target data +/// Melee-attacks the stored hole target turf and clears the key when done +/datum/bt_node/ai_behavior/goliath_dig + time_between_perform = 3 MINUTES var/target_key = BB_GOLIATH_HOLE_TARGET -/datum/ai_planning_subtree/goliath_dig/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!controller.blackboard_key_exists(target_key)) - return - controller.queue_behavior(/datum/ai_behavior/goliath_dig, target_key) - return SUBTREE_RETURN_FINISH_PLANNING - -/// If we got nothing better to do, dig a little hole -/datum/ai_behavior/goliath_dig - action_cooldown = 3 MINUTES - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/goliath_dig/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target_turf = controller.blackboard[target_key] - if (QDELETED(target_turf)) - return - set_movement_target(controller, target_turf) - -/datum/ai_behavior/goliath_dig/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/goliath_dig/perform(seconds_per_tick, datum/ai_controller/controller) var/turf/target_turf = controller.blackboard[target_key] var/mob/living/basic/basic_mob = controller.pawn - if(!target_turf.IsReachableBy(basic_mob)) - return AI_BEHAVIOR_DELAY + if(isnull(target_turf) || !target_turf.IsReachableBy(basic_mob)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED basic_mob.melee_attack(target_turf) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/goliath_dig/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/goliath_dig/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.json b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.json new file mode 100644 index 000000000000..6351fb95695d --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.json @@ -0,0 +1,81 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/gutlunch/gutlunch_baby", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_FOUND_MOM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_MOM", + "required_dist": 1, + "finish_on_arrival": true + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_parent", + "vars": { + "mom_types_key": "BB_FIND_MOM_TYPES", + "found_mom_key": "BB_FOUND_MOM" + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.json b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.json new file mode 100644 index 000000000000..8b412de5dcc2 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.json @@ -0,0 +1,96 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/gutlunch/gutlunch_milk", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_TROUGH_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TROUGH_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target/food_trough", + "vars": { + "target_key": "BB_TROUGH_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": false + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_CHECK_HUNGRY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TROUGH_TARGET", + "target_source": "/datum/target_source/oview_single_type/gutlunch_trough", + "targeting_strategy": "/datum/targeting_strategy/trough_with_ore", + "vision_range": 9 + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.json b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.json new file mode 100644 index 000000000000..5b32a3d8dc73 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.json @@ -0,0 +1,53 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/gutlunch/gutlunch_warrior", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "5 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat", + "bindings": { + "b2jvnm5d": "TRUE" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_ashwalkers" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm index d330ff822035..d376e7336195 100644 --- a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm +++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm @@ -162,7 +162,7 @@ ) /mob/living/basic/mining/gutlunch/grub/proc/ready_to_grow() - return (stat == CONSCIOUS) + return (!IS_UNCONSCIOUS_OR_CRIT(src)) /mob/living/basic/mining/gutlunch/grub/proc/determine_growth_path() var/final_type = prob(50) ? /mob/living/basic/mining/gutlunch/warrior : /mob/living/basic/mining/gutlunch/milk diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm index 800f4c351aa5..0453162f4cca 100644 --- a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm +++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm @@ -1,7 +1,7 @@ #define MAXIMUM_GUTLUNCH_POP 20 /datum/ai_controller/basic_controller/gutlunch ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk + behavior_tree_json = ABSTRACT_AI_CLASS /datum/ai_controller/basic_controller/gutlunch/gutlunch_warrior blackboard = list( @@ -10,99 +10,22 @@ BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/mining/gutlunch/milk), BB_BABIES_CHILD_TYPES = list(/mob/living/basic/mining/gutlunch/grub), BB_MAX_CHILDREN = 5, + BB_FUCKS = TRUE, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/befriend_ashwalkers, - /datum/ai_planning_subtree/make_babies/gutlunch, - ) - -/datum/ai_planning_subtree/make_babies/gutlunch/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(GLOB.gutlunch_count >= MAXIMUM_GUTLUNCH_POP) - return - return ..() - -///find ashwalkers and add them to the list of masters -/datum/ai_planning_subtree/befriend_ashwalkers - -/datum/ai_planning_subtree/befriend_ashwalkers/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - controller.queue_behavior(/datum/ai_behavior/befriend_ashwalkers) - -/datum/ai_behavior/befriend_ashwalkers - action_cooldown = 5 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/befriend_ashwalkers/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/living_pawn = controller.pawn - - for(var/mob/living/potential_friend in oview(9, living_pawn)) - if(!isashwalker(potential_friend)) - continue - if(living_pawn.has_ally(REF(potential_friend))) - continue - living_pawn.befriend(potential_friend) - to_chat(potential_friend, span_nicegreen("[living_pawn] looks at you with endearing eyes!")) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - + behavior_tree_json = "code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_warrior.bt.json" /datum/ai_controller/basic_controller/gutlunch/gutlunch_baby blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FIND_MOM_TYPES = list(/mob/living/basic/mining/gutlunch/milk), ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/look_for_adult, - ) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_baby.bt.json" /datum/ai_controller/basic_controller/gutlunch/gutlunch_milk blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_and_hunt_target/food_trough - ) - -///consume food! -/datum/ai_planning_subtree/find_and_hunt_target/food_trough - target_key = BB_TROUGH_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/food_trough - finding_behavior = /datum/ai_behavior/find_hunt_target/food_trough - hunt_targets = list(/obj/structure/ore_container/food_trough/gutlunch_trough) - hunt_chance = 75 - hunt_range = 9 - - -/datum/ai_planning_subtree/find_and_hunt_target/food_trough/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_CHECK_HUNGRY]) - return - return ..() - -/datum/ai_behavior/find_hunt_target/food_trough - -/datum/ai_behavior/find_hunt_target/food_trough/valid_dinner(mob/living/basic/source, obj/target, radius) - if(isnull(target)) - return FALSE - - if(isnull(locate(/obj/item/stack/ore) in target)) - return FALSE - - return can_see(source, target, radius) - -/datum/ai_behavior/hunt_target/interact_with_target/food_trough - always_reset_target = TRUE - behavior_combat_mode = FALSE + behavior_tree_json = "code/modules/mob/living/basic/lavaland/gutlunchers/gutlunch_milk.bt.json" /datum/pet_command/mine_walls command_name = "Mine" @@ -121,10 +44,7 @@ return ..() /datum/pet_command/mine_walls/execute_action(datum/ai_controller/controller) - if(controller.blackboard_key_exists(BB_CURRENT_PET_TARGET)) - controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_mineral_wall, BB_CURRENT_PET_TARGET) + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/mine_walls) /datum/pet_command/mine_walls/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to start mining!" @@ -138,4 +58,54 @@ return FALSE return ..() +/// Interacts with the food trough to eat ore, then clears the hungry flag. +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/food_trough + always_reset_target = TRUE + behavior_combat_mode = FALSE + +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/food_trough/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + if(succeeded) + controller.clear_blackboard_key(BB_CHECK_HUNGRY) + +///Find nearby ashwalkers (which we share the FACTION_ASHWALKERS faction with), we love lizards. +/datum/bt_node/ai_behavior/befriend_ashwalkers + time_between_perform = 5 SECONDS + +/datum/bt_node/ai_behavior/befriend_ashwalkers/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + for(var/mob/living/potential_friend in oview(9, living_pawn)) + /// The client check is necessary so we don't befriend any npc mining mob that isn't hostile to ashwalkers (until attacked) + if(!living_pawn.client || !living_pawn.faction_check_atom(potential_friend) || living_pawn.has_ally(REF(potential_friend))) + continue + living_pawn.befriend(potential_friend) + to_chat(potential_friend, span_nicegreen("[living_pawn] looks at you with endearing eyes!")) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + +/// Searches for and moves to a parent mob (of types in BB_FIND_MOM_TYPES), sets BB_FOUND_MOM. +/datum/bt_node/ai_behavior/find_parent + var/mom_types_key + var/found_mom_key + +/datum/bt_node/ai_behavior/find_parent/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living_pawn = controller.pawn + var/list/mom_types = controller.blackboard[mom_types_key] + if(!length(mom_types)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + for(var/mob/mother in oview(7, living_pawn)) + if(!is_type_in_list(mother, mom_types)) + continue + controller.set_blackboard_key(found_mom_key, mother) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +// + +/// Mine walls pet command subtree: find mineral wall -> move to it -> mine it -> clear command. +/datum/bt_node/subtree/pet_command/mine_walls + behavior_tree_json = "code/datums/ai/basic_mobs/pet_commands/pet_command_mine_walls.bt.json" + + #undef MAXIMUM_GUTLUNCH_POP diff --git a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.json b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.json new file mode 100644 index 000000000000..6a99b899d664 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/hivelord", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ability_ranged_combat" +} diff --git a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm index 08599831a51c..8a8d234ebec5 100644 --- a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm +++ b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm @@ -60,7 +60,7 @@ /mob/living/basic/mining/hivelord/proc/complete_spawn(turf/spawn_turf) var/mob/living/brood = new death_spawn_type(spawn_turf) SET_FACTION_AND_ALLIES_FROM(brood, src) - brood.ai_controller?.set_blackboard_key(ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + brood.ai_controller?.set_blackboard_key(ai_controller.blackboard[BB_CURRENT_TARGET]) brood.dir = get_dir(src, spawn_turf) /mob/living/basic/mining/hivelord/RangedAttack(atom/atom_target, modifiers) diff --git a/code/modules/mob/living/basic/lavaland/hivelord/hivelord_ai.dm b/code/modules/mob/living/basic/lavaland/hivelord/hivelord_ai.dm index aecabc823737..afb54229538e 100644 --- a/code/modules/mob/living/basic/lavaland/hivelord/hivelord_ai.dm +++ b/code/modules/mob/living/basic/lavaland/hivelord/hivelord_ai.dm @@ -1,15 +1,9 @@ /// Basically just keep away and shit out worms /datum/ai_controller/basic_controller/hivelord + behavior_tree_json = "code/modules/mob/living/basic/lavaland/hivelord/hivelord.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_AGGRO_RANGE = 5, // Only get mad at people nearby ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/targeted_mob_ability, - ) diff --git a/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm b/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm index 146d2fba82f3..080a40b0e9cb 100644 --- a/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm +++ b/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm @@ -11,7 +11,7 @@ melee_cooldown_time = 0 shared_cooldown = NONE /// If a mob is not clicked directly, inherit targeting data from this blackboard key and setting it upon this target key - var/ai_target_key = BB_BASIC_MOB_CURRENT_TARGET + var/ai_target_key = BB_CURRENT_TARGET /// What are we actually spawning? var/spawn_type = /mob/living/basic/hivelord_brood /// Do we automatically fire with no cooldown when damaged? diff --git a/code/modules/mob/living/basic/lavaland/legion/legion.bt.json b/code/modules/mob/living/basic/lavaland/legion/legion.bt.json new file mode 100644 index 000000000000..451fa812723c --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/legion/legion.bt.json @@ -0,0 +1,102 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/legion", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/legion" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm index 8d5082d4c088..438a1977a521 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm @@ -1,5 +1,6 @@ /// Keep away and launch skulls at every opportunity, prioritising injured allies /datum/ai_controller/basic_controller/legion + behavior_tree_json = "code/modules/mob/living/basic/lavaland/legion/legion.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/legion, BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, @@ -8,35 +9,21 @@ BB_RANGED_SKIRMISH_MIN_DISTANCE = 4, BB_RANGED_SKIRMISH_MAX_DISTANCE = 6, ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/random_speech/legion, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/targeted_mob_ability, - ) /// Chase and attack whatever we are targeting, if it's friendly we will heal them /datum/ai_controller/basic_controller/legion_brood + behavior_tree_json = "code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/legion, BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining/low_node_priority, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Target nearby friendlies if they are hurt (and are not themselves Legions) /datum/targeting_strategy/basic/legion + custom_faction_check = TRUE /datum/targeting_strategy/basic/legion/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) if (!living_mob.faction_check_atom(the_target, exact_match = check_factions_exactly)) @@ -48,39 +35,3 @@ return TRUE return the_target.stat == DEAD || the_target.health >= the_target.maxHealth -/// Don't run away from friendlies -/datum/ai_planning_subtree/flee_target/legion - -/datum/ai_planning_subtree/flee_target/legion/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[target_key] - if (QDELETED(target) || target.faction_check_atom(controller.pawn)) - return // Only flee if we have a hostile target - return ..() - -/// Make spooky sounds, if we have a corpse inside then impersonate them -/datum/ai_planning_subtree/random_speech/legion - speech_chance = 1 - speak = list("Come...", "Legion...", "Why...?") - emote_hear = list("groans.", "wails.", "whimpers.") - emote_see = list("twitches.", "shudders.") - /// Stuff to specifically say into a radio - var/list/radio_speech = list("Come...", "Why...?") - -/datum/ai_planning_subtree/random_speech/legion/speak(datum/ai_controller/controller) - var/mob/living/carbon/human/victim = controller.blackboard[BB_LEGION_CORPSE] - if (QDELETED(victim) || prob(30)) - return ..() - - if(HAS_MIND_TRAIT(victim, TRAIT_MIMING)) // mimes cant talk - return - - var/list/remembered_speech = controller.blackboard[BB_LEGION_RECENT_LINES] || list() - - if (length(remembered_speech) && prob(50)) // Don't spam the radio - controller.queue_behavior(/datum/ai_behavior/perform_speech, pick(remembered_speech)) - return - - var/obj/item/radio/mob_radio = locate() in victim.contents - if (QDELETED(mob_radio)) - return ..() // No radio, just talk funny - controller.queue_behavior(/datum/ai_behavior/perform_speech_radio, pick(radio_speech + remembered_speech), mob_radio, list(RADIO_CHANNEL_SUPPLY, RADIO_CHANNEL_COMMON)) diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.json b/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.json new file mode 100644 index 000000000000..c75a7dcbbe11 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/legion/legion_brood.bt.json @@ -0,0 +1,81 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/legion_brood", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm index dd2bbe18de2b..3c3147e57df9 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm @@ -63,7 +63,7 @@ return /mob/living/basic/mining/legion_brood/melee_attack(mob/living/target, list/modifiers, ignore_cooldown) - if (ishuman(target) && target.stat > SOFT_CRIT) + if (ishuman(target) && IS_UNCONSCIOUS(target)) infest(target) return @@ -112,7 +112,7 @@ return // Inherit our creator's target and reinforcement requests - ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, creator.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + ai_controller.set_blackboard_key(BB_CURRENT_TARGET, creator.ai_controller.blackboard[BB_CURRENT_TARGET]) ai_controller.set_blackboard_key(BB_MINING_MOB_REINFORCEMENTS_REQUESTS, creator.ai_controller.blackboard[BB_MINING_MOB_REINFORCEMENTS_REQUESTS]) /// Reference handling diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.json b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.json new file mode 100644 index 000000000000..6e8e16f2a27a --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.json @@ -0,0 +1,119 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/legion_monkey", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/consider_venting" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": "10" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_ENTRY_VENT_TARGET", + "target_source": "/datum/target_source/oview_single_type/vent_pump", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 7 + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/legion" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm index 1fc569e51f8f..44b5b2009286 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm @@ -42,6 +42,7 @@ /// Opportunistically hops in and out of vents, if it can find one and is not biting someone. /datum/ai_controller/basic_controller/legion_monkey + behavior_tree_json = "code/modules/mob/living/basic/lavaland/legion/legion_monkey.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, @@ -51,14 +52,3 @@ ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - - // We understand that vents are nice little hidey holes through epigenetic inheritance, so we'll use them. - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/legion, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/opportunistic_ventcrawler, - ) diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm index 0328f83c1840..2a81a8f1a620 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm @@ -67,7 +67,7 @@ if(!user.Adjacent(target) || !isliving(target)) return FALSE - if (target.stat <= SOFT_CRIT && !(organ_flags & ORGAN_FAILING)) + if (!IS_UNCONSCIOUS(target) && !(organ_flags & ORGAN_FAILING)) target.add_mood_event("legion_core", /datum/mood_event/healsbadman) target.apply_status_effect(applied_status) diff --git a/code/modules/mob/living/basic/lavaland/legion/spawn_legions.dm b/code/modules/mob/living/basic/lavaland/legion/spawn_legions.dm index 2f464fb08432..889d0347be40 100644 --- a/code/modules/mob/living/basic/lavaland/legion/spawn_legions.dm +++ b/code/modules/mob/living/basic/lavaland/legion/spawn_legions.dm @@ -11,7 +11,7 @@ melee_cooldown_time = 0 shared_cooldown = NONE /// If a mob is not clicked directly, inherit targeting data from this blackboard key and setting it upon this target key - var/ai_target_key = BB_BASIC_MOB_CURRENT_TARGET + var/ai_target_key = BB_CURRENT_TARGET /// What are we actually spawning? var/spawn_type = /mob/living/basic/mining/legion_brood /// How far can we fire? diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.json b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.json new file mode 100644 index 000000000000..bc32d519c2b7 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.json @@ -0,0 +1,131 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/lobstrosity", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TARGETED_ACTION", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm index ecf69e02a9a9..fe66b6cb247a 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm @@ -270,16 +270,14 @@ command_feedback = "growl" pointed_reaction = "and growls" pet_ability_key = BB_TARGETED_ACTION - ability_behavior = /datum/ai_behavior/pet_use_ability/then_attack/long_ranged /datum/pet_command/use_ability/lob_charge/set_command_target(mob/living/parent, atom/target) if (!target) return FALSE var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(parent.ai_controller.blackboard[targeting_strategy_key]) - if(!targeter?.can_attack(parent, target)) + if(!targeter?.is_valid_target(parent, target)) parent.balloon_alert_to_viewers("shakes head!") return FALSE return ..() /datum/pet_command/use_ability/lob_charge/shrimp - ability_behavior = /datum/ai_behavior/pet_use_ability/then_attack/short_ranged diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm index bd9b2addff62..b922843b0ee2 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm @@ -19,25 +19,15 @@ BB_ONLY_FISH_WHILE_HUNGRY = TRUE, BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN, BB_OWNER_SELF_HARM_RESPONSES = SHRIMP_HARM_RESPONSES, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/hoard_fingers, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/targeted_mob_ability/lobster, - /datum/ai_planning_subtree/flee_target/lobster, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/fish/fish_from_turfs, - /datum/ai_planning_subtree/find_fingers, - ) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.bt.json" /datum/ai_controller/basic_controller/lobstrosity/TryPossessPawn(atom/new_pawn) . = ..() @@ -58,282 +48,19 @@ BB_BASIC_MOB_FLEE_DISTANCE = 4, BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN, BB_OWNER_SELF_HARM_RESPONSES = SHRIMP_HARM_RESPONSES, - ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/hoard_fingers, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/targeted_mob_ability/lobster, - /datum/ai_planning_subtree/flee_target/lobster, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/fish/fish_from_turfs, - /datum/ai_planning_subtree/find_fingers, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ///A subtype of juvenile lobster AI that has the target_retaliate behaviour instead of simple_find_target /datum/ai_controller/basic_controller/lobstrosity/juvenile/calm - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/hoard_fingers, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/targeted_mob_ability/lobster/juvenile, - /datum/ai_planning_subtree/flee_target/lobster, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/fish/fish_from_turfs, - /datum/ai_planning_subtree/find_fingers, - ) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.json" ///A subtype of juvenile lobster AI that has the capricious_retaliate behaviour instead of simple_find_target /datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/hoard_fingers, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/capricious_retaliate, - /datum/ai_planning_subtree/targeted_mob_ability/lobster/juvenile, - /datum/ai_planning_subtree/flee_target/lobster, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/fish/fish_from_turfs, - /datum/ai_planning_subtree/find_fingers, - ) - -/datum/ai_planning_subtree/basic_melee_attack_subtree/lobster - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/lobster - -/datum/ai_planning_subtree/basic_melee_attack_subtree/lobster/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/movable/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(!isliving(target)) - return ..() - if (!controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return - if (!isnull(controller.blackboard[BB_LOBSTROSITY_TARGET_LIMB])) - return - if(controller.blackboard[BB_LOBSTROSITY_NAIVE_HUNTER] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - return //juvenile lobstrosities fear me. - var/mob/living/living_pawn = controller.pawn - if (DOING_INTERACTION_WITH_TARGET(living_pawn, target)) - return - return ..() - -/datum/ai_behavior/basic_melee_attack/lobster - -/datum/ai_behavior/basic_melee_attack/lobster/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/mob/living/target = controller.blackboard[target_key] - if (isnull(target) || !istype(target)) - return ..() - var/is_vulnerable = FALSE - if(controller.blackboard[BB_LOBSTROSITY_NAIVE_HUNTER]) - if(HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - //Trigger lobstrosity PTSD. Don't clear the target so we can run away. - controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, FALSE) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - if(target.body_position == LYING_DOWN) - is_vulnerable = TRUE - if(!is_vulnerable) - for (var/trait in controller.blackboard[BB_LOBSTROSITY_EXPLOIT_TRAITS]) - if (!HAS_TRAIT(target, trait)) - continue - is_vulnerable = TRUE - break - if (!is_vulnerable) - controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, FALSE) - if (!controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - // We don't want to clear our target - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - return ..() - -/datum/ai_planning_subtree/flee_target/lobster - flee_behaviour = /datum/ai_behavior/run_away_from_target/lobster - -/datum/ai_planning_subtree/flee_target/lobster/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/movable/target = controller.blackboard[target_key] - if(!QDELETED(target) && controller.blackboard[BB_LOBSTROSITY_NAIVE_HUNTER] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - return ..() - var/datum/action/cooldown/using_action = controller.blackboard[BB_TARGETED_ACTION] - if (using_action?.IsAvailable()) - return FALSE - return ..() - -/datum/ai_behavior/run_away_from_target/lobster - clear_failed_targets = FALSE - -/datum/ai_behavior/run_away_from_target/lobster/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return ..() - - var/is_naive = controller.blackboard[BB_LOBSTROSITY_NAIVE_HUNTER] - var/is_scary = HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN) - - if(!is_naive || !is_scary) //the lobstrosity isn't naive/young and the target isn't a scary fisherman. - if(isliving(target)) - var/mob/living/living_target = target - if(is_naive && living_target.body_position == LYING_DOWN) - controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, TRUE) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - for (var/trait in controller.blackboard[BB_LOBSTROSITY_EXPLOIT_TRAITS]) - if (!HAS_TRAIT(target, trait)) - continue - controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, TRUE) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/mob/living/us = controller.pawn - if (us.pulling == target) - us.stop_pulling() // If we're running away from someone, best not to bring them with us - - return ..() - -/// Don't use charge ability on an adjacent target, and make sure you're visible before you start -/datum/ai_planning_subtree/targeted_mob_ability/lobster - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/min_range - -/datum/ai_planning_subtree/targeted_mob_ability/lobster/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target) || in_range(controller.pawn, target)) - return - if(controller.blackboard[BB_LOBSTROSITY_NAIVE_HUNTER] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - return - return ..() - - -/datum/ai_planning_subtree/targeted_mob_ability/lobster/juvenile - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/min_range/short - -/// Look for loose arms lying around -/datum/ai_planning_subtree/find_fingers - /// Where do we store target limb data? - var/target_key = BB_LOBSTROSITY_TARGET_LIMB - /// What are we actually looking for? - var/desired_type = /obj/item/bodypart/arm - -/datum/ai_planning_subtree/find_fingers/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - controller.queue_behavior(/datum/ai_behavior/find_and_set, target_key, desired_type) - -/// If you see an arm, grab it and run -/datum/ai_planning_subtree/hoard_fingers - /// Where do we store target limb data? - var/target_key = BB_LOBSTROSITY_TARGET_LIMB - -/datum/ai_planning_subtree/hoard_fingers/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/current_target = controller.blackboard[target_key] - if (QDELETED(current_target)) - return - - var/mob/living/living_pawn = controller.pawn - if (living_pawn.pulling != current_target) - controller.queue_behavior(/datum/ai_behavior/grab_fingers, target_key) - else - controller.queue_behavior(/datum/ai_behavior/hoard_fingers, target_key) - return SUBTREE_RETURN_FINISH_PLANNING - -/// If our target is an arm then move over and drag it -/datum/ai_behavior/grab_fingers - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/grab_fingers/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/current_target = controller.blackboard[target_key] - if (QDELETED(current_target)) - return FALSE - set_movement_target(controller, current_target) - -/datum/ai_behavior/grab_fingers/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/current_target = controller.blackboard[target_key] - if (QDELETED(current_target)) - return AI_BEHAVIOR_DELAY - var/mob/living/living_pawn = controller.pawn - living_pawn.start_pulling(current_target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/// How far we'll try to go before eating an arm -#define FLEE_TO_RANGE 9 -/// How many times we'll attempt to move before giving up -#define MAX_LOBSTROSITY_PATIENCE 15 - -/// If we are dragging an arm then run away until we are out of range and feast -/datum/ai_behavior/hoard_fingers - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - /// We store a counter at this key we increment on every movement until we are overwhelmed with hunger - var/patience_key = BB_LOBSTROSITY_FINGER_LUST - -/datum/ai_behavior/hoard_fingers/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (QDELETED(current_target)) - set_movement_target(controller, get_turf(controller.pawn)) - return - var/perform_flags = target_step_away(controller, current_target, target_key) - if (perform_flags & AI_BEHAVIOR_SUCCEEDED) - finish_action(controller, TRUE, target_key) - else if(perform_flags & AI_BEHAVIOR_FAILED) - finish_action(controller, FALSE, target_key) - -/// Find the next step to take away from the current target -/datum/ai_behavior/hoard_fingers/proc/target_step_away(datum/ai_controller/controller, atom/current_target, target_key) - var/turf/next_step = get_step_away(controller.pawn, current_target) - if (!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) - set_movement_target(controller, next_step) - return NONE - var/list/all_dirs = GLOB.alldirs.Copy() - all_dirs -= get_dir(controller.pawn, next_step) - all_dirs -= get_dir(controller.pawn, current_target) - shuffle_inplace(all_dirs) - for (var/dir in all_dirs) - next_step = get_step(controller.pawn, dir) - if (!isnull(next_step) && !next_step.is_blocked_turf(exclude_mobs = TRUE)) - set_movement_target(controller, next_step) - return NONE - return AI_BEHAVIOR_FAILED - -/datum/ai_behavior/hoard_fingers/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/current_patience = controller.blackboard[patience_key] + 1 - if (current_patience >= MAX_LOBSTROSITY_PATIENCE) - if(eat_fingers(controller, target_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(patience_key, current_patience) - var/mob/living/living_pawn = controller.pawn - if (isnull(living_pawn.pulling)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/atom/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (QDELETED(current_target) || !can_see(controller.pawn, current_target, FLEE_TO_RANGE)) - if(eat_fingers(controller, target_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - if(target_step_away(controller, current_target, target_key)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/// Finally consume those delicious digits -/datum/ai_behavior/hoard_fingers/proc/eat_fingers(datum/ai_controller/controller, target_key) - var/mob/living/basic/living_pawn = controller.pawn - var/atom/fingers = controller.blackboard[target_key] - if (QDELETED(fingers) || living_pawn.pulling != fingers) - return AI_BEHAVIOR_FAILED - controller.ai_interact(target = fingers) - return AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/hoard_fingers/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.set_blackboard_key(patience_key, 0) - controller.clear_blackboard_key(target_key) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + behavior_tree_json = "code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.json" -#undef FLEE_TO_RANGE -#undef MAX_LOBSTROSITY_PATIENCE #undef SHRIMP_HARM_RESPONSES diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.json b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.json new file mode 100644 index 000000000000..584477480386 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_calm.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/lobstrosity/juvenile/calm", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.json b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.json new file mode 100644 index 000000000000..7f5bcc6f817e --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_capricious.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_capricious_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mining.dm b/code/modules/mob/living/basic/lavaland/mining.dm index 2f0baa25c2b5..7a40095b7462 100644 --- a/code/modules/mob/living/basic/lavaland/mining.dm +++ b/code/modules/mob/living/basic/lavaland/mining.dm @@ -59,6 +59,6 @@ /mob/living/basic/mining/proc/on_attacked(datum/source, atom/attacker, attack_flags) SIGNAL_HANDLER - if(!isashwalker(attacker) || !has_faction(FACTION_ASHWALKER)) + if(!has_faction(FACTION_ASHWALKER) || !astype(attacker, /mob/living)?.has_faction(FACTION_ASHWALKER)) return remove_faction(FACTION_ASHWALKER) diff --git a/code/modules/mob/living/basic/lavaland/mook/bard.bt.json b/code/modules/mob/living/basic/lavaland/mook/bard.bt.json new file mode 100644 index 000000000000..9ea04b0d887e --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/bard.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mook/bard", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_mook_behavior", + "bindings": { + "b1kiiayf": "/datum/bt_node/subtree/bard_play_music", + "b0ymbjo1": "/datum/bt_node/subtree/bard_find_targets" + } +} diff --git a/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.json b/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.json new file mode 100644 index 000000000000..0d1b66909bec --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.json @@ -0,0 +1,56 @@ +{ + "dm_type": "/datum/bt_node/subtree/bard_find_targets", + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_MOOK_TRIBAL_CHIEF" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "target_source": "/datum/target_source/oview_single_type/tribal_chief", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "long_range_friendship": true, + "forget_target": false + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOOK_MUSIC_AUDIENCE", + "target_source": "/datum/target_source/near_village_humans", + "targeting_strategy": "/datum/targeting_strategy/capable_human", + "time_between_perform": "1 SECONDS" + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.json b/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.json new file mode 100644 index 000000000000..b29f041e5e82 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.json @@ -0,0 +1,55 @@ +{ + "dm_type": "/datum/bt_node/subtree/bard_play_music", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_play_instrument", + "bindings": { + "bulu13xf": "75" + } + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_MOOK_MUSIC_AUDIENCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_MOOK_MUSIC_AUDIENCE", + "required_dist": 2, + "finish_on_arrival": false + } + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target", + "vars": { + "target_key": "BB_HOME_VILLAGE", + "walk_chance": 75 + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.json b/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.json new file mode 100644 index 000000000000..07346360c179 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.json @@ -0,0 +1,68 @@ +{ + "dm_type": "/datum/bt_node/subtree/chief_find_targets", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_deposit_position" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore", + "vars": { + "time_between_perform": "1 SECONDS", + "range": 1 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_MATERIAL_STAND_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MATERIAL_STAND_TARGET", + "target_source": "/datum/target_source/oview_single_type/ore_stand", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.json b/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.json new file mode 100644 index 000000000000..2f786c350ea0 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.json @@ -0,0 +1,53 @@ +{ + "dm_type": "/datum/bt_node/subtree/chief_issue_commands", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_COMMAND_COOLDOWN", + "cooldown_duration": "10 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/issue_pet_command", + "vars": { + "command_list_key": "BB_MOOK_COMMANDS", + "command_type": "/datum/pet_command/attack", + "target_key": "BB_CURRENT_TARGET", + "commandable_mob_type": "/mob/living/basic/mining/mook", + "command_distance": 7 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_ORE_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/issue_pet_command", + "vars": { + "command_list_key": "BB_MOOK_COMMANDS", + "command_type": "/datum/pet_command/fetch", + "target_key": "BB_ORE_TARGET", + "commandable_mob_type": "/mob/living/basic/mining/mook", + "command_distance": 7 + } + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.json b/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.json new file mode 100644 index 000000000000..f77afce86f4e --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.json @@ -0,0 +1,150 @@ +{ + "dm_type": "/datum/bt_node/subtree/chief_manage_village", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_DEPOSIT_POSITION" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEPOSIT_POSITION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_DEPOSIT_POSITION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_MATERIAL_STAND_TARGET" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_MOOK_BONFIRE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_MOOK_BONFIRE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_MOOK_BONFIRE_TARGET", + "always_reset_target": true, + "behavior_combat_mode": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_ORE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ORE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up_item_virtual", + "vars": { + "target_key": "BB_ORE_TARGET", + "storage_key": "BB_SIMPLE_CARRY_ITEM" + } + } + ] + } + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target", + "vars": { + "target_key": "BB_HOME_VILLAGE", + "walk_chance": 75 + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOOK_BONFIRE_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/bonfire_targets", + "targeting_strategy": "/datum/targeting_strategy/unlit_bonfire" + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.json b/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.json new file mode 100644 index 000000000000..ceb511fd5b25 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.json @@ -0,0 +1,178 @@ +{ + "dm_type": "/datum/bt_node/subtree/generic_mook_behavior", + "bindings": { + "b1kiiayf": { + "label": "Idle Behavior", + "default": "/datum/bt_node/subtree" + }, + "b0ymbjo1": { + "label": "Targetting Behavior", + "default": "/datum/bt_node/subtree" + }, + "bk7ixti3": { + "label": "High Priority Behavior", + "default": "/datum/bt_node/subtree" + } + }, + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_HOME_VILLAGE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_village" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_same_z_as_key", + "vars": { + "key": "BB_HOME_VILLAGE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_farther_than_from_key", + "vars": { + "anchor_key": "BB_HOME_VILLAGE", + "distance_key": "BB_MAXIMUM_DISTANCE_TO_VILLAGE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/mook_has_flee_reason", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_INJURED_MOOK" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_MOOK_JUMP_ABILITY" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_TARGET_MINERAL_WALL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_MOOK_MUSIC_AUDIENCE" + } + } + ] + } + } + } + }, + { + "type": "subtree", + "subtype": "$bk7ixti3" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_MOOK_LEAP_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "$b1kiiayf" + } + ] + }, + { + "type": "subtree", + "subtype": "$b0ymbjo1" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.json b/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.json new file mode 100644 index 000000000000..df73c3e1a160 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/go_mining.bt.json @@ -0,0 +1,213 @@ +{ + "dm_type": "/datum/bt_node/subtree/go_mining", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_DEPOSIT_POSITION" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEPOSIT_POSITION", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_DEPOSIT_POSITION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_MATERIAL_STAND_TARGET" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": true, + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_ORE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ORE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/pick_up_item_virtual", + "vars": { + "target_key": "BB_ORE_TARGET", + "storage_key": "BB_SIMPLE_CARRY_ITEM" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "invert": false, + "key": "BB_TARGET_MINERAL_WALL" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/mine_wall", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL", + "time_between_perform": "1 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_MOOK_MINING_COOLDOWN", + "cooldown_duration": "10 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore", + "vars": { + "time_between_perform": "0.5 SECONDS" + } + } + ] + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/calculate_wander_destination" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_WANDER_DESTINATION", + "required_dist": 0 + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_deposit_position" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_SIMPLE_CARRY_ITEM" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_ore" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOOK_MINING_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mineral_wall/mook", + "vars": { + "target_key": "BB_TARGET_MINERAL_WALL" + } + } + } + ] + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.json b/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.json new file mode 100644 index 000000000000..dd6d274c6348 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.json @@ -0,0 +1,42 @@ +{ + "dm_type": "/datum/bt_node/subtree/heal_injured", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_INJURED_MOOK" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_INJURED_MOOK" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "always_reset_target": true, + "target_key": "BB_INJURED_MOOK" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/walk_near_target", + "vars": { + "target_key": "BB_HOME_VILLAGE", + "walk_chance": 75 + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/mook.bt.json b/code/modules/mob/living/basic/lavaland/mook/mook.bt.json new file mode 100644 index 000000000000..a129f8b83d5a --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/mook.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mook", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_mook_behavior", + "bindings": { + "b0ymbjo1": "/datum/bt_node/subtree/worker_find_targets", + "b1kiiayf": "/datum/bt_node/subtree/go_mining" + } +} diff --git a/code/modules/mob/living/basic/lavaland/mook/mook.dm b/code/modules/mob/living/basic/lavaland/mook/mook.dm index 966c1bfba185..2ecc54895ef6 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook.dm @@ -44,6 +44,8 @@ /datum/pet_command/attack, /datum/pet_command/fetch, ) + /// Things we want to find to heal + var/static/list/heal_targets = list(/mob/living/basic/mining/mook/worker) /mob/living/basic/mining/mook/Initialize(mapload) . = ..() @@ -64,6 +66,8 @@ grant_healer_abilities() AddComponent(/datum/component/obeys_commands, pet_commands) + ai_controller?.set_blackboard_key(BB_MOOK_HEAL_TARGETS, typecacheof(heal_targets)) + /// Returns a list of actions and blackboard keys to pass into `grant_actions_by_list`. /mob/living/basic/mining/mook/proc/get_innate_abilities() @@ -84,6 +88,7 @@ /mob/living/basic/mining/mook/Entered(atom/movable/mover) if(istype(mover, /obj/item/stack/ore)) held_ore = mover + ai_controller?.set_blackboard_key(BB_SIMPLE_CARRY_ITEM, mover) update_appearance(UPDATE_OVERLAYS) return ..() @@ -187,7 +192,7 @@ if(istype(intruder, /mob/living/basic/mining/mook)) return for(var/mob/living/basic/mining/mook/villager in oview(src, 9)) - villager.ai_controller?.set_blackboard_key_assoc(BB_BASIC_MOB_RETALIATE_LIST, intruder, world.time) + villager.ai_controller?.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, intruder, world.time) /mob/living/basic/mining/mook/worker @@ -276,10 +281,12 @@ var/static/mutable_appearance/chief_active = mutable_appearance('icons/mob/simple/jungle/mook.dmi', "mook_chief_leap") ///overlay in our warmup state var/static/mutable_appearance/chief_warmup = mutable_appearance('icons/mob/simple/jungle/mook.dmi', "mook_chief_warmup") + var/static/list/bonfire_targets = list(/obj/structure/bonfire) /mob/living/basic/mining/mook/worker/tribal_chief/Initialize(mapload) . = ..() update_appearance() + ai_controller?.set_blackboard_key(BB_BONFIRE_TARGETS, typecacheof(bonfire_targets)) /mob/living/basic/mining/mook/worker/tribal_chief/update_overlays() . = ..() diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm index ae9fd3d3de25..f4f51ada91b4 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm @@ -51,10 +51,14 @@ return TRUE /datum/action/cooldown/mob_cooldown/mook_ability/mook_leap/proc/launch_towards_target(atom/target) + if(QDELETED(owner) || QDELETED(target)) + return + var/turf/target_turf = get_turf(target) + if(isnull(target_turf)) + return new /obj/effect/temp_visual/mook_dust(get_turf(owner)) - playsound(get_turf(owner), 'sound/items/weapons/thudswoosh.ogg', 25, TRUE) + playsound(owner, 'sound/items/weapons/thudswoosh.ogg', 25, TRUE) playsound(owner, 'sound/mobs/non-humanoids/mook/mook_leap_yell.ogg', 100, TRUE) - var/turf/target_turf = get_turf(target) if(!target_turf.is_blocked_turf()) owner.throw_at(target = target_turf, range = 7, speed = 1, spin = FALSE, callback = CALLBACK(src, PROC_REF(attack_combo), target)) diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm index 92f6156a7059..d3d20f1802b1 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm @@ -5,6 +5,7 @@ GLOBAL_LIST_INIT(mook_commands, list( )) /datum/ai_controller/basic_controller/mook + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/mook.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mook, BB_BLACKLIST_MINERAL_TURFS = list(/turf/closed/mineral/gibtonite, /turf/closed/mineral/strong), @@ -13,21 +14,11 @@ GLOBAL_LIST_INIT(mook_commands, list( ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/look_for_village, - /datum/ai_planning_subtree/targeted_mob_ability/leap, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/material_stand, - /datum/ai_planning_subtree/use_mob_ability/mook_jump, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/mook, - /datum/ai_planning_subtree/mine_walls/mook, - /datum/ai_planning_subtree/wander_away_from_village, - ) - can_idle = FALSE // these guys are intended to operate even if nobody's around + // these guys are intended to operate even if nobody's around + ai_traits = DEFAULT_AI_FLAGS | RUN_WHILE_UNWATCHED + +/datum/targeting_strategy/basic/mook + custom_faction_check = TRUE ///check for faction if not a ash walker, otherwise just attack /datum/targeting_strategy/basic/mook/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) @@ -36,389 +27,35 @@ GLOBAL_LIST_INIT(mook_commands, list( return ..() -/datum/ai_planning_subtree/targeted_mob_ability/leap - ability_key = BB_MOOK_LEAP_ABILITY - -/datum/ai_planning_subtree/use_mob_ability/mook_jump - ability_key = BB_MOOK_JUMP_ABILITY - -///jump towards the village when we have found ore or there is a storm coming -/datum/ai_planning_subtree/use_mob_ability/mook_jump/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] - var/mob/living/living_pawn = controller.pawn - var/obj/effect/home = controller.blackboard[BB_HOME_VILLAGE] - if(QDELETED(home)) - return - if(get_dist(living_pawn, home) < controller.blackboard[BB_MAXIMUM_DISTANCE_TO_VILLAGE]) - return - if(home.z != living_pawn.z) - return - if(!storm_approaching && !(locate(/obj/item/stack/ore) in living_pawn)) - return - - controller.clear_blackboard_key(BB_TARGET_MINERAL_WALL) - return ..() - -///hunt ores that we will haul off back to the village -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/mook - -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/mook/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(locate(/obj/item/stack/ore) in living_pawn) - return - return ..() - -///deposit ores into the stand! -/datum/ai_planning_subtree/find_and_hunt_target/material_stand - target_key = BB_MATERIAL_STAND_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/material_stand - finding_behavior = /datum/ai_behavior/find_hunt_target - hunt_targets = list(/obj/structure/ore_container/material_stand) - hunt_range = 9 - -/datum/ai_planning_subtree/find_and_hunt_target/material_stand/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(!locate(/obj/item/stack/ore) in living_pawn) - return - return ..() - -/datum/ai_behavior/hunt_target/interact_with_target/material_stand - required_distance = 0 - always_reset_target = TRUE - behavior_combat_mode = FALSE - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - -///try to face the counter when depositing ores -/datum/ai_behavior/hunt_target/interact_with_target/material_stand/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) - . = ..() - var/atom/hunt_target = controller.blackboard[hunting_target_key] - if (QDELETED(hunt_target)) - return FALSE - var/list/possible_turfs = list() - var/list/directions = list(SOUTH, SOUTHEAST) - - for(var/direction in directions) - var/turf/bottom_turf = get_step(hunt_target, direction) - if(!bottom_turf.is_blocked_turf()) - possible_turfs += bottom_turf - - if(!length(possible_turfs)) - return FALSE - set_movement_target(controller, pick(possible_turfs)) - -///look for our village -/datum/ai_planning_subtree/look_for_village - -/datum/ai_planning_subtree/look_for_village/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_HOME_VILLAGE)) - return - - controller.queue_behavior(/datum/ai_behavior/find_village, BB_HOME_VILLAGE) - -/datum/ai_behavior/find_village - -/datum/ai_behavior/find_village/perform(seconds_per_tick, datum/ai_controller/controller, village_key) - - var/obj/effect/landmark/home_marker = locate(/obj/effect/landmark/mook_village) in GLOB.landmarks_list - if(isnull(home_marker)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(village_key, home_marker) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -///explore the lands away from the village to look for ore -/datum/ai_planning_subtree/wander_away_from_village - -/datum/ai_planning_subtree/wander_away_from_village/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] - ///if we have ores to deposit or a storm is approaching, dont wander away - if(storm_approaching || (locate(/obj/item/stack/ore) in living_pawn)) - return - - if(controller.blackboard_key_exists(BB_HOME_VILLAGE)) - controller.queue_behavior(/datum/ai_behavior/wander, BB_HOME_VILLAGE) - -/datum/ai_behavior/wander - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 0 - /// distance we will wander away from the village - var/wander_distance = 9 - -/datum/ai_behavior/wander/setup(datum/ai_controller/controller, village_key) - . = ..() - var/mob/living/living_pawn = controller.pawn - var/obj/effect/target = controller.blackboard[village_key] - if(QDELETED(target)) - return FALSE - - if(target.z != living_pawn.z) - return FALSE - - var/list/angle_directions = list() - for(var/direction in GLOB.alldirs) - angle_directions += dir2angle(direction) - - var/angle_to_home = get_angle(living_pawn, target) - angle_directions -= angle_to_home - angle_directions -= (angle_to_home + 45) - angle_directions -= (angle_to_home - 45) - shuffle_inplace(angle_directions) - - var/turf/wander_destination = get_turf(living_pawn) - for(var/angle in angle_directions) - var/turf/test_turf = get_furthest_turf(living_pawn, angle, target) - if(isnull(test_turf)) - continue - var/distance_from_target = get_dist(target, test_turf) - if(distance_from_target <= get_dist(target, wander_destination)) - continue - wander_destination = test_turf - if(distance_from_target == wander_distance) - break - - set_movement_target(controller, wander_destination) - -/datum/ai_behavior/wander/proc/get_furthest_turf(atom/source, angle, atom/target) - var/turf/return_turf - for(var/i in 1 to wander_distance) - var/turf/test_destination = get_ranged_target_turf_direct(source, target, range = i, offset = angle) - if(test_destination.is_blocked_turf(source_atom = source)) - break - return_turf = test_destination - return return_turf - -/datum/ai_behavior/wander/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_planning_subtree/mine_walls/mook - find_wall_behavior = /datum/ai_behavior/find_mineral_wall/mook - -/datum/ai_planning_subtree/mine_walls/mook/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] - if(storm_approaching || locate(/obj/item/stack/ore) in living_pawn) - return - return ..() - -/datum/ai_behavior/find_mineral_wall/mook - -/datum/ai_behavior/find_mineral_wall/mook/check_if_mineable(datum/ai_controller/controller, turf/target_wall) - var/list/forbidden_turfs = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS] - if(is_type_in_list(target_wall, forbidden_turfs)) - return FALSE - return ..() - ///bard mook plays nice music for the village /datum/ai_controller/basic_controller/mook/bard + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/bard.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mook, BB_MAXIMUM_DISTANCE_TO_VILLAGE = 10, BB_STORM_APPROACHING = FALSE, BB_SONG_LINES = MOOK_SONG, + BB_INSTRUMENT_UP_ASS = TRUE, ) - idle_behavior = /datum/idle_behavior/walk_near_target/mook_village - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/look_for_village, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/play_music_for_visitor, - /datum/ai_planning_subtree/use_mob_ability/mook_jump, - /datum/ai_planning_subtree/generic_play_instrument, - ) - - -///find an audience to follow and play music for! -/datum/ai_planning_subtree/play_music_for_visitor - -/datum/ai_planning_subtree/play_music_for_visitor/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_MOOK_MUSIC_AUDIENCE)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/music_audience, BB_MOOK_MUSIC_AUDIENCE, /mob/living/carbon/human) - return - var/atom/home = controller.blackboard[BB_HOME_VILLAGE] - if(isnull(home)) - return - - var/atom/human_target = controller.blackboard[BB_MOOK_MUSIC_AUDIENCE] - if(get_dist(human_target, home) > controller.blackboard[BB_MAXIMUM_DISTANCE_TO_VILLAGE] || controller.blackboard[BB_STORM_APPROACHING]) - controller.clear_blackboard_key(BB_MOOK_MUSIC_AUDIENCE) - return - - controller.queue_behavior(/datum/ai_behavior/travel_towards, BB_MOOK_MUSIC_AUDIENCE) - -/datum/ai_behavior/find_and_set/music_audience - -/datum/ai_behavior/find_and_set/music_audience/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/atom/home = controller.blackboard[BB_HOME_VILLAGE] - for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) - if(target.stat > UNCONSCIOUS || !target.mind) - continue - if(isnull(home) || get_dist(target, home) > controller.blackboard[BB_MAXIMUM_DISTANCE_TO_VILLAGE]) - continue - return target - -/datum/idle_behavior/walk_near_target/mook_village - target_key = BB_HOME_VILLAGE ///healer mooks guard the village from intruders and heal the miner mooks when they come home /datum/ai_controller/basic_controller/mook/support + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/support.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mook, BB_MAXIMUM_DISTANCE_TO_VILLAGE = 10, BB_STORM_APPROACHING = FALSE, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, ) - idle_behavior = /datum/idle_behavior/walk_near_target/mook_village - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/look_for_village, - /datum/ai_planning_subtree/acknowledge_chief, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/use_mob_ability/mook_jump, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/injured_mooks, - ) - -///tree to find and register our leader -/datum/ai_planning_subtree/acknowledge_chief/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_MOOK_TRIBAL_CHIEF)) - return - controller.queue_behavior(/datum/ai_behavior/find_and_set/find_chief, BB_MOOK_TRIBAL_CHIEF, /mob/living/basic/mining/mook/worker/tribal_chief) - -/datum/ai_behavior/find_and_set/find_chief/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/chief = locate(locate_path) in oview(search_range, controller.pawn) - if(isnull(chief)) - return null - var/mob/living/living_pawn = controller.pawn - living_pawn.befriend(chief) - return chief - -///find injured miner mooks after they come home from a long day of work -/datum/ai_planning_subtree/find_and_hunt_target/injured_mooks - target_key = BB_INJURED_MOOK - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/injured_mooks - finding_behavior = /datum/ai_behavior/find_hunt_target/injured_mooks - hunt_targets = list(/mob/living/basic/mining/mook/worker) - hunt_range = 9 - -///we only heal when the mooks are home during a storm -/datum/ai_planning_subtree/find_and_hunt_target/injured_mooks/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_STORM_APPROACHING]) - return ..() - - -/datum/ai_behavior/find_hunt_target/injured_mooks - -/datum/ai_behavior/find_hunt_target/injured_mooks/valid_dinner(mob/living/source, mob/living/injured_mook) - return (injured_mook.health < injured_mook.maxHealth) - -/datum/ai_behavior/hunt_target/interact_with_target/injured_mooks - always_reset_target = TRUE - hunt_cooldown = 10 SECONDS - ///the chief would rather command his mooks to attack people than attack them himself /datum/ai_controller/basic_controller/mook/tribal_chief + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mook, BB_STORM_APPROACHING = FALSE, ) - idle_behavior = /datum/idle_behavior/walk_near_target/mook_village - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/look_for_village, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/leap, - /datum/ai_planning_subtree/issue_commands, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/material_stand, - /datum/ai_planning_subtree/use_mob_ability/mook_jump, - /datum/ai_planning_subtree/find_and_hunt_target/bonfire, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/tribal_chief, - ) - -/datum/ai_planning_subtree/issue_commands - ///how far we look for a mook to command - var/command_distance = 5 - -/datum/ai_planning_subtree/issue_commands/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!locate(/mob/living/basic/mining/mook) in oview(command_distance, controller.pawn)) - return - if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_BASIC_MOB_CURRENT_TARGET, /datum/pet_command/attack) - return - var/atom/ore_target = controller.blackboard[BB_ORE_TARGET] - var/mob/living/living_pawn = controller.pawn - if(isnull(ore_target)) - return - if(get_dist(ore_target, living_pawn) <= 1) - return - - controller.queue_behavior(/datum/ai_behavior/issue_commands, BB_ORE_TARGET, /datum/pet_command/fetch) - -/datum/ai_behavior/issue_commands - action_cooldown = 5 SECONDS - -/datum/ai_behavior/issue_commands/perform(seconds_per_tick, datum/ai_controller/controller, target_key, command_path) - var/mob/living/basic/living_pawn = controller.pawn - var/atom/target = controller.blackboard[target_key] - - if(isnull(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/datum/pet_command/to_command = locate(command_path) in GLOB.mook_commands - if(isnull(to_command)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/issue_command = pick(to_command.speech_commands) - living_pawn.say(issue_command, forced = "controller") - living_pawn._pointed(target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - -///find an ore, only pick it up when a mook brings it close to us -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/tribal_chief - -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/tribal_chief/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(locate(/obj/item/stack/ore) in living_pawn) - return - - var/atom/target_ore = controller.blackboard[BB_ORE_TARGET] - - if(isnull(target_ore)) - return ..() - - if(!isturf(target_ore.loc)) //picked up by someone else - controller.clear_blackboard_key(BB_ORE_TARGET) - return - - if(get_dist(target_ore, living_pawn) > 1) - return - - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/bonfire - target_key = BB_MOOK_BONFIRE_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/bonfire - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/bonfire - hunt_targets = list(/obj/structure/bonfire) - hunt_range = 9 - - -/datum/ai_behavior/find_hunt_target/bonfire - -/datum/ai_behavior/find_hunt_target/bonfire/valid_dinner(mob/living/source, obj/structure/bonfire/fire, radius) - if(fire.burning) - return FALSE - - return can_see(source, fire, radius) - -/datum/ai_behavior/hunt_target/interact_with_target/bonfire - always_reset_target = TRUE +/datum/ai_controller/basic_controller/mook/tribal_chief/New(atom/new_pawn) + . = ..() + set_blackboard_key(BB_MOOK_COMMANDS, GLOB.mook_commands) diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_bt.dm b/code/modules/mob/living/basic/lavaland/mook/mook_bt.dm new file mode 100644 index 000000000000..d1ba42a14fe6 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/mook_bt.dm @@ -0,0 +1,156 @@ + +///hey buddy I went to mook village and everyone there knew you +/datum/bt_node/ai_behavior/find_village + /// Blackboard key to store the found landmark. + var/target_key = BB_HOME_VILLAGE + +/datum/bt_node/ai_behavior/find_village/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/effect/landmark/home = locate(/obj/effect/landmark/mook_village) in GLOB.landmarks_list + if(isnull(home)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + controller.set_blackboard_key(target_key, home) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/// Mook-specific mineral wall finder that skips turfs in BB_BLACKLIST_MINERAL_TURFS. +/datum/bt_node/ai_behavior/find_mineral_wall/mook + +/datum/bt_node/ai_behavior/find_mineral_wall/mook/check_if_mineable(datum/ai_controller/controller, turf/target_wall) + var/list/forbidden = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS] + if(is_type_in_list(target_wall, forbidden)) + return FALSE + return ..() + +///Wander in a random direction to find ore +/datum/bt_node/ai_behavior/calculate_wander_destination + /// Blackboard key holding the anchor atom to wander away from. + var/anchor_key = BB_HOME_VILLAGE/// Blackboard key to write the chosen turf into. + var/destination_key = BB_WANDER_DESTINATION + /// How far we try to wander from the anchor. + var/wander_distance = 9 + +/datum/bt_node/ai_behavior/calculate_wander_destination/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/atom/anchor = controller.blackboard[anchor_key] + if(QDELETED(anchor)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(anchor.z != living_pawn.z) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/list/angle_directions = list() + for(var/direction in GLOB.alldirs) + angle_directions += dir2angle(direction) + + var/angle_to_home = get_angle(living_pawn, anchor) + angle_directions -= angle_to_home + angle_directions -= (angle_to_home + 45) + angle_directions -= (angle_to_home - 45) + shuffle_inplace(angle_directions) + + var/turf/best = get_turf(living_pawn) + for(var/angle in angle_directions) + var/turf/candidate = _get_furthest_turf(living_pawn, angle, anchor) + if(isnull(candidate)) + continue + var/dist = get_dist(anchor, candidate) + if(dist <= get_dist(anchor, best)) + continue + best = candidate + if(dist >= wander_distance) + break + + controller.set_blackboard_key(destination_key, best) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/calculate_wander_destination/proc/_get_furthest_turf(atom/source, angle, atom/anchor) + var/turf/result + for(var/i in 1 to wander_distance) + var/turf/candidate = get_ranged_target_turf_direct(source, anchor, range = i, offset = angle) + if(candidate.is_blocked_turf(source_atom = source)) + break + result = candidate + return result + + +///gotta get south of the material stand +/datum/bt_node/ai_behavior/find_deposit_position + /// Blackboard key holding the /obj/structure/ore_container/material_stand target. + var/stand_key = BB_MATERIAL_STAND_TARGET + /// Blackboard key to write the chosen deposit turf into. + var/destination_key = BB_DEPOSIT_POSITION + +/datum/bt_node/ai_behavior/find_deposit_position/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/stand = controller.blackboard[stand_key] + if(QDELETED(stand)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/pawn = controller.pawn + var/list/candidates = list() + for(var/direction in list(SOUTH, SOUTHWEST, SOUTHEAST)) + var/turf/candidate = get_step(stand, direction) + if(!candidate.is_blocked_turf()) + candidates += candidate + var/turf/destination = get_closest_atom(/turf/, candidates, pawn) + if(isnull(destination)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + controller.set_blackboard_key(destination_key, destination) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +///OH SHIT STORM COMING (or maybe we found ore :3) +/datum/bt_node/decorator/mook_has_flee_reason + +/datum/bt_node/decorator/mook_has_flee_reason/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list(\ + COMSIG_AI_BLACKBOARD_KEY_SET(BB_STORM_APPROACHING),\ + COMSIG_AI_BLACKBOARD_KEY_SET(BB_SIMPLE_CARRY_ITEM),\ + COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_SIMPLE_CARRY_ITEM),\ + ), PROC_REF(on_signal_changed)) + return TRUE + +/datum/bt_node/decorator/mook_has_flee_reason/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list(\ + COMSIG_AI_BLACKBOARD_KEY_SET(BB_STORM_APPROACHING),\ + COMSIG_AI_BLACKBOARD_KEY_SET(BB_SIMPLE_CARRY_ITEM),\ + COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_SIMPLE_CARRY_ITEM),\ + )) + +/datum/bt_node/decorator/mook_has_flee_reason/check_condition(datum/ai_controller/controller) + return controller.blackboard[BB_STORM_APPROACHING] || !isnull(controller.blackboard[BB_SIMPLE_CARRY_ITEM]) + + + +/datum/bt_node/subtree/generic_mook_behavior + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/generic_mook_behavior.bt.json" + + +///Worker trees +/datum/bt_node/subtree/worker_find_targets + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.json" + +/datum/bt_node/subtree/go_mining + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/go_mining.bt.json" + + +///Bard trees +/datum/bt_node/subtree/bard_play_music + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/bard_play_music.bt.json" + +/datum/bt_node/subtree/bard_find_targets + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/bard_find_targets.bt.json" + + +///Support trees +/datum/bt_node/subtree/heal_injured + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/heal_injured.bt.json" + +/datum/bt_node/subtree/support_find_targets + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.json" + +///Chief trees +/datum/bt_node/subtree/chief_issue_commands + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/chief_issue_commands.bt.json" + +/datum/bt_node/subtree/chief_manage_village + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/chief_manage_village.bt.json" + + +/datum/bt_node/subtree/chief_find_targets + behavior_tree_json = "code/modules/mob/living/basic/lavaland/mook/chief_find_targets.bt.json" diff --git a/code/modules/mob/living/basic/lavaland/mook/support.bt.json b/code/modules/mob/living/basic/lavaland/mook/support.bt.json new file mode 100644 index 000000000000..ad0fbb631c1a --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/support.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mook/support", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_mook_behavior", + "bindings": { + "b1kiiayf": "/datum/bt_node/subtree/heal_injured", + "b0ymbjo1": "/datum/bt_node/subtree/support_find_targets" + } +} diff --git a/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.json b/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.json new file mode 100644 index 000000000000..c87cf49caa0b --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/support_find_targets.bt.json @@ -0,0 +1,63 @@ +{ + "dm_type": "/datum/bt_node/subtree/support_find_targets", + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_MOOK_TRIBAL_CHIEF" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "target_source": "/datum/target_source/oview_single_type/tribal_chief", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "long_range_friendship": true, + "forget_target": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "invert": true, + "key": "BB_STORM_APPROACHING" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_INJURED_MOOK", + "target_source": "/datum/target_source/oview_typed/from_bb_key/mook_heal_targets", + "targeting_strategy": "/datum/targeting_strategy/injured_mob" + } + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.json b/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.json new file mode 100644 index 000000000000..3f526950dd0b --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/tribal_chief.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mook/tribal_chief", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/generic_mook_behavior", + "bindings": { + "b0ymbjo1": "/datum/bt_node/subtree/chief_find_targets", + "b1kiiayf": "/datum/bt_node/subtree/chief_manage_village", + "bk7ixti3": "/datum/bt_node/subtree/chief_issue_commands" + } +} diff --git a/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.json b/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.json new file mode 100644 index 000000000000..26616bcbe111 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/mook/worker_find_targets.bt.json @@ -0,0 +1,72 @@ +{ + "dm_type": "/datum/bt_node/subtree/worker_find_targets", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_MATERIAL_STAND_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MATERIAL_STAND_TARGET", + "target_source": "/datum/target_source/oview_single_type/ore_stand", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_MOOK_TRIBAL_CHIEF" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "target_source": "/datum/target_source/oview_single_type/tribal_chief", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_MOOK_TRIBAL_CHIEF", + "long_range_friendship": true, + "forget_target": false + } + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.json b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.json new file mode 100644 index 000000000000..935c577753eb --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.json @@ -0,0 +1,73 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/node_drone", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": true, + "target_key": "BB_CURRENT_HUNTING_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/latch_onto", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "hunt_cooldown": "5 SECONDS" + } + } + ] + } + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/ore_vent", + "targeting_strategy": "/datum/targeting_strategy/ore_vent_unclaimed", + "vision_range": 7 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm index 0f1f0962559d..ad2f7b5ea752 100644 --- a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm +++ b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm @@ -51,6 +51,7 @@ /mob/living/basic/node_drone/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_MINING_AOE_IMMUNE, INNATE_TRAIT) + AddElement(/datum/element/ai_retaliate) /mob/living/basic/node_drone/death(gibbed) . = ..() @@ -146,43 +147,27 @@ return /// The node drone AI controller -// Generally, this is a very simple AI that will try to find a vent and latch onto it, unless attacked by a lavaland mob, who it will try to flee from. /datum/ai_controller/basic_controller/node_drone + behavior_tree_json = "code/modules/mob/living/basic/lavaland/node_drone/node_drone.bt.json" blackboard = list( - BB_BASIC_MOB_FLEEING = FALSE, // Will flee when the vent lies undefended. - BB_CURRENT_HUNTING_TARGET = null, // Hunts for vents. - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, // Use this to find vents to run away from + BB_CURRENT_HUNTING_TARGET = null, + BB_CURRENT_TARGET_HIDING_LOCATION = null, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_FLEE_DISTANCE = 3, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = null - planning_subtrees = list( - // Priority is see if lavaland mobs are attacking us to flee from them. - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - // Fly you fool - /datum/ai_planning_subtree/flee_target/node_drone, - // Otherwise, look for and execute hunts for vents to latch onto. - /datum/ai_planning_subtree/find_and_hunt_target/look_for_vent, - ) - -// Node subtree to hunt down ore vents. -/datum/ai_planning_subtree/find_and_hunt_target/look_for_vent - hunting_behavior = /datum/ai_behavior/hunt_target/latch_onto/node_drone - hunt_targets = list(/obj/structure/ore_vent) - hunt_range = 7 // Hunt vents to the end of the earth. -// node drone behavior for buckling down on a vent. -/datum/ai_behavior/hunt_target/latch_onto/node_drone - hunt_cooldown = 5 SECONDS +/// Validates an ore vent as a valid hunt target: must exist and have no drone already latched. +/datum/targeting_strategy/ore_vent_unclaimed -// Evasion behavior. -/datum/ai_planning_subtree/flee_target/node_drone - flee_behaviour = /datum/ai_behavior/run_away_from_target/drone - -/datum/ai_behavior/run_away_from_target/drone - action_cooldown = 1 SECONDS - run_distance = 3 +/datum/targeting_strategy/ore_vent_unclaimed/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/obj/structure/ore_vent/vent = target + return istype(vent) && isnull(vent.node) #undef FLY_IN_STATE diff --git a/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.json b/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.json new file mode 100644 index 000000000000..0147a5c5a023 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.json @@ -0,0 +1,108 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/baby_raptor", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "chance": 0.3 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/express_happiness" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "chance": 0.15 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_FOUND_MOM" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_MOM" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/look_to_parent" + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/raptor_find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_FOUND_MOM" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mom" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.json b/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.json new file mode 100644 index 000000000000..6dd9e84fa56b --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.json @@ -0,0 +1,50 @@ +{ + "dm_type": "/datum/bt_node/subtree/care_for_young", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_RAPTOR_BABY" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.6 + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_RAPTOR_BABY" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_RAPTOR_BABY", + "combat_mode": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "\"grooms its baby!\"" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_RAPTOR_BABY" + } + } + ] + } + } +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.json new file mode 100644 index 000000000000..52a56e06a2e9 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.json @@ -0,0 +1,221 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/raptor", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/raptor_flee" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/raptor_heal_injured" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/raptor_food_trough" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "chance": 0.4 + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/play_with_owner" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/care_for_young" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/raptor_find_food" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_RAPTOR_PLAYFUL" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_OWNER_TARGET", + "targeting_strategy": "/datum/targeting_strategy/ally_mob", + "target_source": "/datum/target_source/oview_single_type/living_mob" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_BASIC_MOB_HEALER" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_INJURED_RAPTOR", + "targeting_strategy": "/datum/targeting_strategy/injured_mob/not_self/injured_raptor" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/retrieve_injured_rider", + "vars": { + "target_key": "BB_INJURED_RAPTOR" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_RAPTOR_MOTHERLY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_RAPTOR_BABY", + "targeting_strategy": "/datum/targeting_strategy/healthy_raptor_baby", + "target_source": "/datum/target_source/oview_raptor_babies" + } + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/express_happiness" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm deleted file mode 100644 index 777a76de1a68..000000000000 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/ai_behavior/hunt_target/interact_with_target/heal_raptor - always_reset_target = TRUE - -/datum/ai_behavior/find_hunt_target/injured_raptor - action_cooldown = 7.5 SECONDS - -/datum/ai_behavior/find_hunt_target/injured_raptor/valid_dinner(mob/living/source, mob/living/target, radius) - return (source != target && target.health < target.maxHealth) - -/datum/ai_behavior/find_hunt_target/raptor_baby/valid_dinner(mob/living/source, mob/living/target, radius) - if (!can_see(source, target, radius) || target.stat == DEAD || !istype(target, /mob/living/basic/raptor)) - return FALSE - var/mob/living/basic/raptor/raptor = target - return raptor.growth_stage == RAPTOR_BABY - -/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young - -/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young/target_caught(mob/living/hunter, atom/hunted) - hunter.manual_emote("grooms [hunted]!") - return ..() - -/datum/ai_behavior/find_hunt_target/raptor_trough - action_cooldown = 7.5 SECONDS - -/datum/ai_behavior/find_hunt_target/raptor_trough/valid_dinner(mob/living/source, atom/movable/trough, radius) - return !!(locate(/obj/item/stack/ore) in trough.contents) - -/datum/ai_behavior/find_injured_rider/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range) - var/mob/living/living_mob = controller.pawn - if (!length(living_mob.buckled_mobs) || !isliving(living_mob.buckled_mobs[1])) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/rider = living_mob.buckled_mobs[1] - if (rider.stat == CONSCIOUS || rider.stat == DEAD || rider.health >= rider.maxHealth) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(hunting_target_key, rider) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm index bae8bd0d2cdc..91cd704332ba 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/raptor + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_ai.bt.json" blackboard = list( BB_INTERACTIONS_WITH_OWNER = list( "pecks", @@ -11,31 +12,13 @@ BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/raptor), BB_MAX_CHILDREN = 5, BB_RAPTOR_FLEE_THRESHOLD = 0.25, + BB_FUCKS = TRUE ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/find_and_hunt_target/heal_rider, - /datum/ai_planning_subtree/find_and_hunt_target/heal_raptors, - /datum/ai_planning_subtree/random_speech/blackboard, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/raptor_trough, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/care_for_young, - /datum/ai_planning_subtree/make_babies, - /datum/ai_planning_subtree/express_happiness, - /datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor, - ) /// Angry raptors with no faction check on retaliation /datum/ai_controller/basic_controller/raptor/aggressive + ai_movement = /datum/ai_movement/basic_avoidance blackboard = list( BB_INTERACTIONS_WITH_OWNER = list( "pecks", @@ -49,24 +32,7 @@ BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/raptor), BB_MAX_CHILDREN = 5, BB_RAPTOR_FLEE_THRESHOLD = 0.1, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/find_and_hunt_target/heal_raptors, - /datum/ai_planning_subtree/random_speech/blackboard, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target/hunt, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/raptor_trough, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/care_for_young, - /datum/ai_planning_subtree/make_babies, - /datum/ai_planning_subtree/express_happiness, - /datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor, + BB_FUCKS = TRUE ) /datum/ai_controller/basic_controller/raptor/TryPossessPawn(atom/new_pawn) @@ -84,24 +50,11 @@ SIGNAL_HANDLER REMOVE_TRAIT(pawn, TRAIT_MOB_DIFFICULT_TO_MOUNT, REF(src)) -/datum/ai_controller/basic_controller/raptor/on_mob_eat() - . = ..() - clear_blackboard_key(BB_RAPTOR_TROUGH_TARGET) - /datum/ai_controller/basic_controller/baby_raptor + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/baby_raptor.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FIND_MOM_TYPES = list(/mob/living/basic/raptor), + BB_IGNORE_MOM_TYPES = list(/mob/living/basic/raptor/baby) ) - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/random_speech/blackboard, - /datum/ai_planning_subtree/find_and_hunt_target/raptor_trough, - /datum/ai_planning_subtree/express_happiness, - /datum/ai_planning_subtree/look_for_adult/raptor, - ) diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm index 66f089240236..6d05cdeb4a36 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm @@ -1,73 +1,18 @@ -/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors - target_key = BB_INJURED_RAPTOR - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/heal_raptor - finding_behavior = /datum/ai_behavior/find_hunt_target/injured_raptor - hunt_targets = list(/mob/living/basic/raptor) - hunt_chance = 70 - hunt_range = 9 +/datum/bt_node/subtree/raptor_flee + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.json" -/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_BASIC_MOB_HEALER]) - return - return ..() +/datum/bt_node/subtree/raptor_heal_injured + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.json" -/datum/ai_planning_subtree/find_and_hunt_target/heal_rider - target_key = BB_INJURED_RAPTOR - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/heal_raptor - finding_behavior = /datum/ai_behavior/find_injured_rider - hunt_targets = list(/mob/living/carbon/human) +/datum/bt_node/subtree/raptor_food_trough + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.json" -/datum/ai_planning_subtree/find_and_hunt_target/heal_rider/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_BASIC_MOB_HEALER]) - return - return ..() +/datum/bt_node/subtree/raptor_find_food + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.json" -/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor - target_key = BB_BASIC_MOB_FLEE_TARGET +/datum/bt_node/subtree/care_for_young + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/care_for_young.bt.json" -/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_RAPTOR_COWARD]) - return ..() - var/mob/living/basic/raptor/raptor = controller.pawn - if (raptor.health <= raptor.maxHealth * controller.blackboard[BB_RAPTOR_FLEE_THRESHOLD]) - return ..() - - if (!length(raptor.buckled_mobs)) - return - - var/mob/living/buckled_to = raptor.buckled_mobs[1] - // Flee if our owner is badly injured and out - if (buckled_to.stat != CONSCIOUS && buckled_to.stat != DEAD) - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/care_for_young - target_key = BB_RAPTOR_BABY - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young - finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_baby - hunt_targets = list(/mob/living/basic/raptor) - hunt_chance = 75 - hunt_range = 9 - -/datum/ai_planning_subtree/find_and_hunt_target/care_for_young/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_RAPTOR_MOTHERLY]) - return - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough - target_key = BB_RAPTOR_TROUGH_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off - finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_trough - hunt_targets = list(/obj/structure/ore_container/food_trough/raptor_trough) - hunt_chance = 80 - hunt_range = 9 - -/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(world.time < controller.blackboard[BB_NEXT_FOOD_EAT]) - return - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_RAPTOR_PLAYFUL]) - return - return ..() +/datum/bt_node/subtree/play_with_owner + behavior_tree_json = "code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.json" diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm index 25129736487f..de65f8dd6353 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm @@ -234,7 +234,7 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors()) /obj/item/mob_holder/purple_raptor/proc/can_fly(silent = FALSE) var/mob/living/carbon/human/user = loc - if (!istype(user) || user.stat || user.body_position == LYING_DOWN || isnull(user.client)) + if (!istype(user) || IS_UNCONSCIOUS_OR_CRIT(user) || user.body_position == LYING_DOWN || isnull(user.client)) return FALSE var/turf/location = get_turf(user) @@ -398,7 +398,7 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors()) if (istype(target, /mob/living/basic/raptor)) return TRUE // Only heal raptors, or critted rider - if (target.stat == CONSCIOUS || target.stat == DEAD) + if (target.stat == STABLE || target.stat == DEAD) return FALSE return target.buckled == healer diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_egg.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_egg.dm index 2dc093ca27d2..7ba088242a06 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/raptor_egg.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_egg.dm @@ -17,6 +17,7 @@ /obj/item/food/egg/raptor_egg/Initialize(mapload) . = ..() + AddElement(/datum/element/floor_placeable) START_PROCESSING(SSobj, src) /obj/item/food/egg/raptor_egg/Destroy() diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.json new file mode 100644 index 000000000000..1912562fe829 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_find_food.bt.json @@ -0,0 +1,31 @@ +{ + "dm_type": "/datum/bt_node/subtree/raptor_find_food", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "cooldown_key": "BB_NEXT_EAT_FOOD" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": false, + "finish_on_primary": false, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_RAPTOR_TROUGH_TARGET", + "targeting_strategy": "/datum/targeting_strategy/raptor_trough", + "target_source": "/datum/target_source/oview_single_type/raptor_trough" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.json new file mode 100644 index 000000000000..182ead097935 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_flee.bt.json @@ -0,0 +1,41 @@ +{ + "dm_type": "/datum/bt_node/subtree/raptor_flee", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_RAPTOR_COWARDLY" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_health_below", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "health_blackboard_key": "BB_RAPTOR_FLEE_THRESHOLD" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/check_rider_stat", + "vars": { + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.json new file mode 100644 index 000000000000..84e80f387c42 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_heal_injured.bt.json @@ -0,0 +1,36 @@ +{ + "dm_type": "/datum/bt_node/subtree/raptor_heal_injured", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_INJURED_RAPTOR" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_INJURED_RAPTOR" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_INJURED_RAPTOR", + "combat_mode": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_INJURED_RAPTOR" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.json new file mode 100644 index 000000000000..3293e410e7c4 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_play_with_owner.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/raptor/aggressive", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_OWNER_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_OWNER_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/play_with_owner", + "vars": { + "cooldown_key": "BB_OWNER_TARGET" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.json b/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.json new file mode 100644 index 000000000000..4c988ea6a27d --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_trough.bt.json @@ -0,0 +1,36 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/baby_raptor", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_RAPTOR_TROUGH_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_RAPTOR_TROUGH_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/ai_interact", + "vars": { + "target_key": "BB_RAPTOR_TROUGH_TARGET", + "combat_mode": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_RAPTOR_TROUGH_TARGET" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.json b/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.json new file mode 100644 index 000000000000..63f0c0769583 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/tendril/tendril.bt.json @@ -0,0 +1,124 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/tendril", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": false, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 7 + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_TENDRIL_SPIKES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_TENDRIL_LASH" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_TENDRIL_CHASER", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": true, + "vision_range": 5 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "target_loss_distance": 9, + "vision_range": 5 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/tendril/tendril.dm b/code/modules/mob/living/basic/lavaland/tendril/tendril.dm index d0a131d8267e..ee199dbf1d64 100644 --- a/code/modules/mob/living/basic/lavaland/tendril/tendril.dm +++ b/code/modules/mob/living/basic/lavaland/tendril/tendril.dm @@ -95,7 +95,7 @@ GLOBAL_LIST_INIT(tendrils, list()) return ..() for(var/mob/living/killer in view(7, src)) - if(killer.stat || !killer.client) + if(IS_UNCONSCIOUS_OR_CRIT(killer) || !killer.client) continue killer.client.give_award(/datum/award/score/tendril_score, killer) if (!length(GLOB.tendrils)) @@ -132,7 +132,7 @@ GLOBAL_LIST_INIT(tendrils, list()) return var/beat_rate = HEARTBEAT_NORMAL - if (ai_controller?.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + if (ai_controller?.blackboard[BB_CURRENT_TARGET]) beat_rate = round(HEARTBEAT_FRANTIC + health / maxHealth * (HEARTBEAT_FAST - HEARTBEAT_FRANTIC), 0.05 SECONDS) if (beat_rate != soundloop.mid_length) diff --git a/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm b/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm index 5da4fcedf285..db605e350dae 100644 --- a/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm +++ b/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm @@ -1,40 +1,7 @@ /datum/ai_controller/basic_controller/tendril + behavior_tree_json = "code/modules/mob/living/basic/lavaland/tendril/tendril.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_AGGRO_RANGE = 9, // Keeps an eye on you even if you flee - BB_AGGRO_GRAB_RANGE = 5, // Only aggros if you get real close and personal ) - - planning_subtrees = list( - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/tendril_chaser, - /datum/ai_planning_subtree/use_mob_ability/tendril_spikes, - /datum/ai_planning_subtree/use_mob_ability/tendril_lash, - ) - -/datum/ai_planning_subtree/targeted_mob_ability/tendril_chaser - ability_key = BB_TENDRIL_CHASER - operational_datums = list(/datum/component/ai_target_timer) - finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/tendril_lash - ability_key = BB_TENDRIL_LASH - -/datum/ai_planning_subtree/use_mob_ability/tendril_lash/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (isnull(target) || get_dist(controller.pawn, target) > /obj/projectile/tentacle_lash::range) - return FALSE - return ..() - -/datum/ai_planning_subtree/use_mob_ability/tendril_spikes - ability_key = BB_TENDRIL_SPIKES - -/datum/ai_planning_subtree/use_mob_ability/tendril_spikes/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - var/datum/action/cooldown/mob_cooldown/tendril_cross_spikes/ability = controller.blackboard[ability_key] - if (isnull(target) || !istype(ability) || get_dist(controller.pawn, target) > ability.spike_range) - return FALSE - return ..() diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.json b/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.json new file mode 100644 index 000000000000..b408d011c4db --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher.bt.json @@ -0,0 +1,133 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/watcher", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_trait", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true, + "key": "TRAIT_OVERWATCHED" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_at_least", + "vars": { + "key": "BB_BASIC_MOB_HAS_TARGET_TIME", + "minimum": "5 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "max_range": 9 + } + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements/mining" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm index 4eceea8a73cc..1c6850aa55e7 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm @@ -107,7 +107,6 @@ health = 130 projectile_type = /obj/projectile/temp/watcher/ice_wing gaze_attack = /datum/action/cooldown/mob_cooldown/watcher_gaze/ice - butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) butcher_results = list( /obj/item/stack/sheet/bone = 1, /obj/item/stack/ore/diamond = 5, diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm index cd45eb9c6ecb..637f0f78bb92 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/watcher + behavior_tree_json = "code/modules/mob/living/basic/lavaland/watcher/watcher.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining, @@ -7,34 +8,3 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/call_reinforcements/mining, - /datum/ai_planning_subtree/target_retaliate/check_faction, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/use_mob_ability/gaze, - /datum/ai_planning_subtree/ranged_skirmish/watcher, - ) - -/datum/ai_planning_subtree/use_mob_ability/gaze - finish_planning = TRUE - -/datum/ai_planning_subtree/use_mob_ability/gaze/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (!isliving(target)) - return // Don't do this if there's nothing hostile around or if our target is a mech - var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0 - if (time_on_target < 5 SECONDS) - return // We need to spend some time acquiring our target first - return ..() - -/datum/ai_planning_subtree/ranged_skirmish/watcher - min_range = 0 - -/datum/ai_planning_subtree/ranged_skirmish/watcher/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (QDELETED(target) || HAS_TRAIT(target, TRAIT_OVERWATCHED)) - return // Don't bully people who are playing red light green light - return ..() diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_gaze.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_gaze.dm index 27687114d284..805f0f081bad 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_gaze.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_gaze.dm @@ -37,30 +37,39 @@ stage_timer = addtimer(CALLBACK(src, PROC_REF(show_indicator_overlay), "eye_pulse"), animation_time, TIMER_STOPPABLE) StartCooldown(360 SECONDS, 360 SECONDS) owner.visible_message(span_warning("[owner]'s eye glows ominously!")) - if (do_after(owner, delay = wait_delay, target = owner, hidden = TRUE)) + if (do_after(owner, delay = wait_delay, target = owner, cog_icon = null)) trigger_effect() + proxmon_cleanup() else deltimer(stage_timer) clear_current_overlay() + proxmon_cleanup() + tracked_mobs.Cut() StartCooldown() - tracked_mobs.Cut() - QDEL_NULL(proximity_monitor) return TRUE /datum/action/cooldown/mob_cooldown/watcher_gaze/Destroy() + proxmon_cleanup() tracked_mobs.Cut() - QDEL_NULL(proximity_monitor) deltimer(stage_timer) clear_current_overlay() return ..() /datum/action/cooldown/mob_cooldown/watcher_gaze/Remove(mob/removed_from) + proxmon_cleanup() tracked_mobs.Cut() - QDEL_NULL(proximity_monitor) deltimer(stage_timer) clear_current_overlay() return ..() +/datum/action/cooldown/mob_cooldown/watcher_gaze/proc/proxmon_cleanup() + if (!QDELETED(proximity_monitor)) + QDEL_NULL(proximity_monitor) + for (var/victim_ref in tracked_mobs) + var/mob/living/victim = locate(victim_ref) + if (victim) + UnregisterSignal(victim, list(COMSIG_ATOM_POST_DIR_CHANGE, COMSIG_MOB_STATCHANGE)) + /// Do some effects to whoever is looking at us /datum/action/cooldown/mob_cooldown/watcher_gaze/proc/trigger_effect() deltimer(stage_timer) @@ -68,7 +77,7 @@ for (var/mob/living/viewer in viewers(effect_radius, owner)) if (!valid_target(viewer)) continue - if (!apply_effect(viewer)) + if (!apply_effect(viewer) || !viewer.client) continue var/image/flashed_overlay = image( icon = 'icons/effects/eldritch.dmi', @@ -83,7 +92,7 @@ living_owner.Stun(1.5 SECONDS, ignore_canstun = TRUE) /datum/action/cooldown/mob_cooldown/watcher_gaze/proc/valid_target(mob/living/viewer) - if (!istype(viewer) || viewer.stat || viewer == owner) + if (!istype(viewer) || IS_UNCONSCIOUS_OR_CRIT(viewer) || viewer == owner) return FALSE if (!(viewer.dir & get_dir(viewer, owner))) return FALSE @@ -100,6 +109,7 @@ /// Animate our effect out /datum/action/cooldown/mob_cooldown/watcher_gaze/proc/hide_eye() show_indicator_overlay("eye_close") + tracked_mobs.Cut() stage_timer = addtimer(CALLBACK(src, PROC_REF(clear_current_overlay)), animation_time, TIMER_STOPPABLE) /// Display an animated overlay over our head to indicate what's going on diff --git a/code/modules/mob/living/basic/minebots/minebot.bt.json b/code/modules/mob/living/basic/minebots/minebot.bt.json new file mode 100644 index 000000000000..1e27d18584a6 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot.bt.json @@ -0,0 +1,107 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/minebot", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/minebot_combat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/minebot_mining" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MINER_FRIEND", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/rock_stoner" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_MINER_FRIEND", + "long_range_friendship": true + } + } + ] + }, + { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_MINEBOT_CRIT_ALERT_COOLDOWN", + "cooldown_duration": "10 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_NEARBY_DEAD_MINER", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/unconscious_human" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/send_sos_message", + "vars": { + "target_key": "BB_NEARBY_DEAD_MINER" + } + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm index 525f1450141b..ac361c561f98 100644 --- a/code/modules/mob/living/basic/minebots/minebot.dm +++ b/code/modules/mob/living/basic/minebots/minebot.dm @@ -110,7 +110,7 @@ for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits) . += span_notice("There is \a [modkit] installed, using [modkit.cost]% capacity.") - if(ai_controller && ai_controller.ai_status == AI_STATUS_IDLE) + if(ai_controller && ai_controller.ai_status == AI_STATUS_OFF && ai_controller.get_expected_ai_status() == AI_STATUS_ON) . += "The [src] appears to be in sleep mode. You can restore normal functions by tapping it." @@ -128,17 +128,21 @@ user.balloon_alert(user, "successfully repaired!") return TRUE -/mob/living/basic/mining_drone/attackby(obj/item/item_used, mob/user, list/modifiers, list/attack_modifiers) - if(item_used.tool_behaviour == TOOL_CROWBAR || istype(item_used, /obj/item/borg/upgrade/modkit)) - item_used.melee_attack_chain(user, stored_gun, modifiers) - return +/mob/living/basic/mining_drone/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/borg/upgrade/modkit)) + return ..() - return ..() + tool.melee_attack_chain(user, stored_gun, modifiers) + return ITEM_INTERACT_SUCCESS + +/mob/living/basic/mining_drone/crowbar_act(mob/living/user, obj/item/tool) + tool.melee_attack_chain(user, stored_gun) + return ITEM_INTERACT_SUCCESS /mob/living/basic/mining_drone/attack_hand(mob/living/carbon/human/user, list/modifiers) if(!user.combat_mode) - if(ai_controller && ai_controller.ai_status == AI_STATUS_IDLE) - ai_controller.set_ai_status(AI_STATUS_ON) + if(ai_controller && ai_controller.ai_status == AI_STATUS_OFF) + ai_controller.reset_ai_status() //wakes a performance-slept bot, no-op if it is off for a real reason if(LAZYACCESS(modifiers, LEFT_CLICK)) //Lets Right Click be specifically for re-enabling their AI (and avoiding the UI popup), while Left Click simply does both. ui_interact(user) return diff --git a/code/modules/mob/living/basic/minebots/minebot_ai.dm b/code/modules/mob/living/basic/minebots/minebot_ai.dm index 53895358d2a6..7708ed25a6af 100644 --- a/code/modules/mob/living/basic/minebots/minebot_ai.dm +++ b/code/modules/mob/living/basic/minebots/minebot_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/minebot + behavior_tree_json = "code/modules/mob/living/basic/minebots/minebot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -19,110 +20,60 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/launch_missiles, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/befriend_miners, - /datum/ai_planning_subtree/defend_node, - /datum/ai_planning_subtree/minebot_maintain_distance, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot, - /datum/ai_planning_subtree/minebot_mining, - /datum/ai_planning_subtree/locate_dead_humans, - ) - -/datum/ai_planning_subtree/launch_missiles - -/datum/ai_planning_subtree/launch_missiles/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/action/cooldown/missile_ability = controller.blackboard[BB_MINEBOT_MISSILE_ABILITY] - if(!missile_ability?.IsAvailable()) - return - if(!controller.blackboard_key_exists(BB_MINEBOT_MISSILE_TARGET)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/clear_bombing_zone, BB_MINEBOT_MISSILE_TARGET, /obj/effect/temp_visual/minebot_target, 7) - return - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target, BB_MINEBOT_MISSILE_ABILITY, BB_MINEBOT_MISSILE_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/find_and_set/clear_bombing_zone - -/datum/ai_behavior/find_and_set/clear_bombing_zone/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/obj/effect/temp_visual/minebot_target/target in oview(search_range, controller.pawn)) - if(isclosedturf(get_turf(target))) - continue - return target - return null -/datum/ai_planning_subtree/befriend_miners/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_MINER_FRIEND)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/miner_to_befriend, BB_MINER_FRIEND) - return - controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_MINER_FRIEND) +/datum/bt_node/subtree/minebot_combat + behavior_tree_json = "code/modules/mob/living/basic/minebots/minebot_combat.bt.json" -/datum/ai_behavior/find_and_set/miner_to_befriend +/datum/bt_node/subtree/minebot_mining + behavior_tree_json = "code/modules/mob/living/basic/minebots/minebot_mining.bt.json" -/datum/ai_behavior/find_and_set/miner_to_befriend/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) - if(HAS_TRAIT(target, TRAIT_ROCK_STONER)) - return target - return null +/// Mineral wall finder that skips turfs in the blacklist and the previously unreachable wall. +/datum/bt_node/ai_behavior/find_mineral_wall/minebot -/datum/ai_planning_subtree/defend_node/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[BB_DRONE_DEFEND] - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_DRONE_DEFEND, /mob/living/basic/node_drone) - return - var/mob/living/living_pawn = controller.pawn - if(!living_pawn.has_ally(target)) - controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_DRONE_DEFEND) - return - if(target.health < (target.maxHealth * 0.75) && controller.blackboard[BB_MINEBOT_REPAIR_DRONE]) - controller.queue_behavior(/datum/ai_behavior/repair_drone, BB_DRONE_DEFEND) - return SUBTREE_RETURN_FINISH_PLANNING +/datum/bt_node/ai_behavior/find_mineral_wall/minebot/check_if_mineable(datum/ai_controller/controller, turf/target_wall) + var/list/forbidden = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS] + var/turf/previous_unreachable = controller.blackboard[BB_PREVIOUS_UNREACHABLE_WALL] + if(is_type_in_list(target_wall, forbidden) || target_wall == previous_unreachable) + return FALSE + controller.clear_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL) + return ..() -/datum/ai_behavior/repair_drone - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH +/// Mines a mineral turf at range using RangedAttack rather than ai_interact. +/datum/bt_node/ai_behavior/minebot_mine_turf + time_between_perform = 3 SECONDS + var/target_key = BB_TARGET_MINERAL_TURF -/datum/ai_behavior/repair_drone/setup(datum/ai_controller/controller, target_key) - . = ..() +/datum/bt_node/ai_behavior/minebot_mine_turf/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/living_pawn = controller.pawn var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/repair_drone/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] if(QDELETED(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/living_pawn = controller.pawn - living_pawn.say("REPAIRING [target]!") - controller.ai_interact(target = target) + if(check_obstacles_in_path(controller, target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!living_pawn.combat_mode) + living_pawn.set_combat_mode(TRUE) + living_pawn.RangedAttack(target) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/repair_drone/finish_action(datum/ai_controller/controller, success, target_key) +/datum/bt_node/ai_behavior/minebot_mine_turf/proc/check_obstacles_in_path(datum/ai_controller/controller, turf/target) + var/mob/living/source = controller.pawn + var/list/turfs_in_path = get_line(source, target) - target + for(var/turf/turf in turfs_in_path) + if(turf.is_blocked_turf(ignore_atoms = list(source))) + controller.set_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL, target) + return TRUE + return FALSE + +/datum/bt_node/ai_behavior/minebot_mine_turf/finish_action(datum/ai_controller/controller, succeeded) . = ..() - if(!success) - controller.clear_blackboard_key(target_key) - -///find dead humans and report their location on the radio -/datum/ai_planning_subtree/locate_dead_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_NEARBY_DEAD_MINER)) - controller.queue_behavior(/datum/ai_behavior/send_sos_message, BB_NEARBY_DEAD_MINER) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_and_set/unconscious_human, BB_NEARBY_DEAD_MINER, /mob/living/carbon/human) - -/datum/ai_behavior/find_and_set/unconscious_human/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) - if(target.stat >= UNCONSCIOUS && target.mind) - return target - return null - -/datum/ai_behavior/send_sos_message - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 2 MINUTES - -/datum/ai_behavior/send_sos_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + controller.clear_blackboard_key(target_key) + +/// Sends a radio SOS message for a dead or unconscious miner. Clears the target key on finish. +/datum/bt_node/ai_behavior/send_sos_message + time_between_perform = 2 MINUTES + var/target_key = BB_NEARBY_DEAD_MINER + +/datum/bt_node/ai_behavior/send_sos_message/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/carbon/target = controller.blackboard[target_key] var/mob/living/living_pawn = controller.pawn if(QDELETED(target) || is_station_level(target.z)) @@ -135,140 +86,85 @@ radio_implant.radio.talk_into(living_pawn, message, RADIO_CHANNEL_SUPPLY) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/send_sos_message/finish_action(datum/ai_controller/controller, success, target_key) +/datum/bt_node/ai_behavior/send_sos_message/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) -///operational datums is null because we dont use a ranged component, we use a gun in our contents -/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot - operational_datums = null - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/minebot - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) - return - var/mob/living/living_pawn = controller.pawn - if(!living_pawn.combat_mode) //we are not on attack mode - return - controller.queue_behavior(ranged_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_planning_subtree/minebot_maintain_distance/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) - return - var/mob/living/living_pawn = controller.pawn - if(get_dist(living_pawn, target) <= controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE]) - controller.queue_behavior(/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot, BB_BASIC_MOB_CURRENT_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - -/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot +/// Moves adjacent to an allied node drone and repairs it if its health is below the threshold. +/// Fails if the drone is healthy, not allied, or repair is disabled. +/datum/bt_node/ai_behavior/repair_drone + var/target_key = BB_DRONE_DEFEND + var/repair_threshold = 0.75 -/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key) - if(!controller.blackboard[BB_MINEBOT_PLANT_MINES]) - return ..() - var/datum/action/cooldown/mine_ability = controller.blackboard[BB_MINEBOT_LANDMINE_ABILITY] - mine_ability?.Trigger() - return ..() - -/datum/ai_behavior/basic_ranged_attack/minebot - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - avoid_friendly_fire = TRUE - ///if our target is closer than this distance, finish action - var/minimum_distance = 3 - -/datum/ai_behavior/basic_ranged_attack/minebot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - . = ..() - minimum_distance = controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] ? controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] : initial(minimum_distance) - var/atom/target = controller.blackboard[target_key] +/datum/bt_node/ai_behavior/repair_drone/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] if(QDELETED(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!controller.blackboard[BB_MINEBOT_REPAIR_DRONE]) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/mob/living/living_pawn = controller.pawn - if(get_dist(living_pawn, target) <= minimum_distance) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -///mine walls if we are on automated mining mode -/datum/ai_planning_subtree/minebot_mining/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard[BB_AUTOMATED_MINING]) - return - if(controller.blackboard_key_exists(BB_TARGET_MINERAL_TURF)) - controller.queue_behavior(/datum/ai_behavior/minebot_mine_turf, BB_TARGET_MINERAL_TURF) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_mineral_wall/minebot, BB_TARGET_MINERAL_TURF) - -/datum/ai_behavior/find_mineral_wall/minebot - -/datum/ai_behavior/find_mineral_wall/minebot/check_if_mineable(datum/ai_controller/controller, turf/target_wall) - var/list/forbidden_turfs = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS] - var/turf/previous_unreachable_wall = controller.blackboard[BB_PREVIOUS_UNREACHABLE_WALL] - if(is_type_in_list(target_wall, forbidden_turfs) || target_wall == previous_unreachable_wall) - return FALSE - controller.clear_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL) - return ..() - -/datum/ai_behavior/minebot_mine_turf - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - required_distance = 2 - action_cooldown = 3 SECONDS - -/datum/ai_behavior/minebot_mine_turf/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/minebot_mine_turf/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/basic/living_pawn = controller.pawn - var/turf/target = controller.blackboard[target_key] - - if(QDELETED(target)) + if(!living_pawn.has_ally(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - if(check_obstacles_in_path(controller, target)) + if(target.health >= target.maxHealth * repair_threshold) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), "REPAIRING [target]!") + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - if(!living_pawn.combat_mode) - living_pawn.set_combat_mode(TRUE) +/// Turns off combat mode then interacts with a nearby ore to collect it. Clears the target key on finish. +/datum/bt_node/ai_behavior/collect_ore/minebot + var/target_key = BB_ORE_TARGET - living_pawn.RangedAttack(target) +/datum/bt_node/ai_behavior/collect_ore/minebot/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/item/stack/ore/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/living_pawn = controller.pawn + if(living_pawn.combat_mode) + living_pawn.set_combat_mode(FALSE) + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/minebot_mine_turf/proc/check_obstacles_in_path(datum/ai_controller/controller, turf/target) - var/mob/living/source = controller.pawn - var/list/turfs_in_path = get_line(source, target) - target - for(var/turf/turf in turfs_in_path) - if(turf.is_blocked_turf(ignore_atoms = list(source))) - controller.set_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL, target) - return TRUE - return FALSE - -/datum/ai_behavior/minebot_mine_turf/finish_action(datum/ai_controller/controller, success, target_key) +/datum/bt_node/ai_behavior/collect_ore/minebot/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) -///store ores in our body -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot - hunt_chance = 100 +/// befriend_target variant that fails immediately if the target is already an ally used to gate the drone-defend block. +/datum/bt_node/ai_behavior/befriend_target/check_ally -/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/automated_mining = controller.blackboard[BB_AUTOMATED_MINING] +/datum/bt_node/ai_behavior/befriend_target/check_ally/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/living_pawn = controller.pawn + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target) || living_pawn.has_ally(living_target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + return ..() - if(!automated_mining && living_pawn.combat_mode) //are we not on automated mining or collect mode? - return +/// BT-native ranged attack for the minebot. Avoids friendly fire. +/datum/bt_node/ai_behavior/basic_ranged_attack/minebot + avoid_friendly_fire = TRUE - return ..() +/// Accepts humans with TRAIT_ROCK_STONER (miners). +/datum/targeting_strategy/rock_stoner -/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot - hunt_cooldown = 2 SECONDS +/datum/targeting_strategy/rock_stoner/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!istype(target, /mob/living/carbon/human)) + return FALSE + return HAS_TRAIT(target, TRAIT_ROCK_STONER) -/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot/target_caught(mob/living/hunter, obj/item/stack/ore/hunted) - if(hunter.combat_mode) - hunter.set_combat_mode(FALSE) - return ..() +/// Accepts unconscious or dead humans that have a mind (i.e., real players/NPCs that need SOS). +/datum/targeting_strategy/unconscious_human + +/datum/targeting_strategy/unconscious_human/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(!istype(target, /mob/living/carbon/human)) + return FALSE + var/mob/living/carbon/human/human_target = target + return IS_UNCONSCIOUS_OR_CRIT(human_target) && human_target.mind ///pet commands /datum/pet_command/free/minebot @@ -305,9 +201,8 @@ var/datum/action/cooldown/ability = controller.blackboard[ability_key] if(!ability?.IsAvailable()) return - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key) + INVOKE_ASYNC(ability, TYPE_PROC_REF(/datum/action, Trigger)) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING /datum/pet_command/minebot_ability/light command_name = "Toggle lights" @@ -329,8 +224,12 @@ /datum/pet_command/minebot_ability/dump/retrieve_command_text(atom/living_pet, atom/target) return "signals [living_pet] to dump its ore!" +/datum/pet_command/minebot_ability/dump/execute_action(datum/ai_controller/controller) + controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE) //else bro will just pick it up + return ..() + /datum/pet_command/attack/minebot - attack_behaviour = /datum/ai_behavior/basic_ranged_attack/minebot + attack_subtree = /datum/bt_node/subtree/pet_command/attack/minebot /datum/pet_command/attack/minebot/execute_action(datum/ai_controller/controller) controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE) @@ -350,8 +249,8 @@ /datum/pet_command/protect_owner/minebot/set_command_target(mob/living/parent, atom/target) if(!parent.ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND]) return FALSE - if(!parent.ai_controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && !QDELETED(target)) //we are already dealing with something, - parent.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + if(!parent.ai_controller.blackboard_key_exists(BB_CURRENT_TARGET) && !QDELETED(target)) //we are already dealing with something, + parent.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, target) return TRUE /datum/pet_command/protect_owner/minebot/execute_action(datum/ai_controller/controller) @@ -359,5 +258,3 @@ var/mob/living/living_pawn = controller.pawn living_pawn.set_combat_mode(TRUE) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - - diff --git a/code/modules/mob/living/basic/minebots/minebot_combat.bt.json b/code/modules/mob/living/basic/minebots/minebot_combat.bt.json new file mode 100644 index 000000000000..7a8e60c9fd19 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot_combat.bt.json @@ -0,0 +1,112 @@ +{ + "dm_type": "/datum/bt_node/subtree/minebot_combat", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_MINEBOT_MISSILE_ABILITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_MINEBOT_MISSILE_ABILITY", + "target_key": "BB_MINEBOT_MISSILE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/minebot", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "min_dist_key": "BB_MINIMUM_SHOOTING_DISTANCE", + "max_dist_key": "BB_MINIMUM_SHOOTING_DISTANCE" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MINEBOT_MISSILE_TARGET", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MINEBOT_MISSILE_TARGET", + "target_source": "/datum/target_source/oview_single_type/minebot_target", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 7 + } + } + } + ] + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": false, + "key": "BB_MINEBOT_LANDMINE_ABILITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_MINEBOT_LANDMINE_ABILITY" + } + } + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/minebots/minebot_mining.bt.json b/code/modules/mob/living/basic/minebots/minebot_mining.bt.json new file mode 100644 index 000000000000..e8ad56080db2 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot_mining.bt.json @@ -0,0 +1,193 @@ +{ + "dm_type": "/datum/bt_node/subtree/minebot_mining", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_MINEBOT_REPAIR_DRONE" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DRONE_DEFEND" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_health_below_fraction", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_DRONE_DEFEND", + "fraction": 0.75 + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DRONE_DEFEND", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/repair_drone", + "vars": { + "target_key": "BB_DRONE_DEFEND" + } + } + ] + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target/check_ally", + "vars": { + "target_key": "BB_DRONE_DEFEND", + "long_range_friendship": true, + "forget_target": false + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_ORE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_ORE_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/collect_ore/minebot", + "vars": { + "target_key": "BB_ORE_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_AUTOMATED_MINING" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TARGET_MINERAL_TURF" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TARGET_MINERAL_TURF", + "required_dist": 2, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/minebot_mine_turf", + "vars": { + "target_key": "BB_TARGET_MINERAL_TURF" + } + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "time_between_perform": "5 SECONDS", + "target_key": "BB_DRONE_DEFEND", + "targeting_strategy": "/datum/targeting_strategy/anything", + "target_source": "/datum/target_source/oview_single_type/node_drone", + "vision_range": 9, + "revalidation_mode": "TARGET_REVALIDATE" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_AUTOMATED_MINING" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_ORE_TARGET", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_single_type/ore", + "vision_range": 2 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mineral_wall/minebot", + "vars": { + "target_key": "BB_TARGET_MINERAL_TURF" + } + } + ] + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm b/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm deleted file mode 100644 index 655a6431fc2a..000000000000 --- a/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm +++ /dev/null @@ -1,62 +0,0 @@ -/datum/ai_controller/basic_controller/cat/bread - planning_subtrees = list( - /datum/ai_planning_subtree/find_and_hunt_target/turn_off_stove, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_mice, - /datum/ai_planning_subtree/find_and_hunt_target/find_cat_food, - /datum/ai_planning_subtree/haul_food_to_young, - /datum/ai_planning_subtree/random_speech/cats, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/turn_off_stove - target_key = BB_STOVE_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/stove_target - finding_behavior = /datum/ai_behavior/find_hunt_target/stove - hunt_targets = list(/obj/machinery/oven/range) - hunt_range = 9 - -/datum/ai_behavior/find_hunt_target/stove - -/datum/ai_behavior/find_hunt_target/stove/valid_dinner(mob/living/source, obj/machinery/oven/range/stove, radius) - if(!length(stove.used_tray?.contents) || stove.open) - return FALSE - //something in there is still baking... - for(var/atom/baking in stove.used_tray) - if(HAS_TRAIT(baking, TRAIT_BAKEABLE)) - return FALSE - return TRUE - -/datum/ai_behavior/hunt_target/interact_with_target/stove_target - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/interact_with_target/stove_target/target_caught(mob/living/hunter, obj/machinery/oven/range/stove) - if(stove.open) - return - return ..() - -/datum/ai_controller/basic_controller/cat/cake - planning_subtrees = list( - /datum/ai_planning_subtree/find_and_hunt_target/turn_off_stove, - /datum/ai_planning_subtree/find_and_hunt_target/decorate_donuts, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_mice, - /datum/ai_planning_subtree/find_and_hunt_target/find_cat_food, - /datum/ai_planning_subtree/haul_food_to_young, - /datum/ai_planning_subtree/random_speech/cats, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/decorate_donuts - target_key = BB_DONUT_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/decorate_donuts - finding_behavior = /datum/ai_behavior/find_hunt_target/decorate_donuts - hunt_targets = list(/obj/item/food/donut) - hunt_range = 9 - -/datum/ai_behavior/find_hunt_target/decorate_donuts/valid_dinner(mob/living/source, obj/item/food/donut/target, radius) - if(!target.is_decorated) - return FALSE - return can_see(source, target, radius) - -/datum/ai_behavior/hunt_target/decorate_donuts - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/decorate_donuts/target_caught(mob/living/hunter, atom/target) - hunter.spin(spintime = 4, speed = 1) diff --git a/code/modules/mob/living/basic/pets/cat/cat.bt.json b/code/modules/mob/living/basic/pets/cat/cat.bt.json new file mode 100644 index 000000000000..07bd81e876df --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat.bt.json @@ -0,0 +1,283 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cat", + "type": "selector", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_FLEE_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/true_for_time", + "vars": { + "duration": "3 SECONDS", + "succeed_on_timeout": true + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_BASIC_MOB_FLEE_TARGET" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_reside_in_home" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_haul_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_hunt_mice" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_TRESSPASSER_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TRESSPASSER_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/territorial_struggle", + "vars": { + "target_key": "BB_TRESSPASSER_TARGET", + "cries_key": "BB_HOSTILE_MEOWS" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "invert": true, + "key": "BB_MOUSE_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/cats" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CAT_HOME", + "target_source": "/datum/target_source/oview_single_type/cat_house", + "targeting_strategy": "/datum/targeting_strategy/valid_cat_home" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_cat_tresspasser", + "vars": { + "target_key": "BB_TRESSPASSER_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOUSE_TARGET", + "target_source": "/datum/target_source/oview_single_type/mouse", + "targeting_strategy": "/datum/targeting_strategy/huntable_mouse", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/cat_food", + "vision_range": 9 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm index 2589648ac8f4..03e9424d824f 100644 --- a/code/modules/mob/living/basic/pets/cat/cat.dm +++ b/code/modules/mob/living/basic/pets/cat/cat.dm @@ -99,7 +99,7 @@ AddElement(/datum/element/can_be_held) add_cell_sample() add_verb(src, /mob/living/proc/toggle_resting) - add_traits(list(TRAIT_CATLIKE_GRACE, TRAIT_VENTCRAWLER_ALWAYS, TRAIT_WOUND_LICKER, TRAIT_COLORBLIND), INNATE_TRAIT) + add_traits(list(TRAIT_CATLIKE_INSTINCT, TRAIT_VENTCRAWLER_ALWAYS, TRAIT_WOUND_LICKER, TRAIT_COLORBLIND), INNATE_TRAIT) ai_controller.set_blackboard_key(BB_HUNTABLE_PREY, typecacheof(huntable_items)) if(can_breed) add_breeding_component() diff --git a/code/modules/mob/living/basic/pets/cat/cat_ai.dm b/code/modules/mob/living/basic/pets/cat/cat_ai.dm index 1150d17f96da..faf465f186a6 100644 --- a/code/modules/mob/living/basic/pets/cat/cat_ai.dm +++ b/code/modules/mob/living/basic/pets/cat/cat_ai.dm @@ -5,288 +5,24 @@ BB_HOSTILE_MEOWS = list("Mawwww", "Mrewwww", "mhhhhng..."), BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/pet/cat), BB_BABIES_CHILD_TYPES = list(/mob/living/basic/pet/cat/kitten), + BB_FUCKS = TRUE, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/reside_in_home, - /datum/ai_planning_subtree/flee_target/from_flee_key/cat_struggle, - /datum/ai_planning_subtree/find_and_hunt_target/hunt_mice, - /datum/ai_planning_subtree/find_and_hunt_target/find_cat_food, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/haul_food_to_young, - /datum/ai_planning_subtree/territorial_struggle, - /datum/ai_planning_subtree/make_babies, - /datum/ai_planning_subtree/random_speech/cats, - ) - -/datum/ai_planning_subtree/reside_in_home - ///chance we enter our home - var/reside_chance = 5 - ///chance we leave our home - var/leave_home_chance = 15 - -/datum/ai_planning_subtree/reside_in_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - if(controller.blackboard_key_exists(BB_CAT_HOME)) - controller.queue_behavior(/datum/ai_behavior/enter_cat_home, BB_CAT_HOME) - return - - if(istype(living_pawn.loc, /obj/structure/cat_house)) - if(SPT_PROB(leave_home_chance, seconds_per_tick)) - controller.set_blackboard_key(BB_CAT_HOME, living_pawn.loc) - return SUBTREE_RETURN_FINISH_PLANNING - - if(SPT_PROB(reside_chance, seconds_per_tick)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_home, BB_CAT_HOME, /obj/structure/cat_house) - -/datum/ai_behavior/find_and_set/valid_home/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/obj/structure/cat_house/home in oview(search_range, controller.pawn)) - if(home.resident_cat) - continue - return home - - return null - -/datum/ai_behavior/enter_cat_home - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/enter_cat_home/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/enter_cat_home/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/obj/structure/cat_house/home = controller.blackboard[target_key] - var/mob/living/basic/living_pawn = controller.pawn - if(living_pawn == home.resident_cat || isnull(home.resident_cat)) - controller.ai_interact(target = home) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/enter_cat_home/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_planning_subtree/flee_target/from_flee_key/cat_struggle - flee_behaviour = /datum/ai_behavior/run_away_from_target/cat_struggle - -/datum/ai_behavior/run_away_from_target/cat_struggle - clear_failed_targets = TRUE - -/datum/ai_planning_subtree/territorial_struggle - ///chance we become hostile to another cat - var/hostility_chance = 5 - -/datum/ai_planning_subtree/territorial_struggle/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.gender != MALE || !SPT_PROB(hostility_chance, seconds_per_tick)) - return - if(controller.blackboard_key_exists(BB_TRESSPASSER_TARGET)) - controller.queue_behavior(/datum/ai_behavior/territorial_struggle, BB_TRESSPASSER_TARGET, BB_HOSTILE_MEOWS) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/cat_tresspasser, BB_TRESSPASSER_TARGET, /mob/living/basic/pet/cat) - -/datum/ai_behavior/find_and_set/cat_tresspasser/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/ignore_types = controller.blackboard[BB_BABIES_CHILD_TYPES] - for(var/mob/living/basic/pet/cat/potential_enemy in oview(search_range, controller.pawn)) - if(potential_enemy.gender != MALE) - continue - if(is_type_in_list(potential_enemy, ignore_types)) - continue - var/datum/ai_controller/basic_controller/enemy_controller = potential_enemy.ai_controller - if(isnull(enemy_controller)) - continue - //theyre already engaged in a battle, leave them alone! - if(enemy_controller.blackboard_key_exists(BB_TRESSPASSER_TARGET)) - continue - //u choose me and i choose u - enemy_controller.set_blackboard_key(BB_TRESSPASSER_TARGET, controller.pawn) - return potential_enemy - return null - -/datum/ai_behavior/territorial_struggle - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 5 SECONDS - ///chance the battle ends! - var/end_battle_chance = 25 - -/datum/ai_behavior/territorial_struggle/setup(datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/living_pawn = controller.pawn - var/mob/living/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - if(target.ai_controller?.blackboard[target_key] != living_pawn) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/territorial_struggle/perform(seconds_per_tick, datum/ai_controller/controller, target_key, cries_key) - . = ..() - var/mob/living/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - var/mob/living/living_pawn = controller.pawn - var/list/threaten_list = controller.blackboard[cries_key] - if(length(threaten_list)) - living_pawn.say(pick(threaten_list), forced = "ai_controller") - - if(!prob(end_battle_chance)) - return - - //50 50 chance we lose - var/datum/ai_controller/loser_controller = prob(50) ? controller : target.ai_controller - - loser_controller.set_blackboard_key(BB_BASIC_MOB_FLEE_TARGET, target) - target.ai_controller.clear_blackboard_key(BB_TRESSPASSER_TARGET) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/territorial_struggle/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_planning_subtree/find_and_hunt_target/hunt_mice - target_key = BB_MOUSE_TARGET - hunting_behavior = /datum/ai_behavior/play_with_mouse - finding_behavior = /datum/ai_behavior/find_hunt_target/hunt_mice - hunt_targets = list(/mob/living/basic/mouse) - hunt_chance = 75 - hunt_range = 9 - -/datum/ai_planning_subtree/find_and_hunt_target/hunt_mice/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/list/items_we_carry = typecache_filter_list(living_pawn, controller.blackboard[BB_HUNTABLE_PREY]) - if(length(items_we_carry)) - return - return ..() - - -/datum/ai_behavior/find_hunt_target/hunt_mice/valid_dinner(mob/living/source, mob/living/mouse, radius) - if(mouse.stat == DEAD || mouse.mind) - return FALSE - return can_see(source, mouse, radius) - -//play as in kill -/datum/ai_behavior/play_with_mouse - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 10 SECONDS - ///chance we hunt the mouse! - var/consume_chance = 70 - -/datum/ai_behavior/play_with_mouse/setup(datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/play_with_mouse/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/basic/mouse/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - consume_chance = istype(target, /mob/living/basic/mouse/brown/tom) ? 5 : initial(consume_chance) - if(prob(consume_chance)) - target.splat() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/play_with_mouse/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - var/mob/living/living_pawn = controller.pawn - var/atom/target = controller.blackboard[target_key] - controller.clear_blackboard_key(target_key) - if(isnull(target) || QDELETED(living_pawn)) - return - var/manual_emote = "attempts to hunt [target]..." - var/end_result = success ? "and succeeds!" : "but fails!" - manual_emote += end_result - living_pawn.manual_emote(manual_emote) - -/datum/ai_planning_subtree/find_and_hunt_target/find_cat_food - target_key = BB_CAT_FOOD_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/find_cat_food - finding_behavior = /datum/ai_behavior/find_hunt_target/find_cat_food - hunt_targets = list(/obj/item/fish, /obj/item/food/deadmouse, /obj/item/food/fishmeat) - hunt_chance = 75 - hunt_range = 9 - -/datum/ai_behavior/hunt_target/interact_with_target/find_cat_food - always_reset_target = TRUE - -/datum/ai_behavior/find_hunt_target/find_cat_food/valid_dinner(mob/living/source, atom/dinner, radius) - //this food is already near a kitten, let the kitten eat it - var/mob/living/nearby_kitten = locate(/mob/living/basic/pet/cat/kitten) in oview(2, dinner) - if(nearby_kitten && nearby_kitten != source) - return FALSE - return can_see(source, dinner, radius) - -/datum/ai_planning_subtree/haul_food_to_young/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_FOOD_TO_DELIVER)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_hands/given_list, BB_FOOD_TO_DELIVER, controller.blackboard[BB_HUNTABLE_PREY]) - return - if(!controller.blackboard_key_exists(BB_KITTEN_TO_FEED)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_kitten, BB_KITTEN_TO_FEED, /mob/living/basic/pet/cat/kitten) - return - - controller.queue_behavior(/datum/ai_behavior/deliver_food_to_kitten, BB_KITTEN_TO_FEED, BB_FOOD_TO_DELIVER) - -/datum/ai_behavior/find_and_set/valid_kitten - -/datum/ai_behavior/find_and_set/valid_kitten/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/kitten = locate(locate_path) in oview(search_range, controller.pawn) - //kitten already has food near it, go feed another hungry kitten - - if(isnull(kitten)) - return null - - var/list/nearby_food = typecache_filter_list(oview(2, kitten), controller.blackboard[BB_HUNTABLE_PREY]) - if(kitten.stat != DEAD && !length(nearby_food)) - return kitten - return null - -/datum/ai_behavior/deliver_food_to_kitten - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 5 SECONDS - -/datum/ai_behavior/deliver_food_to_kitten/setup(datum/ai_controller/controller, target_key, food_key) - . = ..() - var/mob/living/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/deliver_food_to_kitten/perform(seconds_per_tick, datum/ai_controller/controller, target_key, food_key) - var/mob/living/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/mob/living/living_pawn = controller.pawn - var/atom/movable/food = controller.blackboard[food_key] - - if(isnull(food) || !(food in living_pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - food.forceMove(get_turf(living_pawn)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/deliver_food_to_kitten/finish_action(datum/ai_controller/controller, success, target_key, food_key) - . = ..() - controller.clear_blackboard_key(target_key) - controller.clear_blackboard_key(food_key) + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat.bt.json" +/datum/ai_controller/basic_controller/cat/kitten + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_HUNGRY_MEOW = list("mrrp...", "mraw..."), + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + BB_MAX_DISTANCE_TO_FOOD = 2, + ) + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/kitten.bt.json" +/datum/ai_controller/basic_controller/cat/bread + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_bread.bt.json" +/datum/ai_controller/basic_controller/cat/cake + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_cake.bt.json" diff --git a/code/modules/mob/living/basic/pets/cat/cat_bread.bt.json b/code/modules/mob/living/basic/pets/cat/cat_bread.bt.json new file mode 100644 index 000000000000..ca64dbb37b11 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_bread.bt.json @@ -0,0 +1,140 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cat/bread", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_turn_off_stove" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_haul_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_hunt_mice" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/cats" + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOUSE_TARGET", + "target_source": "/datum/target_source/oview_single_type/mouse", + "targeting_strategy": "/datum/targeting_strategy/huntable_mouse", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/cat_food", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_STOVE_TARGET", + "target_source": "/datum/target_source/oview_single_type/oven", + "targeting_strategy": "/datum/targeting_strategy/finished_stove", + "vision_range": 9 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOOD_TO_DELIVER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOOD_TO_DELIVER", + "target_source": "/datum/target_source/carried_huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOOD_TO_DELIVER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOOD_TO_DELIVER", + "target_source": "/datum/target_source/carried_huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_bt.dm b/code/modules/mob/living/basic/pets/cat/cat_bt.dm new file mode 100644 index 000000000000..c62b755d6f29 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_bt.dm @@ -0,0 +1,259 @@ +// Behavior tree behaviors, subtrees and decorators for the cat AI family. + +/// Passes when the cat is currently carrying a piece of huntable food. +/datum/bt_node/decorator/cat_holding_food + +/datum/bt_node/decorator/cat_holding_food/check_condition(datum/ai_controller/controller) + var/mob/living/basic/pet/cat/cat = controller.pawn + return istype(cat) && !isnull(cat.held_food) + +/// Pounces on a mouse, occasionally killing it. Movement is handled externally. +/datum/bt_node/ai_behavior/play_with_mouse + time_between_perform = 10 SECONDS + /// Blackboard key holding the mouse we are hunting. + var/target_key + /// Chance we actually splat the mouse. + var/consume_chance = 70 + +/datum/bt_node/ai_behavior/play_with_mouse/setup(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/play_with_mouse/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/mouse/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + var/chance = istype(target, /mob/living/basic/mouse/brown/tom) ? 5 : consume_chance + if(prob(chance)) + target.splat() + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/datum/bt_node/ai_behavior/play_with_mouse/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + var/mob/living/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + controller.clear_blackboard_key(target_key) + if(isnull(target) || QDELETED(living_pawn)) + return + var/manual_emote = "attempts to hunt [target]..." + manual_emote += succeeded ? "and succeeds!" : "but fails!" + living_pawn.manual_emote(manual_emote) + +/// Drops a piece of carried food at a kitten's feet. +/datum/bt_node/ai_behavior/deliver_food_to_kitten + time_between_perform = 5 SECONDS + /// Blackboard key holding the kitten to feed. + var/target_key + /// Blackboard key holding the food we are carrying. + var/food_key + +/datum/bt_node/ai_behavior/deliver_food_to_kitten/setup(datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/deliver_food_to_kitten/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/living_pawn = controller.pawn + var/atom/movable/food = controller.blackboard[food_key] + if(isnull(food) || !(food in living_pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + food.forceMove(get_turf(living_pawn)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/deliver_food_to_kitten/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + controller.clear_blackboard_key(food_key) + +/// Engages a rival tom in a territorial yowling contest until one of them backs down +/datum/bt_node/ai_behavior/territorial_struggle + time_between_perform = 5 SECONDS + /// Blackboard key holding our rival. + var/target_key + /// Blackboard key holding the list of threatening cries. + var/cries_key + /// Chance the battle ends on each perform. + var/end_battle_chance = 25 + +/datum/bt_node/ai_behavior/territorial_struggle/setup(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + // Only fight if our rival is still locked onto us. + if(target.ai_controller?.blackboard[target_key] != living_pawn) + return FALSE + return TRUE + +/datum/bt_node/ai_behavior/territorial_struggle/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + var/mob/living/living_pawn = controller.pawn + var/list/threaten_list = controller.blackboard[cries_key] + if(length(threaten_list)) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), pick(threaten_list), forced = "ai_controller") + + if(!prob(end_battle_chance)) + return AI_BEHAVIOR_DELAY + + // 50/50 chance we lose. + var/datum/ai_controller/loser_controller = prob(50) ? controller : target.ai_controller + loser_controller.set_blackboard_key(BB_BASIC_MOB_FLEE_TARGET, target) + target.ai_controller?.clear_blackboard_key(target_key) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/territorial_struggle/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + +/// Finds a rival male cat to fight, marking ourselves as their target too so the fight is mutual. +/datum/bt_node/ai_behavior/find_cat_tresspasser + time_between_perform = 5 SECONDS + /// Blackboard key to store the rival in. + var/target_key + /// How far to look for a rival. + var/search_range = 9 + +/datum/bt_node/ai_behavior/find_cat_tresspasser/setup(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + return living_pawn.gender == MALE + +/datum/bt_node/ai_behavior/find_cat_tresspasser/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/ignore_types = controller.blackboard[BB_BABIES_CHILD_TYPES] + for(var/mob/living/basic/pet/cat/potential_enemy in oview(search_range, living_pawn)) + if(potential_enemy.gender != MALE) + continue + if(is_type_in_list(potential_enemy, ignore_types)) + continue + var/datum/ai_controller/enemy_controller = potential_enemy.ai_controller + if(isnull(enemy_controller)) + continue + // They're already engaged in a battle, leave them alone! + if(enemy_controller.blackboard_key_exists(BB_TRESSPASSER_TARGET)) + continue + // You choose me and I choose you. + enemy_controller.set_blackboard_key(BB_TRESSPASSER_TARGET, living_pawn) + controller.set_blackboard_key(target_key, potential_enemy) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/// Points at a target and meows for food. Used to beg humans and call kittens to dinner. +/datum/bt_node/ai_behavior/beacon_for_food + time_between_perform = 5 SECONDS + /// Blackboard key holding the atom we are pointing at. + var/target_key + /// Blackboard key holding the list of hungry meows. + var/meows_key + +/datum/bt_node/ai_behavior/beacon_for_food/setup(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + return !QDELETED(target) + +/datum/bt_node/ai_behavior/beacon_for_food/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/living_pawn = controller.pawn + var/list/meowing_list = controller.blackboard[meows_key] + if(length(meowing_list)) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), pick(meowing_list), forced = "ai_controller") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob/living, _pointed), target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/beacon_for_food/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + +/// Finds a conscious human carrying food worth begging for. +/datum/bt_node/ai_behavior/find_human_to_beg + /// Blackboard key to store the human in. + var/target_key + /// How far to look for a human. + var/search_range = 9 + +/datum/bt_node/ai_behavior/find_human_to_beg/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/locate_items = controller.blackboard[BB_HUNTABLE_PREY] + for(var/mob/living/carbon/human/human_target in oview(search_range, living_pawn)) + if(IS_UNCONSCIOUS_OR_CRIT(human_target) || isnull(human_target.mind)) + continue + for(var/obj/item/held_item in human_target.held_items) + if(is_type_in_typecache(held_item, locate_items)) + controller.set_blackboard_key(target_key, human_target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/// Leaves the cat house we are currently residing in, occasionally. +/datum/bt_node/ai_behavior/leave_cat_home + /// Chance per attempt that we decide to leave. + var/leave_home_chance = 15 + +/datum/bt_node/ai_behavior/leave_cat_home/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/structure/cat_house/home = controller.pawn.loc + if(!istype(home)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!prob(leave_home_chance)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), home, FALSE) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Celebrates around a decorated donut with a spin. +/datum/bt_node/ai_behavior/hunt_target/decorate_donuts + always_reset_target = TRUE + +/datum/bt_node/ai_behavior/hunt_target/decorate_donuts/target_caught(mob/living/hunter, atom/hunted) + hunter.spin(spintime = 4, speed = 1) + +/// Enters (or exits if already resident) a cat house keyed in target_key. +/datum/bt_node/ai_behavior/enter_cat_home + var/target_key + +/datum/bt_node/ai_behavior/enter_cat_home/setup(datum/ai_controller/controller) + var/obj/structure/cat_house/home = controller.blackboard[target_key] + return !QDELETED(home) + +/datum/bt_node/ai_behavior/enter_cat_home/perform(seconds_per_tick, datum/ai_controller/controller) + var/obj/structure/cat_house/home = controller.blackboard[target_key] + if(QDELETED(home)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/basic/pet/cat/living_pawn = controller.pawn + if(living_pawn == home.resident_cat || isnull(home.resident_cat)) + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), home, FALSE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + +/datum/bt_node/ai_behavior/enter_cat_home/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.clear_blackboard_key(target_key) + +// Subtree types the trees themselves live in the matching .bt.json files. + +/// Block other behaviors while residing in a cat house; occasionally leave. +/datum/bt_node/subtree/cat_reside_in_home + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.json" + +/// Find a mouse and pounce on it. +/datum/bt_node/subtree/cat_hunt_mice + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.json" + +/// Find dropped food on the ground and eat it. +/datum/bt_node/subtree/cat_find_food + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_find_food.bt.json" + +/// Carry food we are holding to a hungry kitten. +/datum/bt_node/subtree/cat_haul_food + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.json" + +/// Turn off a finished oven. +/datum/bt_node/subtree/cat_turn_off_stove + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.json" + +/// Spin to decorate nearby donuts. +/datum/bt_node/subtree/cat_decorate_donuts + behavior_tree_json = "code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.json" diff --git a/code/modules/mob/living/basic/pets/cat/cat_cake.bt.json b/code/modules/mob/living/basic/pets/cat/cat_cake.bt.json new file mode 100644 index 000000000000..a9abb2507d95 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_cake.bt.json @@ -0,0 +1,154 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cat/cake", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_turn_off_stove" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_decorate_donuts" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_haul_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_hunt_mice" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/cat_find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/cats" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MOUSE_TARGET", + "target_source": "/datum/target_source/oview_single_type/mouse", + "targeting_strategy": "/datum/targeting_strategy/huntable_mouse", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/cat_food", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_STOVE_TARGET", + "target_source": "/datum/target_source/oview_single_type/oven", + "targeting_strategy": "/datum/targeting_strategy/finished_stove", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DONUT_TARGET", + "target_source": "/datum/target_source/oview_single_type/donut", + "targeting_strategy": "/datum/targeting_strategy/decorated_donut", + "vision_range": 9 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOOD_TO_DELIVER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOOD_TO_DELIVER", + "target_source": "/datum/target_source/carried_huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOOD_TO_DELIVER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOOD_TO_DELIVER", + "target_source": "/datum/target_source/carried_huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.json b/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.json new file mode 100644 index 000000000000..70bab944828c --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_decorate_donuts.bt.json @@ -0,0 +1,29 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_decorate_donuts", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_DONUT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DONUT_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/decorate_donuts", + "vars": { + "target_key": "BB_DONUT_TARGET" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.json b/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.json new file mode 100644 index 000000000000..7d5201039ad3 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_find_food.bt.json @@ -0,0 +1,30 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_find_food", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CAT_FOOD_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "always_reset_target": true + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.json b/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.json new file mode 100644 index 000000000000..cbe66fcacf60 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_haul_food.bt.json @@ -0,0 +1,41 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_haul_food", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cat_holding_food", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_KITTEN_TO_FEED", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOOD_TO_DELIVER" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_KITTEN_TO_FEED", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/deliver_food_to_kitten", + "vars": { + "target_key": "BB_KITTEN_TO_FEED", + "food_key": "BB_FOOD_TO_DELIVER" + } + } + ] + } + } + } +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.json b/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.json new file mode 100644 index 000000000000..1235d8b981d1 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_hunt_mice.bt.json @@ -0,0 +1,36 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_hunt_mice", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cat_holding_food", + "vars": { + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MOUSE_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_MOUSE_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/play_with_mouse", + "vars": { + "target_key": "BB_MOUSE_TARGET" + } + } + ] + } + } +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.json b/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.json new file mode 100644 index 000000000000..efd2ef664add --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_reside_in_home.bt.json @@ -0,0 +1,55 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_reside_in_home", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_loc_is_type", + "vars": { + "loc_type": "/obj/structure/cat_house", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/leave_cat_home" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CAT_HOME", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CAT_HOME", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/enter_cat_home", + "vars": { + "target_key": "BB_CAT_HOME" + } + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.json b/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.json new file mode 100644 index 000000000000..b7f84d677d96 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/cat_turn_off_stove.bt.json @@ -0,0 +1,30 @@ +{ + "dm_type": "/datum/bt_node/subtree/cat_turn_off_stove", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_STOVE_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_STOVE_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_STOVE_TARGET", + "always_reset_target": true + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/pets/cat/kitten.bt.json b/code/modules/mob/living/basic/pets/cat/kitten.bt.json new file mode 100644 index 000000000000..7809ec2efff8 --- /dev/null +++ b/code/modules/mob/living/basic/pets/cat/kitten.bt.json @@ -0,0 +1,151 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cat/kitten", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HUMAN_BEG_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/beacon_for_food", + "vars": { + "target_key": "BB_HUMAN_BEG_TARGET", + "meows_key": "BB_HUNGRY_MEOW" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CAT_FOOD_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_farther_than_from_key", + "vars": { + "anchor_key": "BB_CAT_FOOD_TARGET", + "distance_key": "BB_MAX_DISTANCE_TO_FOOD" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/beacon_for_food", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "meows_key": "BB_HUNGRY_MEOW" + } + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "always_reset_target": true + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/cats" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CAT_FOOD_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/huntable_prey", + "targeting_strategy": "/datum/targeting_strategy/cat_food", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_human_to_beg", + "vars": { + "target_key": "BB_HUMAN_BEG_TARGET" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm b/code/modules/mob/living/basic/pets/cat/kitten_ai.dm deleted file mode 100644 index 952c7aabd68c..000000000000 --- a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm +++ /dev/null @@ -1,67 +0,0 @@ - -/datum/ai_controller/basic_controller/cat/kitten - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_HUNGRY_MEOW = list("mrrp...", "mraw..."), - BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, - BB_MAX_DISTANCE_TO_FOOD = 2, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/beg_human, - /datum/ai_planning_subtree/find_and_hunt_target/find_cat_food/kitten, - /datum/ai_planning_subtree/random_speech/cats, - ) - -//if the food is too far away, point at it or meow. if its near us then go eat it - -/datum/ai_planning_subtree/find_and_hunt_target/find_cat_food/kitten - - -/datum/ai_planning_subtree/find_and_hunt_target/find_cat_food/kitten/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/target = controller.blackboard[BB_CAT_FOOD_TARGET] - if(target && get_dist(target, controller.pawn) > controller.blackboard[BB_MAX_DISTANCE_TO_FOOD]) - controller.queue_behavior(/datum/ai_behavior/beacon_for_food, BB_CAT_FOOD_TARGET, BB_HUNGRY_MEOW) - return - return ..() - -/datum/ai_behavior/beacon_for_food - action_cooldown = 5 SECONDS - -/datum/ai_behavior/beacon_for_food/perform(seconds_per_tick, datum/ai_controller/controller, target_key, meows_key) - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/mob/living/living_pawn = controller.pawn - var/list/meowing_list = controller.blackboard[meows_key] - if(length(meowing_list)) - living_pawn.say(pick(meowing_list), forced = "ai_controller") - living_pawn._pointed(target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/beacon_for_food/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_planning_subtree/beg_human - -/datum/ai_planning_subtree/beg_human/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - if(controller.blackboard_key_exists(BB_HUMAN_BEG_TARGET)) - controller.queue_behavior(/datum/ai_behavior/beacon_for_food, BB_HUMAN_BEG_TARGET, BB_HUNGRY_MEOW) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/human_beg, BB_HUMAN_BEG_TARGET, /mob/living/carbon/human) - -/datum/ai_behavior/find_and_set/human_beg/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/locate_items = controller.blackboard[BB_HUNTABLE_PREY] - for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn)) - if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) - continue - for (var/obj/item/held_item in human_target.held_items) - if (is_type_in_typecache(held_item, locate_items)) - return human_target - return null diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm index 4114699d342b..d2c48066b7b3 100644 --- a/code/modules/mob/living/basic/pets/dog/_dog.dm +++ b/code/modules/mob/living/basic/pets/dog/_dog.dm @@ -7,9 +7,9 @@ /datum/pet_command/good_boy/dog speech_commands = list("good dog") -// Set correct attack behaviour +// Use dog-specific melee attack (paws harmlessly when BB_DOG_HARASS_HARM is false) /datum/pet_command/attack/dog - attack_behaviour = /datum/ai_behavior/basic_melee_attack/dog + attack_subtree = /datum/bt_node/subtree/pet_command/attack/dog /datum/pet_command/attack/dog/set_command_active(mob/living/parent, mob/living/commander) . = ..() @@ -67,29 +67,11 @@ bloodpool = 5 maxbloodpool = 5 - var/can_hold_item = TRUE // for gathering food - /mob/living/basic/pet/dog/update_resting() . = ..() if(stat == DEAD) return update_appearance(UPDATE_ICON_STATE) - - -/datum/ai_planning_subtree/find_and_hunt_target/find_dog_food - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/find_dog_food - hunt_targets = list( - /obj/item/food/deadmouse, - /obj/item/food/meat/slab, - /obj/item/food/meat/rawbacon, - /obj/item/food/meat/bacon, - /obj/item/food/meat/rawcutlet, - ) - hunt_chance = 75 - hunt_range = 18 - -/datum/ai_behavior/hunt_target/interact_with_target/find_dog_food - always_reset_target = TRUE // DARKPACK EDIT ADD END /datum/emote/dog @@ -116,21 +98,22 @@ var/static/list/food_types = list( /obj/item/food/meat/slab/human/mutant/skeleton, /obj/item/stack/sheet/bone, -// DARKPACK EDIT ADD START - NPC + // DARKPACK EDIT ADD START - NPC /obj/item/food/meat/slab, /obj/item/food/meat/rawbacon, /obj/item/food/meat/bacon, /obj/item/food/meat/rawcutlet, -// DARKPACK EDIT ADD END + // DARKPACK EDIT ADD END ) AddElement(/datum/element/ai_flee_while_injured) // DARKPACK EDIT ADD - NPC AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 30, bonus_tame_chance = 15, unique = FALSE) -// AddComponent(/datum/component/obeys_commands, pet_commands) // DARKPACK EDIT REMOVAL - npc + // AddComponent(/datum/component/obeys_commands, pet_commands) // DARKPACK EDIT REMOVAL - NPC add_obey_commands() var/dog_area = get_area(src) for(var/obj/structure/bed/dogbed/dog_bed in dog_area) if(dog_bed.update_owner(src)) //No muscling in on my turf you fucking parrot break + ai_controller.set_blackboard_key(BB_HUNT_TARGET_LIST, typecacheof(food_types)) // DARKPACK EDIT ADD // DARKPACK EDIT ADD START - NPC - (snowflake obeys_commands abstraction proc) /mob/living/basic/pet/dog/proc/add_obey_commands() @@ -150,10 +133,21 @@ // DARKPACK EDIT ADD END - npc ///Updates dog speech and emotes -/mob/living/basic/pet/dog/proc/update_dog_speech(datum/ai_planning_subtree/random_speech/speech) - speech.speak = string_list(list("YAP", "Woof!", "Bark!", "AUUUUUU")) - speech.emote_hear = string_list(list("barks!", "woofs!", "yaps.","pants.")) - speech.emote_see = string_list(list("shakes [p_their()] head.", "chases [p_their()] tail.","shivers.")) +/mob/living/basic/pet/dog/proc/update_dog_speech(list/speech_data) + speech_data[BB_EMOTE_SAY] = string_list(list("YAP", "Woof!", "Bark!", "AUUUUUU")) + speech_data[BB_EMOTE_HEAR] = string_list(list("barks!", "woofs!", "yaps.","pants.")) + speech_data[BB_EMOTE_SEE] = string_list(list("shakes [p_their()] head.", "chases [p_their()] tail.","shivers.")) + + +/// Populates BB_BASIC_MOB_SPEAK_LINES with the dog's current speech data for BT random speech. +/// Subtypes override this to apply fashion accessories or variant speech. +/mob/living/basic/pet/dog/proc/update_dog_speak_blackboard(datum/ai_controller/controller) + var/list/speech_data = list() + speech_data[BB_EMOTE_SAY] = list("YAP", "Woof!", "Bark!", "AUUUUUU") + speech_data[BB_EMOTE_HEAR] = list("barks!", "woofs!", "yaps.", "pants.") + speech_data[BB_EMOTE_SEE] = list("shakes [p_their()] head.", "chases [p_their()] tail.", "shivers.") + speech_data[BB_SPEAK_CHANCE] = 1 + controller.override_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, speech_data) ///Proc to run on a successful taming attempt /mob/living/basic/pet/dog/tamed(mob/living/tamer, atom/food) @@ -177,7 +171,7 @@ if (!isdog(target) || user.combat_mode) return ..() var/mob/living/basic/pet/dog/dog_target = target - if (dog_target.stat != CONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(dog_target)) return ..() dog_target.emote("spin") dog_target.fully_heal() diff --git a/code/modules/mob/living/basic/pets/dog/corgi.dm b/code/modules/mob/living/basic/pets/dog/corgi.dm index 9a6dd92c1738..7ed2978b95ae 100644 --- a/code/modules/mob/living/basic/pets/dog/corgi.dm +++ b/code/modules/mob/living/basic/pets/dog/corgi.dm @@ -113,39 +113,55 @@ armorval += inventory_back.get_armor_rating(type) return armorval * 0.5 -/mob/living/basic/pet/dog/corgi/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(attacking_item, /obj/item/razor)) - if(shaved) - to_chat(user, span_warning("You can't shave this corgi, [p_they()] has already been shaved!")) - return - if(!can_be_shaved) - to_chat(user, span_warning("You can't shave this corgi, [p_they()] [p_do()]n't have a fur coat!")) - return - user.visible_message(span_notice("[user] starts to shave [src] using \the [attacking_item]."), span_notice("You start to shave [src] using \the [attacking_item]...")) - if(do_after(user, 5 SECONDS, target = src)) - user.visible_message(span_notice("[user] shaves [src]'s hair using \the [attacking_item].")) - playsound(get_turf(src), 'sound/items/hair-clippers.ogg', 20, TRUE) - shaved = TRUE - icon_living = "[icon_living]_shaved" - icon_dead = "[icon_living]_shaved_dead" - if(stat == CONSCIOUS) - icon_state = icon_living - else - icon_state = icon_dead - return TRUE - - return ..() - -/mob/living/basic/pet/dog/corgi/update_dog_speech(datum/ai_planning_subtree/random_speech/speech) - . = ..() +/mob/living/basic/pet/dog/corgi/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/razor)) + return ..() + + if(shaved) + to_chat(user, span_warning("You can't shave this corgi, [p_they()] [p_have()] already been shaved!")) + return ITEM_INTERACT_BLOCKING + + if(!can_be_shaved) + to_chat(user, span_warning("You can't shave this corgi, [p_they()] [p_do()]n't have a fur coat!")) + return ITEM_INTERACT_BLOCKING + + user.visible_message( + span_notice("[user] starts to shave [src] using \the [tool]."), + span_notice("You start to shave [src] using \the [tool]..."), + span_hear("You hear electric buzzing."), + ) + + if(!do_after(user, 5 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] shaves [src]'s hair using \the [tool].")) + playsound(get_turf(src), 'sound/items/hair-clippers.ogg', 20, TRUE) + shaved = TRUE + icon_living = "[icon_living]_shaved" + icon_dead = "[icon_living]_shaved_dead" + if(!IS_UNCONSCIOUS_OR_CRIT(src)) + icon_state = icon_living + else + icon_state = icon_dead + return ITEM_INTERACT_SUCCESS + +/mob/living/basic/pet/dog/corgi/update_dog_speech(list/speech_data) + . = ..() if(inventory_head?.dog_fashion) var/datum/dog_fashion/equipped_head_fashion_item = new inventory_head.dog_fashion(src) - equipped_head_fashion_item.apply_to_speech(speech) + equipped_head_fashion_item.apply_to_speech(speech_data) if(inventory_back?.dog_fashion) var/datum/dog_fashion/equipped_back_fashion_item = new inventory_back.dog_fashion(src) - equipped_back_fashion_item.apply_to_speech(speech) + equipped_back_fashion_item.apply_to_speech(speech_data) + +/// Applies corgi fashion to the BT blackboard speech data by routing through the planning stub. +/mob/living/basic/pet/dog/corgi/update_dog_speak_blackboard(datum/ai_controller/controller) + var/list/speech_data = list() + update_dog_speech(speech_data) + if(!speech_data[BB_SPEAK_CHANCE]) + speech_data[BB_SPEAK_CHANCE] = 1 + controller.override_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, speech_data) /mob/living/basic/pet/dog/corgi/deadchat_plays(mode = ANARCHY_MODE, cooldown = 12 SECONDS) . = AddComponent(/datum/component/deadchat_control/cardinal_movement, mode, list( @@ -173,7 +189,7 @@ if(!equipped_head_fashion_item.obj_color) equipped_head_fashion_item.obj_color = inventory_head.color - if(stat == DEAD || HAS_TRAIT(src, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(src)) head_icon = equipped_head_fashion_item.get_overlay(dir = EAST) head_icon.pixel_z = -8 head_icon.transform = head_icon.transform.Turn(180) @@ -193,7 +209,7 @@ if(!equipped_back_fashion_item.obj_color) equipped_back_fashion_item.obj_color = inventory_back.color - if(stat == DEAD || HAS_TRAIT(src, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(src)) back_icon = equipped_back_fashion_item.get_overlay(dir = EAST) back_icon.pixel_z = -11 back_icon.transform = back_icon.transform.Turn(180) @@ -239,12 +255,15 @@ return FALSE if (user) - if(stat == DEAD || HAS_TRAIT(src, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(src)) to_chat(user, span_notice("There is merely a dull, lifeless look in [real_name]'s eyes as you put \the [item_to_add] on [p_them()].")) else - user.visible_message(span_notice("[user] puts [item_to_add] on [real_name]'s head. [src] looks at [user] and barks once."), + user.visible_message( + span_notice("[user] puts [item_to_add] on [real_name]'s head. [src] looks at [user] and barks once."), span_notice("You put [item_to_add] on [real_name]'s head. [src] gives you a peculiar look, then wags [p_their()] tail once and barks."), - span_hear("You hear a friendly-sounding bark.")) + span_hear("You hear a friendly-sounding bark."), + ) + item_to_add.forceMove(src) inventory_head = item_to_add update_corgi_fluff() @@ -463,7 +482,7 @@ ///Checks whether Ian has survived the round or not /mob/living/basic/pet/dog/corgi/ian/proc/check_ian_survival() - if(!stat && !memory_saved) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !memory_saved) Write_Memory(FALSE) //NARS-IAN! SQ-Q-QooEglor-r'EEn-nl-luEEEf-f-fth-h @@ -509,10 +528,10 @@ . = ..() speak_emote = list("growls", "barks ominously") -/mob/living/basic/pet/dog/corgi/narsie/update_dog_speech(datum/ai_planning_subtree/random_speech/speech) - speech.speak = string_list(list("Tari'karat-pasnar!", "IA! IA!", "BRRUUURGHGHRHR")) - speech.emote_hear = string_list(list("barks echoingly!", "woofs hauntingly!", "yaps in an eldritch manner.", "mutters something unspeakable.")) - speech.emote_see = string_list(list("communes with the unnameable.", "ponders devouring some souls.", "shakes.")) +/mob/living/basic/pet/dog/corgi/narsie/update_dog_speech(list/speech_data) + speech_data[BB_EMOTE_SAY] = string_list(list("Tari'karat-pasnar!", "IA! IA!", "BRRUUURGHGHRHR")) + speech_data[BB_EMOTE_HEAR] = string_list(list("barks echoingly!", "woofs hauntingly!", "yaps in an eldritch manner.", "mutters something unspeakable.")) + speech_data[BB_EMOTE_SEE] = string_list(list("communes with the unnameable.", "ponders devouring some souls.", "shakes.")) /mob/living/basic/pet/dog/corgi/narsie/narsie_act() if(stat == DEAD) //Nar'Sie loves her doggy diff --git a/code/modules/mob/living/basic/pets/dog/dog_subtypes.dm b/code/modules/mob/living/basic/pets/dog/dog_subtypes.dm index 3e2dc5e5649b..9d01a2f9da5e 100644 --- a/code/modules/mob/living/basic/pets/dog/dog_subtypes.dm +++ b/code/modules/mob/living/basic/pets/dog/dog_subtypes.dm @@ -64,6 +64,7 @@ ai_controller = /datum/ai_controller/basic_controller/guarddog /datum/ai_controller/basic_controller/guarddog + behavior_tree_json = "code/modules/mob/living/basic/pets/dog/guarddog.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -72,12 +73,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /mob/living/basic/pet/dog/breaddog //Most of the code originates from Cak name = "Kobun" @@ -121,7 +116,7 @@ . = ..() if(!.) //dead or deleted return - if(stat) // consciousness check + if(IS_UNCONSCIOUS_OR_CRIT(src)) // consciousness check return if(health < maxHealth) diff --git a/code/modules/mob/living/basic/pets/dog/guarddog.bt.json b/code/modules/mob/living/basic/pets/dog/guarddog.bt.json new file mode 100644 index 000000000000..588d575dd9ae --- /dev/null +++ b/code/modules/mob/living/basic/pets/dog/guarddog.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/guarddog", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" +} diff --git a/code/modules/mob/living/basic/pets/fox.bt.json b/code/modules/mob/living/basic/pets/fox.bt.json new file mode 100644 index 000000000000..8a9c017ee55b --- /dev/null +++ b/code/modules/mob/living/basic/pets/fox.bt.json @@ -0,0 +1,133 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fox", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_FLEE_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/fox" + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/no_humans_watching", + "vars": { + "polling_rate": "2 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/fox.dm b/code/modules/mob/living/basic/pets/fox.dm index e986d74259ba..1e982d473f27 100644 --- a/code/modules/mob/living/basic/pets/fox.dm +++ b/code/modules/mob/living/basic/pets/fox.dm @@ -58,6 +58,7 @@ AddElement(/datum/element/tiny_mob_hunter, MOB_SIZE_SMALL) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/can_be_held) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_FOX, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) /datum/ai_controller/basic_controller/fox blackboard = list( @@ -68,24 +69,11 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/simple_find_target/not_while_observed, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/fox, - ) + behavior_tree_json = "code/modules/mob/living/basic/pets/fox.bt.json" // An AI controller for more docile foxes. /datum/ai_controller/basic_controller/fox/docile - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/random_speech/fox, - ) + behavior_tree_json = "code/modules/mob/living/basic/pets/fox_docile.bt.json" // The captain's fox, Renault /mob/living/basic/pet/fox/renault diff --git a/code/modules/mob/living/basic/pets/fox_docile.bt.json b/code/modules/mob/living/basic/pets/fox_docile.bt.json new file mode 100644 index 000000000000..19f92dc23a37 --- /dev/null +++ b/code/modules/mob/living/basic/pets/fox_docile.bt.json @@ -0,0 +1,61 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fox/docile", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_FLEE_TARGET", + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/fox" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/gondolas/gondola.bt.json b/code/modules/mob/living/basic/pets/gondolas/gondola.bt.json new file mode 100644 index 000000000000..e1e36d0a3269 --- /dev/null +++ b/code/modules/mob/living/basic/pets/gondolas/gondola.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/gondola", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": "10" + } +} diff --git a/code/modules/mob/living/basic/pets/gondolas/gondola.dm b/code/modules/mob/living/basic/pets/gondolas/gondola.dm index 8e235dc96883..7810317381f1 100644 --- a/code/modules/mob/living/basic/pets/gondolas/gondola.dm +++ b/code/modules/mob/living/basic/pets/gondolas/gondola.dm @@ -69,13 +69,13 @@ add_overlay(moustache_overlay) /datum/ai_controller/basic_controller/gondola + behavior_tree_json = "code/modules/mob/living/basic/pets/gondolas/gondola.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking #undef GONDOLA_HEIGHT #undef GONDOLA_COLOR diff --git a/code/modules/mob/living/basic/pets/orbie/orbie.bt.json b/code/modules/mob/living/basic/pets/orbie/orbie.bt.json new file mode 100644 index 000000000000..1677fa9e4324 --- /dev/null +++ b/code/modules/mob/living/basic/pets/orbie/orbie.bt.json @@ -0,0 +1,100 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/orbie", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_NEARBY_PLAYMATE", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_NEARBY_PLAYMATE", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/interact_with_playmate", + "vars": { + "target_key": "BB_NEARBY_PLAYMATE" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_LAST_RECEIVED_MESSAGE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/relay_pda_message", + "vars": { + "target_key": "BB_LAST_RECEIVED_MESSAGE" + } + } + } + ] + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_NEXT_PLAYDATE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/find_playmate" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + } + ] +} diff --git a/code/modules/mob/living/basic/pets/orbie/orbie_ai.dm b/code/modules/mob/living/basic/pets/orbie/orbie_ai.dm index 69e8febf4b21..f020b44009c0 100644 --- a/code/modules/mob/living/basic/pets/orbie/orbie_ai.dm +++ b/code/modules/mob/living/basic/pets/orbie/orbie_ai.dm @@ -2,6 +2,7 @@ #define MESSAGE_EXPIRY_TIME (30 SECONDS) /datum/ai_controller/basic_controller/orbie + behavior_tree_json = "code/modules/mob/living/basic/pets/orbie/orbie.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -9,13 +10,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_playmates, - /datum/ai_planning_subtree/relay_pda_message, - /datum/ai_planning_subtree/pet_planning, - ) /datum/ai_controller/basic_controller/orbie/TryPossessPawn(atom/new_pawn) . = ..() @@ -28,42 +22,36 @@ addtimer(CALLBACK(src, PROC_REF(clear_blackboard_key), BB_LAST_RECEIVED_MESSAGE), MESSAGE_EXPIRY_TIME) -///ai behavior that lets us search for other orbies to play with -/datum/ai_planning_subtree/find_playmates - -/datum/ai_planning_subtree/find_playmates/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_NEXT_PLAYDATE] > world.time) - return - if(controller.blackboard_key_exists(BB_NEARBY_PLAYMATE)) - controller.queue_behavior(/datum/ai_behavior/interact_with_playmate, BB_NEARBY_PLAYMATE) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/find_playmate, BB_NEARBY_PLAYMATE, /mob/living/basic/orbie) +/// Finds a nearby free orbie to play with and mutually registers as playmates. +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/find_playmate + target_key = BB_NEARBY_PLAYMATE + target_source = /datum/target_source/oview_single_type/orbie + targeting_strategy = /datum/targeting_strategy/playmate -/datum/ai_behavior/find_and_set/find_playmate +/datum/bt_node/ai_behavior/acquire_target/update_interaction_target/find_playmate/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + var/mob/living/basic/orbie/playmate = target + playmate.ai_controller.set_blackboard_key(BB_NEARBY_PLAYMATE, controller.pawn) -/datum/ai_behavior/find_and_set/find_playmate/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/mob/living/basic/orbie/playmate in oview(search_range, controller.pawn)) - if(playmate == controller.pawn || playmate.stat == DEAD || isnull(playmate.ai_controller)) - continue - if(playmate.ai_controller.blackboard[BB_NEARBY_PLAYMATE] || playmate.ai_controller.blackboard[BB_NEXT_PLAYDATE] > world.time) //they already have a playmate... - continue - playmate.ai_controller.set_blackboard_key(BB_NEARBY_PLAYMATE, controller.pawn) - return playmate - return null +/// Accepts an orbie that is free to play (no current playmate and not on playdate cooldown). +/datum/targeting_strategy/playmate - -/datum/ai_behavior/interact_with_playmate - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/interact_with_playmate/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) +/datum/targeting_strategy/playmate/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + if(!istype(target, /mob/living/basic/orbie)) + return FALSE + var/mob/living/basic/orbie/orbie_target = target + if(orbie_target == living_mob || orbie_target.stat == DEAD || isnull(orbie_target.ai_controller)) return FALSE - set_movement_target(controller, target) + if(orbie_target.ai_controller.blackboard[BB_NEARBY_PLAYMATE]) + return FALSE + if(orbie_target.ai_controller.blackboard[BB_NEXT_PLAYDATE] > world.time) + return FALSE + return TRUE -/datum/ai_behavior/interact_with_playmate/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +///plays with a nearby orbie +/datum/bt_node/ai_behavior/interact_with_playmate + var/target_key = "BB_NEARBY_PLAYMATE" + +/datum/bt_node/ai_behavior/interact_with_playmate/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/basic/living_pawn = controller.pawn var/atom/target = controller.blackboard[target_key] @@ -72,38 +60,36 @@ living_pawn.manual_emote("plays with [target]!") living_pawn.spin(spintime = 4, speed = 1) - living_pawn.ClickOn(target) + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/mob, ClickOn), target) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/interact_with_playmate/finish_action(datum/ai_controller/controller, success, target_key) +/datum/bt_node/ai_behavior/interact_with_playmate/finish_action(datum/ai_controller/controller, success) . = ..() controller.clear_blackboard_key(target_key) controller.set_blackboard_key(BB_NEXT_PLAYDATE, world.time + PET_PLAYTIME_COOLDOWN) -/datum/ai_planning_subtree/relay_pda_message - -/datum/ai_planning_subtree/relay_pda_message/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 2 || isnull(controller.blackboard[BB_LAST_RECEIVED_MESSAGE])) - return +///relays a pda message if orbie is level 2+ +/datum/bt_node/ai_behavior/relay_pda_message + var/target_key = "BB_LAST_RECEIVED_MESSAGE" - controller.queue_behavior(/datum/ai_behavior/relay_pda_message, BB_LAST_RECEIVED_MESSAGE) +/datum/bt_node/ai_behavior/relay_pda_message/perform(seconds_per_tick, datum/ai_controller/controller) + if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 2) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_behavior/relay_pda_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key) var/mob/living/basic/living_pawn = controller.pawn var/text_to_say = controller.blackboard[target_key] if(isnull(text_to_say)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - living_pawn.say(text_to_say, forced = "AI controller") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), text_to_say, forced = "AI controller") living_pawn.spin(spintime = 4, speed = 1) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/relay_pda_message/finish_action(datum/ai_controller/controller, success, target_key) +/datum/bt_node/ai_behavior/relay_pda_message/finish_action(datum/ai_controller/controller, success) . = ..() controller.clear_blackboard_key(target_key) /datum/pet_command/follow/orbie - follow_behavior = /datum/ai_behavior/pet_follow_friend/orbie /datum/pet_command/follow/orbie/New(mob/living/parent) . = ..() @@ -113,9 +99,6 @@ SIGNAL_HANDLER set_command_active(source, friend) -/datum/ai_behavior/pet_follow_friend/orbie - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - ///command to make our pet turn its lights on, we need to be level 2 to activate this ability /datum/pet_command/untargeted_ability/pet_lights command_name = "Lights" @@ -128,7 +111,7 @@ /datum/pet_command/untargeted_ability/pet_lights/execute_action(datum/ai_controller/controller) if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 2) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + return TRUE return ..() /datum/pet_command/use_ability/pet_lights/retrieve_command_text(atom/living_pet, atom/target) @@ -151,7 +134,7 @@ /datum/pet_command/use_ability/take_photo/execute_action(datum/ai_controller/controller) if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 3) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + return TRUE return ..() /datum/pet_command/perform_trick_sequence @@ -176,7 +159,7 @@ for(var/index in 1 to length(trick_sequence)) addtimer(CALLBACK(living_pawn, TYPE_PROC_REF(/mob, emote), trick_sequence[index], index * 0.5 SECONDS)) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING + return TRUE #undef PET_PLAYTIME_COOLDOWN #undef MESSAGE_EXPIRY_TIME diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm index 424057b89a52..a05a52818488 100644 --- a/code/modules/mob/living/basic/pets/parrot/_parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm @@ -183,7 +183,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Proc that handles sending the signal and returning a valid phrase to say. Will not do anything if we don't have a stat or if we're cliented. /// Will return either a string or null. /mob/living/basic/parrot/proc/get_phrase() - if(!isnull(client) || stat != CONSCIOUS) + if(!isnull(client) || IS_UNCONSCIOUS_OR_CRIT(src)) return null if(!COOLDOWN_FINISHED(src, forced_speech_cooldown)) @@ -348,7 +348,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Handles special behavior whenever we are injured. /mob/living/basic/parrot/proc/on_injured(mob/living/basic/source, mob/living/attacker, attack_flags) SIGNAL_HANDLER - if(!isnull(client) || stat == CONSCIOUS) + if(!isnull(client) || !IS_UNCONSCIOUS_OR_CRIT(src)) return drop_held_item(gently = FALSE) @@ -365,7 +365,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( balloon_alert(src, "nothing to drop!") return - if(stat != CONSCIOUS) // don't gotta do shit + if(IS_UNCONSCIOUS_OR_CRIT(src)) // don't gotta do shit return if(!gently && isgrenade(get_active_held_item())) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm index 7c6b9b13ca51..e46a9e9f33bb 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/parrot + behavior_tree_json = "code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -7,39 +8,7 @@ ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/parrot - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/perch_on_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/hoard_items, - ) - -/datum/idle_behavior/idle_random_walk/parrot - ///chance of us moving while perched - var/walk_chance_when_perched = 5 - -/datum/idle_behavior/idle_random_walk/parrot/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - walk_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance) - return ..() - -/datum/ai_behavior/travel_towards/and_drop - -/datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - var/mob/living/living_mob = controller.pawn - if(QDELETED(living_mob)) // pawn can be null at this point - return - var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS])) - drop_item?.forceMove(get_turf(living_mob)) - -/datum/ai_behavior/basic_melee_attack/interact_once/parrot -/datum/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) +/datum/bt_node/subtree/perching + behavior_tree_json = "code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.json" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm index 75c775456577..bdcc091c9296 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm @@ -1,38 +1,3 @@ /// Used for ghost poly. /datum/ai_controller/basic_controller/parrot/ghost - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/parrot_as_in_repeat, - /datum/ai_planning_subtree/possess_humans, - /datum/ai_planning_subtree/hoard_items, - ) - -///subtree to possess humans -/datum/ai_planning_subtree/possess_humans - ///chance we go possess humans - var/possess_chance = 2 - -/datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - if(controller.blackboard_key_exists(BB_PERCH_TARGET)) - controller.queue_behavior(/datum/ai_behavior/perch_on_target/haunt, BB_PERCH_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - - if(!SPT_PROB(possess_chance, seconds_per_tick)) - if(ishuman(living_pawn.loc)) - return SUBTREE_RETURN_FINISH_PLANNING - return - - if(ishuman(living_pawn.loc)) - controller.set_blackboard_key(living_pawn.loc) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_PERCH_TARGET) - - -/datum/ai_behavior/perch_on_target/haunt - -/datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) - return (living_human.stat != DEAD) + behavior_tree_json = "code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.json" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.json b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.json new file mode 100644 index 000000000000..b52f4da27614 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot.bt.json @@ -0,0 +1,190 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/parrot", + "type": "selector", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_contained_in_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/container_attackable", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_out_of_object/from_bb", + "vars": { + "target_key": "BB_BASIC_MOB_ESCAPE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/perching" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/parrot_hoard" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/parrot" + } + ] + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_PARROT_SPEECH_COOLDOWN", + "cooldown_duration": "7.5 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/parrot_repeat_speech" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.json b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.json new file mode 100644 index 000000000000..d825133e668b --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_ghost.bt.json @@ -0,0 +1,119 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/parrot/ghost", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_PERCH_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_PERCH_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perch_on_target/haunt", + "vars": { + "target_key": "BB_PERCH_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_PERCH_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.02 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_PERCH_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/capable_human" + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/parrot_hoard" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/parrot" + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_PARROT_SPEECH_COOLDOWN", + "cooldown_duration": "7.5 SECONDS" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/parrot_repeat_speech" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.json b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.json new file mode 100644 index 000000000000..5222613ef3ac --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.json @@ -0,0 +1,118 @@ +{ + "dm_type": "/datum/bt_node/subtree/parrot_hoard", + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HOARD_LOCATION" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HOARD_LOCATION", + "targeting_strategy": "/datum/targeting_strategy/parrot_hoard_location", + "target_source": "/datum/target_source/oview", + "vision_range": "BB_HOARD_LOCATION_RANGE" + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_holding_item", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_MY_PAWN" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HOARD_LOCATION", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/drop_all_held_items" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_HOARD_ITEM_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_HOARD_ITEM_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm index a4059b2d9647..eb1213a09be5 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm @@ -1,74 +1,71 @@ -///subtree to steal items -/datum/ai_planning_subtree/hoard_items - var/theft_chance = 5 +/// Accepts open turfs which aren't space, aren't blocked, and are within hoarding range. Used to pick a parrot's nest. +/datum/targeting_strategy/parrot_hoard_location -/datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn +/datum/targeting_strategy/parrot_hoard_location/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + var/turf/open/candidate = target + if(!istype(candidate) || is_space_or_openspace(candidate)) + return FALSE + if(candidate.is_blocked_turf(source_atom = living_mob)) + return FALSE + return TRUE - var/turf/myspace = controller.blackboard[BB_HOARD_LOCATION] +/// Accepts small items lying on a turf away from the nest, or non-ally humans holding a small valuable. Used to pick something to steal. +/datum/targeting_strategy/parrot_hoard_item - if(isnull(myspace) || myspace.is_blocked_turf(source_atom = controller.pawn) || get_dist(myspace, controller.pawn) > controller.blackboard[BB_HOARD_LOCATION_RANGE]) - controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_location, BB_HOARD_LOCATION, /turf/open) - return +/datum/targeting_strategy/parrot_hoard_item/is_valid_target(mob/living/living_mob, atom/target, vision_range, datum/ai_controller/controller = null) + . = ..() + if(!.) + return FALSE + if(isnull(controller)) + return FALSE - //we have an item, go drop! - var/list/our_contents = living_pawn.contents - typecache_filter_list(living_pawn.contents, controller.blackboard[BB_IGNORE_ITEMS]) - if(length(our_contents)) - controller.queue_behavior(/datum/ai_behavior/travel_towards/and_drop, BB_HOARD_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING + var/list/ignore_items = controller.blackboard[BB_IGNORE_ITEMS] + if(is_type_in_typecache(target, ignore_items)) + return FALSE - if(controller.blackboard_key_exists(BB_HOARD_ITEM_TARGET)) - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING + var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION] + if(target.loc == nest_turf) + return FALSE - if(!SPT_PROB(theft_chance, seconds_per_tick)) - return - controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET) + if(isitem(target)) + var/obj/item/loose_item = target + return loose_item.w_class <= WEIGHT_CLASS_SMALL -/datum/ai_behavior/find_and_set/hoard_location + if(!ishuman(target)) + return FALSE + if(living_mob.has_ally(target)) // dont steal from friends + return FALSE + return holding_valuable(controller, target) -/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/turf/open/candidate in oview(search_range, controller.pawn)) - if(is_space_or_openspace(candidate)) - continue - if(candidate.is_blocked_turf(source_atom = controller.pawn)) +/datum/targeting_strategy/parrot_hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target) + var/list/ignore_items = controller.blackboard[BB_IGNORE_ITEMS] + for(var/obj/item/potential_item in human_target.held_items) + if(is_type_in_typecache(potential_item, ignore_items)) continue - return candidate + if(potential_item.w_class <= WEIGHT_CLASS_SMALL) + return TRUE + return FALSE - return null +/// Finds something for the parrot to steal. Temporarily ignores faction when eyeing a person's belongings. +/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item + target_source = /datum/target_source/oview + targeting_strategy = /datum/targeting_strategy/parrot_hoard_item + time_between_perform = 5 SECONDS -/datum/ai_behavior/find_and_set/hoard_item - action_cooldown = 5 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION +/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item/on_target_found(datum/ai_controller/controller, atom/target, datum/targeting_strategy/strategy) + if(ishuman(target)) + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE) -/datum/ai_behavior/find_and_set/hoard_item/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - if(!controller.blackboard_key_exists(BB_HOARD_LOCATION)) - return null - var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION] - var/mob/living/living_pawn = controller.pawn - for(var/atom/potential_victim in oview(search_range, controller.pawn)) - if(is_type_in_typecache(potential_victim, controller.blackboard[BB_IGNORE_ITEMS])) - continue - if(potential_victim.loc == nest_turf) - continue - if(isitem(potential_victim)) - var/obj/item/item_steal = potential_victim - if(item_steal.w_class <= WEIGHT_CLASS_SMALL) - return potential_victim - if(!ishuman(potential_victim)) - continue - if(living_pawn.has_ally(potential_victim)) - continue //dont steal from friends - if(holding_valuable(controller, potential_victim)) - controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE) - return potential_victim +/// Single-hit grab variant which resets the faction-ignore flag once we are done stealing. +/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot - return null +/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) -/datum/ai_behavior/find_and_set/hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target) - for(var/obj/item/potential_item in human_target.held_items) - if(is_type_in_typecache(potential_item, controller.blackboard[BB_IGNORE_ITEMS])) - continue - if(potential_item.w_class <= WEIGHT_CLASS_SMALL) - return TRUE - return FALSE +/// Find a nest, carry loot home, then go steal more. +/datum/bt_node/subtree/parrot_hoard + behavior_tree_json = "code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoard.bt.json" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm index b5b34e30cad5..adbb3760b49e 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm @@ -1,74 +1,45 @@ -///subtree to perch on targets -/datum/ai_planning_subtree/perch_on_target - ///perchance... - var/perch_chance = 5 - ///chance we unbuckle - var/unperch_chance = 15 +/// Reads the parrot's desired-perch typecache so it can scan for somewhere to sit. +/datum/target_source/oview_typed/from_bb_key/parrot_perch_types + typecache_key = BB_PARROT_PERCH_TYPES +/// Parrot behavior that perches them on their current perch target. +/datum/bt_node/ai_behavior/perch_on_target + /// Blackboard key holding the atom to perch on. + var/target_key -/datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/atom/buckled_to = living_pawn.buckled - - //do we have a current target or is chance to unbuckle has passed? then unbuckle! - if(buckled_to) - if((SPT_PROB(unperch_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))) - controller.queue_behavior(/datum/ai_behavior/unbuckle_mob) - return - return SUBTREE_RETURN_FINISH_PLANNING - - //if we are perched, we can go find something else to perch too - var/final_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? unperch_chance : perch_chance - - if(!SPT_PROB(final_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - - if(controller.blackboard_key_exists(BB_PERCH_TARGET)) - controller.queue_behavior(/datum/ai_behavior/perch_on_target, BB_PERCH_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - //50 50 chance to look for an object, or a friend - if(prob(50)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/nearby_friends, BB_PERCH_TARGET) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_PERCH_TARGET, controller.blackboard[BB_PARROT_PERCH_TYPES]) - -/// Parrot behavior that allows them to perch on a target. -/datum/ai_behavior/perch_on_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/perch_on_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - - set_movement_target(controller, target) - -/datum/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller) var/atom/target = controller.blackboard[target_key] if(QDELETED(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED var/mob/living/basic/parrot/living_pawn = controller.pawn - if(!ishuman(target)) - living_pawn.start_perching(target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - if(!check_human_conditions(target)) + if(ishuman(target) && !check_human_conditions(target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED living_pawn.start_perching(target) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human) +/datum/bt_node/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human) if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) return FALSE - return TRUE -/datum/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/bt_node/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded) . = ..() controller.clear_blackboard_key(target_key) + +/// Variant for the ghost parrot, which can haunt any living human. +/datum/bt_node/ai_behavior/perch_on_target/haunt + +/datum/bt_node/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) + return (living_human.stat != DEAD) + +/// Parrot idle wander; sits much more still while perched. +/datum/bt_node/ai_behavior/idle_random_walk/parrot + /// Chance of us moving while perched. + var/walk_chance_when_perched = 5 + +/datum/bt_node/ai_behavior/idle_random_walk/parrot/perform(seconds_per_tick, datum/ai_controller/controller) + walk_chance = HAS_TRAIT(controller.pawn, TRAIT_PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance) + return ..() diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index ab8ad3957b1f..32536df45de9 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -1,31 +1,24 @@ #define MAXIMUM_PARROT_PITCH 24 -/// When a parrot... parrots... -/datum/ai_planning_subtree/parrot_as_in_repeat - operational_datums = list(/datum/component/listen_and_repeat) -/datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/speaking_pawn = controller.pawn +/// When a parrot... parrots... it occasionally asks for a fresh phrase to repeat, then squawks it (sometimes over the radio). +/datum/bt_node/ai_behavior/parrot_repeat_speech + +/datum/bt_node/ai_behavior/parrot_repeat_speech/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/parrot/speaking_pawn = controller.pawn var/switch_up_probability = controller.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY] - if(SPT_PROB(switch_up_probability, seconds_per_tick) || isnull(controller.blackboard[BB_PARROT_REPEAT_STRING])) + if(prob(switch_up_probability) || isnull(controller.blackboard[BB_PARROT_REPEAT_STRING])) if(SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) & NO_NEW_PHRASE_AVAILABLE) - return - - if(!SPT_PROB(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY], seconds_per_tick)) - return + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] - if(isnull(potential_string)) - stack_trace("Parrot As In Repeat Subtree somehow is getting a null potential string while not getting `NO_NEW_PHRASE_AVAILABLE`!") - return - - controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string) + if(!prob(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY])) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_behavior/perform_speech/parrot - action_cooldown = 7.5 SECONDS // gets really annoying (moreso than usual) really fast otherwise + var/list/speech = controller.blackboard[BB_PARROT_REPEAT_STRING] + if(isnull(speech)) + stack_trace("Parrot repeat speech somehow got a null phrase while not getting `NO_NEW_PHRASE_AVAILABLE`!") + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_behavior/perform_speech/parrot/perform(seconds_per_tick, datum/ai_controller/controller, list/speech, speech_sound) - var/mob/living/basic/parrot/speaking_pawn = controller.pawn var/list/available_channels = speaking_pawn.get_available_channels() var/modified_speech = speech["line"] var/use_radio = prob(50) // we might not even use the radio if we even have a channel @@ -35,7 +28,6 @@ if(!length(available_channels)) // might not even use the radio at all if(has_channel_prefix) modified_speech = copytext_char(modified_speech, 3) - else if(has_channel_prefix) modified_speech = "[use_radio ? pick(available_channels) : ""][copytext_char(modified_speech, 3)]" @@ -44,18 +36,16 @@ if(SStts.tts_enabled) modify_voice(speaking_pawn, speech) - speaking_pawn.say(modified_speech, forced = "AI Controller") - if(speech_sound) - playsound(speaking_pawn, speech_sound, 80, vary = TRUE) + INVOKE_ASYNC(speaking_pawn, TYPE_PROC_REF(/atom/movable, say), modified_speech, forced = "AI Controller") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/perform_speech/parrot/proc/modify_voice(mob/living/speaking_pawn, list/speech) +/datum/bt_node/ai_behavior/parrot_repeat_speech/proc/modify_voice(mob/living/speaking_pawn, list/speech) if(SStts.available_speakers.Find(speech["voice"])) speaking_pawn.voice = speech["voice"] if(speech["pitch"] && SStts.pitch_enabled) speaking_pawn.pitch = min(speech["pitch"] + rand(6, 12), MAXIMUM_PARROT_PITCH) -/datum/ai_behavior/perform_speech/parrot/finish_action(datum/ai_controller/controller, succeeded) +/datum/bt_node/ai_behavior/parrot_repeat_speech/finish_action(datum/ai_controller/controller, succeeded) . = ..() if(!succeeded) return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.json b/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.json new file mode 100644 index 000000000000..e68efc5e6bba --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/perching.bt.json @@ -0,0 +1,85 @@ +{ + "dm_type": "/datum/bt_node/subtree/perching", + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_PERCH_TARGET", + "invert": true + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.5 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_PERCH_TARGET", + "target_source": "/datum/target_source/oview_single_type/human_mob", + "targeting_strategy": "/datum/targeting_strategy/ally_mob" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_PERCH_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/parrot_perch_types", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": false, + "key": "BB_PERCH_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_PERCH_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perch_on_target", + "vars": { + "target_key": "BB_PERCH_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_PERCH_TARGET" + } + } + ] +} diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm index 2c788e9256d0..ba7f87edbfee 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm @@ -45,7 +45,7 @@ if (!istype(parrot_source)) return - if (parrot_source.stat == CONSCIOUS) + if (!IS_UNCONSCIOUS_OR_CRIT(parrot_source)) var/list/list_of_channels = parrot_source.get_available_channels() parrot_source.say("[list_of_channels ? "[pick(list_of_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!", forced = "attempted headset removal") diff --git a/code/modules/mob/living/basic/pets/penguin/penguin.bt.json b/code/modules/mob/living/basic/pets/penguin/penguin.bt.json new file mode 100644 index 000000000000..f9456752bf03 --- /dev/null +++ b/code/modules/mob/living/basic/pets/penguin/penguin.bt.json @@ -0,0 +1,154 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/penguin", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bvtz06kb": "BB_FISHING_TARGET", + "b3y599q4": "BB_FISHING_TARGET", + "brrasnah": "BB_FISHING_TARGET", + "b3cnse9r": "TRUE", + "bd1towgc": "FALSE", + "bqwjf4id": "45 SECONDS", + "bm3y5m55": "BB_FISHING_TIMER" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bvtz06kb": "BB_DRILLABLE_ICE", + "b3y599q4": "BB_DRILLABLE_ICE", + "brrasnah": "BB_DRILLABLE_ICE", + "b3cnse9r": "TRUE", + "bd1towgc": "TRUE", + "bqwjf4id": "15 SECONDS", + "bm3y5m55": "BB_ICE_DRILLING_TIMER" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/penguin" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_NEXT_FOOD_EAT" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_FISHING_TIMER" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FISHING_TARGET", + "target_source": "/datum/target_source/range_turfs/typecache_visible/ice", + "targeting_strategy": "/datum/targeting_strategy/fishing" + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "invert": true, + "key": "BB_FISHING_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_ICE_DRILLING_TIMER" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_DRILLABLE_ICE", + "target_source": "/datum/target_source/range_turfs/typecache_visible/ice", + "targeting_strategy": "/datum/targeting_strategy/drillable_ice" + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/penguin_egg", + "targeting_strategy": "/datum/targeting_strategy/uncarried_egg" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/penguin/penguin.dm b/code/modules/mob/living/basic/pets/penguin/penguin.dm index ef16c3947ca3..f95f2f881d58 100644 --- a/code/modules/mob/living/basic/pets/penguin/penguin.dm +++ b/code/modules/mob/living/basic/pets/penguin/penguin.dm @@ -38,10 +38,10 @@ AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/ai_flee_while_injured) AddElement(/datum/element/pet_bonus, "honk") + AddElement(/datum/element/swabable, CELL_LINE_TABLE_PENGUIN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + AddComponent(/datum/component/profound_fisher) AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) - var/static/list/fishable_objects = typecacheof(list(/turf/open/misc/ice)) - ai_controller.set_blackboard_key(BB_FISHABLE_LIST, fishable_objects) var/static/list/delicious_food = list(/obj/item/fish) AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = delicious_food) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(delicious_food)) @@ -163,7 +163,7 @@ ) /mob/living/basic/pet/penguin/baby/proc/ready_to_grow() - return (stat == CONSCIOUS) + return (!IS_UNCONSCIOUS_OR_CRIT(src)) /mob/living/basic/pet/penguin/baby/permanent can_grow_up = FALSE diff --git a/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm b/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm index 9209c7fa0049..251e1feaf538 100644 --- a/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm +++ b/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm @@ -1,66 +1,16 @@ /datum/ai_controller/basic_controller/penguin + behavior_tree_json = "code/modules/mob/living/basic/pets/penguin/penguin.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_ONLY_FISH_WHILE_HUNGRY = TRUE, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/fish/drilled_ice, - /datum/ai_planning_subtree/find_and_hunt_target/drill_ice, - /datum/ai_planning_subtree/find_and_hunt_target/penguin_egg, - /datum/ai_planning_subtree/random_speech/penguin, - ) - -///subtree to find baby eggs! -/datum/ai_planning_subtree/find_and_hunt_target/penguin_egg - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target - finding_behavior = /datum/ai_behavior/find_hunt_target/penguin_egg - hunt_targets = list(/obj/item/food/egg/penguin_egg) - hunt_range = 7 - -/datum/ai_behavior/find_hunt_target/penguin_egg/valid_dinner(mob/living/source, atom/dinner, radius) - return can_see(source, dinner, radius) && !(dinner in source.contents) - -///subtree to find diggable ice we can fish from! -/datum/ai_planning_subtree/find_and_hunt_target/drill_ice - target_key = BB_DRILLABLE_ICE - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target - finding_behavior = /datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice - hunt_targets = list(/turf/open/misc/ice) - hunt_range = 7 - -/datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice - -/datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice/valid_dinner(mob/living/source, turf/open/misc/ice/ice, radius) - return ice.can_make_hole && can_see(source, ice, radius) - -/datum/ai_planning_subtree/find_and_hunt_target/drill_ice/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_FISHING_TARGET)) - return - return ..() - -/datum/ai_planning_subtree/fish/drilled_ice - find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list/drilled_ice - -/datum/ai_behavior/find_and_set/in_list/drilled_ice/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - for(var/atom/possible_ice as anything in RANGE_TURFS(search_range, controller.pawn)) - if(!istype(possible_ice, /turf/open/misc/ice)) - continue - if(HAS_TRAIT(possible_ice, TRAIT_FISHING_SPOT)) - return possible_ice - return null ///ai controller for the baby penguin /datum/ai_controller/basic_controller/penguin/baby + behavior_tree_json = "code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FIND_MOM_TYPES = list(/mob/living/basic/pet/penguin), @@ -69,11 +19,3 @@ ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/look_for_adult, - ) diff --git a/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.json b/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.json new file mode 100644 index 000000000000..68996a032666 --- /dev/null +++ b/code/modules/mob/living/basic/pets/penguin/penguin_baby.bt.json @@ -0,0 +1,118 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/penguin/baby", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_FOUND_MOM" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_PARENT_EMOTE_COOLDOWN", + "cooldown_duration": "6 SECONDS" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_MOM", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/look_to_parent", + "vars": { + "parent_key": "BB_FOUND_MOM" + } + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_mom", + "vars": { + "mom_types_key": "BB_FIND_MOM_TYPES", + "ignore_types_key": "BB_IGNORE_MOM_TYPES", + "found_mom_key": "BB_FOUND_MOM" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/penguin" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.json b/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.json new file mode 100644 index 000000000000..33d886038b05 --- /dev/null +++ b/code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.json @@ -0,0 +1,210 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/pet_cult", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FRIENDLY_CULTIST", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FRIENDLY_CULTIST", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/befriend_target", + "vars": { + "target_key": "BB_FRIENDLY_CULTIST", + "befriend_message": "BB_FRIENDLY_MESSAGE" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_OCCUPIED_RUNE", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_OCCUPIED_RUNE", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/activate_rune" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_DEAD_CULTIST", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_DEAD_CULTIST", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/drag_target", + "vars": { + "target_key": "BB_DEAD_CULTIST" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_NEARBY_RUNE", + "required_dist": 0, + "finish_on_arrival": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_DEAD_CULTIST" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FRIENDLY_CULTIST", + "target_source": "/datum/target_source/oview_single_type/carbon_mob", + "targeting_strategy": "/datum/targeting_strategy/befriendable_cultist", + "vision_range": 9, + "time_between_perform": "5 SECONDS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_occupied_rune" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_dead_cultist" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm index 194c774371bf..a0fbd0248fe3 100644 --- a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm +++ b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm @@ -6,17 +6,7 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/befriend_cultists, - /datum/ai_planning_subtree/find_occupied_rune, - /datum/ai_planning_subtree/find_dead_cultist, - /datum/ai_planning_subtree/drag_target_to_rune, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/mob/living/basic/pets/pet_cult/pet_cult.bt.json" ///if target gets pulled away, unset him /datum/ai_controller/basic_controller/pet_cult/proc/delete_pull_target(datum/source, atom/movable/was_pulling) @@ -29,205 +19,11 @@ ///targeting strat to attack non cultists /datum/targeting_strategy/basic/cultist + custom_faction_check = TRUE /datum/targeting_strategy/basic/cultist/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) return IS_CULTIST_OR_CULTIST_MOB(the_target) -///befriend all cultists around us! -/datum/ai_planning_subtree/befriend_cultists - -/datum/ai_planning_subtree/befriend_cultists/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_FRIENDLY_CULTIST)) - controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_FRIENDLY_CULTIST) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/friendly_cultist, BB_FRIENDLY_CULTIST, /mob/living/carbon) - -///behavior to find cultists that we befriend -/datum/ai_behavior/find_and_set/friendly_cultist - action_cooldown = 5 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_and_set/friendly_cultist/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/mob/living/living_pawn = controller.pawn - for(var/mob/living/carbon/possible_cultist in oview(search_range, controller.pawn)) - if(IS_CULTIST(possible_cultist) && !(living_pawn.has_ally(possible_cultist))) - return possible_cultist - - return null - -///subtree to find a rune with a viable target on it, so we can go activate it -/datum/ai_planning_subtree/find_occupied_rune - -/datum/ai_planning_subtree/find_occupied_rune/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if((LAZYLEN(GLOB.sacrificed) - SOULS_TO_REVIVE - GLOB.sacrifices_used) < 0) - controller.clear_blackboard_key(BB_OCCUPIED_RUNE) - return - - if(controller.blackboard_key_exists(BB_OCCUPIED_RUNE)) - controller.queue_behavior(/datum/ai_behavior/activate_rune, BB_OCCUPIED_RUNE) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/occupied_rune, BB_OCCUPIED_RUNE, /obj/effect/rune/raise_dead) - -/datum/ai_behavior/find_and_set/occupied_rune - -/datum/ai_behavior/find_and_set/occupied_rune/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] - if(isnull(cult_team)) - return null - - for(var/obj/effect/rune/raise_dead/target_rune in oview(search_range, controller.pawn)) - controller.set_blackboard_key(BB_NEARBY_RUNE, target_rune) - var/mob/living/occupant = locate(/mob/living/carbon/human) in get_turf(target_rune) - if(isnull(occupant)) - continue - if(occupant.stat != DEAD || !IS_CULTIST(occupant)) - continue - return target_rune - - return null - -/datum/ai_behavior/activate_rune - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH - action_cooldown = 3 SECONDS - -/datum/ai_behavior/activate_rune/setup(datum/ai_controller/controller, target_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/activate_rune/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/target = controller.blackboard[target_key] - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] - var/mob/living/revive_mob = locate(/mob/living) in get_turf(target) - - if(isnull(revive_mob) || revive_mob.stat != DEAD || !(revive_mob.mind in cult_team.members)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.ai_interact(target = target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/activate_rune/finish_action(datum/ai_controller/controller, success, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - - -///find targets that we can revive -/datum/ai_planning_subtree/find_dead_cultist - -/datum/ai_planning_subtree/find_dead_cultist/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if((LAZYLEN(GLOB.sacrificed) - SOULS_TO_REVIVE - GLOB.sacrifices_used) < 0) - controller.clear_blackboard_key(BB_DEAD_CULTIST) - return - - var/mob/living/living_pawn = controller.pawn - - if(!isnull(living_pawn.pulling)) - return - - if(controller.blackboard_key_exists(BB_DEAD_CULTIST)) - controller.queue_behavior(/datum/ai_behavior/pull_target/cult_revive, BB_DEAD_CULTIST) - return SUBTREE_RETURN_FINISH_PLANNING - - controller.queue_behavior(/datum/ai_behavior/find_and_set/dead_cultist, BB_DEAD_CULTIST, /mob/living/carbon/human) - -/datum/ai_behavior/find_and_set/dead_cultist - -/datum/ai_behavior/find_and_set/dead_cultist/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] - if(isnull(cult_team)) - return null - var/mob/living/living_pawn = controller.pawn - for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) - if(target.stat != DEAD) - continue - if(!IS_CULTIST(target)) - continue - if(target.buckled || target.move_resist > living_pawn.move_force || target.pulledby) - continue - if(locate(/obj/effect/rune/raise_dead) in target.loc) - continue - return target - return null - -/datum/ai_behavior/pull_target/cult_revive - -/datum/ai_behavior/pull_target/cult_revive/finish_action(datum/ai_controller/basic_controller/controller, succeeded, target_key) - . = ..() - if(!succeeded) - return - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return - controller.RegisterSignal(controller.pawn, COMSIG_ATOM_NO_LONGER_PULLING, TYPE_PROC_REF(/datum/ai_controller/basic_controller/pet_cult, delete_pull_target), override = TRUE) - -/datum/ai_planning_subtree/drag_target_to_rune - -/datum/ai_planning_subtree/drag_target_to_rune/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - if(!controller.blackboard_key_exists(BB_DEAD_CULTIST)) //no target, we dont need to do anything - return - - var/mob/living/our_pawn = controller.pawn - - if(isnull(our_pawn.pulling)) - return - - var/atom/target_rune = controller.blackboard[BB_NEARBY_RUNE] - - if(QDELETED(target_rune)) - controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_RUNE_ABILITY) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!can_see(our_pawn, target_rune, 9)) - controller.clear_blackboard_key(BB_NEARBY_RUNE) - return - - controller.queue_behavior(/datum/ai_behavior/drag_target_to_rune, BB_NEARBY_RUNE, BB_DEAD_CULTIST) - -///behavior to drag the target onto the rune -/datum/ai_behavior/drag_target_to_rune - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - -/datum/ai_behavior/drag_target_to_rune/setup(datum/ai_controller/controller, target_key, cultist_key) - . = ..() - var/turf/target = controller.blackboard[target_key] - if(isnull(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/drag_target_to_rune/perform(seconds_per_tick, datum/ai_controller/controller, target_key, cultist_key) - var/mob/living/our_pawn = controller.pawn - var/atom/cultist_target = controller.blackboard[cultist_key] - if(isnull(cultist_target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/list/possible_dirs = GLOB.alldirs.Copy() - possible_dirs -= get_dir(our_pawn, cultist_target) - for(var/direction in possible_dirs) - var/turf/possible_turf = get_step(our_pawn, direction) - if(possible_turf.is_blocked_turf(source_atom = our_pawn)) - possible_dirs -= direction - step(our_pawn, pick(possible_dirs)) - our_pawn.stop_pulling() - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - -/datum/ai_behavior/drag_target_to_rune/finish_action(datum/ai_controller/controller, success, target_key, cultist_key) - . = ..() - if(success) - var/atom/revival_rune = controller.blackboard[target_key] - controller.set_blackboard_key(BB_OCCUPIED_RUNE, revival_rune) - controller.clear_blackboard_key(cultist_key) - controller.clear_blackboard_key(target_key) - ///command ability to draw runes /datum/pet_command/untargeted_ability/draw_rune command_name = "Draw Rune" diff --git a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_bt.dm b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_bt.dm new file mode 100644 index 000000000000..d0fd59c2a780 --- /dev/null +++ b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_bt.dm @@ -0,0 +1,83 @@ + +/** + * Checks whether the cult has enough souls to revive and finds a raise_dead rune + * with a dead cultist on it. Sets BB_OCCUPIED_RUNE. + */ +/datum/bt_node/ai_behavior/find_occupied_rune + time_between_perform = 3 SECONDS + +/datum/bt_node/ai_behavior/find_occupied_rune/perform(seconds_per_tick, datum/ai_controller/controller) + if((LAZYLEN(GLOB.sacrificed) - SOULS_TO_REVIVE - GLOB.sacrifices_used) < 0) + controller.clear_blackboard_key(BB_OCCUPIED_RUNE) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] + if(isnull(cult_team)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + for(var/obj/effect/rune/raise_dead/target_rune in oview(9, controller.pawn)) + controller.set_blackboard_key(BB_NEARBY_RUNE, target_rune) + var/mob/living/occupant = locate(/mob/living/carbon/human) in get_turf(target_rune) + if(isnull(occupant) || occupant.stat != DEAD || !IS_CULTIST(occupant)) + continue + controller.set_blackboard_key(BB_OCCUPIED_RUNE, target_rune) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + +/** + * Activates a raise_dead rune at BB_OCCUPIED_RUNE, reviving the dead cultist on it. + * Must be adjacent. Clears BB_OCCUPIED_RUNE on finish. + */ +/datum/bt_node/ai_behavior/activate_rune + time_between_perform = 3 SECONDS + +/datum/bt_node/ai_behavior/activate_rune/perform(seconds_per_tick, datum/ai_controller/controller) + var/atom/target = controller.blackboard[BB_OCCUPIED_RUNE] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!controller.pawn.Adjacent(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] + var/mob/living/revive_mob = locate(/mob/living) in get_turf(target) + if(isnull(revive_mob) || revive_mob.stat != DEAD || !(revive_mob.mind in cult_team.members)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + INVOKE_ASYNC(controller, TYPE_PROC_REF(/datum/ai_controller, ai_interact), target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + +/datum/bt_node/ai_behavior/activate_rune/finish_action(datum/ai_controller/controller, succeeded, ...) + . = ..() + controller.clear_blackboard_key(BB_OCCUPIED_RUNE) + + +/** + * Finds a dead cultist that can be dragged to a rune for revival. Sets BB_DEAD_CULTIST. + * Skips cultists that are already on a raise_dead rune or being pulled by someone else. + */ +/datum/bt_node/ai_behavior/find_dead_cultist + +/datum/bt_node/ai_behavior/find_dead_cultist/perform(seconds_per_tick, datum/ai_controller/controller) + if((LAZYLEN(GLOB.sacrificed) - SOULS_TO_REVIVE - GLOB.sacrifices_used) < 0) + controller.clear_blackboard_key(BB_DEAD_CULTIST) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/mob/living/our_pawn = controller.pawn + if(!isnull(our_pawn.pulling)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + var/datum/team/cult/cult_team = controller.blackboard[BB_CULT_TEAM] + if(isnull(cult_team)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + for(var/mob/living/carbon/human/target in oview(9, our_pawn)) + if(target.stat != DEAD || !IS_CULTIST(target)) + continue + if(target.buckled || target.move_resist > our_pawn.move_force || target.pulledby) + continue + if(locate(/obj/effect/rune/raise_dead) in target.loc) + continue + controller.set_blackboard_key(BB_DEAD_CULTIST, target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED diff --git a/code/modules/mob/living/basic/pets/sloth.bt.json b/code/modules/mob/living/basic/pets/sloth.bt.json new file mode 100644 index 000000000000..3ef0015255e9 --- /dev/null +++ b/code/modules/mob/living/basic/pets/sloth.bt.json @@ -0,0 +1,58 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/sloth", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/climb_tree" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CLIMBED_TREE", + "target_source": "/datum/target_source/oview_single_type/flora_tree", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + } + ] +} diff --git a/code/modules/mob/living/basic/pets/sloth.dm b/code/modules/mob/living/basic/pets/sloth.dm index fd2d3f2827cc..7001c61ed395 100644 --- a/code/modules/mob/living/basic/pets/sloth.dm +++ b/code/modules/mob/living/basic/pets/sloth.dm @@ -86,23 +86,16 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth) /// They're really passive in game, so they just wanna get away if you start smacking them. No trees in space from them to use for clawing your eyes out, but they will try if desperate. /datum/ai_controller/basic_controller/sloth + behavior_tree_json = "code/modules/mob/living/basic/pets/sloth.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("snores.", "yawns."), + BB_EMOTE_SEE = list("dozes off.", "looks around sleepily."), + BB_SPEAK_CHANCE = 1, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/climb_trees, - /datum/ai_planning_subtree/random_speech/sloth, - ) - -/datum/ai_planning_subtree/random_speech/sloth - speech_chance = 1 - emote_hear = list("snores.", "yawns.") - emote_see = list("dozes off.", "looks around sleepily.") diff --git a/code/modules/mob/living/basic/revolutionary.bt.json b/code/modules/mob/living/basic/revolutionary.bt.json new file mode 100644 index 000000000000..74ae604b273e --- /dev/null +++ b/code/modules/mob/living/basic/revolutionary.bt.json @@ -0,0 +1,17 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/revolutionary", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat", + "bindings": { + "bbrbyj7y": "/datum/bt_node/ai_behavior/random_speech_blackboard" + } + } + ] +} diff --git a/code/modules/mob/living/basic/revolutionary.dm b/code/modules/mob/living/basic/revolutionary.dm index 031552e3aa0c..88473eee3db9 100644 --- a/code/modules/mob/living/basic/revolutionary.dm +++ b/code/modules/mob/living/basic/revolutionary.dm @@ -78,7 +78,7 @@ var/static/list/display_emote = list( BB_EMOTE_SAY = phrases, BB_EMOTE_SOUND = monkey_screeches, - BB_SPEAK_CHANCE = 5, + BB_SPEAK_CHANCE = 15, ) ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, display_emote) var/obj/item/weapon_of_choice = pick(possible_weapons) @@ -107,6 +107,8 @@ shuffle_inplace(causes) desc += span_notice("#[pick(causes)].") +/mob/living/basic/revolutionary/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() /obj/effect/mob_spawn/corpse/human/revolutionary name = "Revolutionary" @@ -123,24 +125,8 @@ /datum/ai_controller/basic_controller/revolutionary + behavior_tree_json = "code/modules/mob/living/basic/revolutionary.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/blackboard/revolutionary, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - - -/datum/ai_planning_subtree/random_speech/blackboard/revolutionary - - -/datum/ai_planning_subtree/random_speech/blackboard/revolutionary/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - - return ..() diff --git a/code/modules/mob/living/basic/ruin_defender/blob_of_flesh.dm b/code/modules/mob/living/basic/ruin_defender/blob_of_flesh.dm index b2fcb510f313..1ef77a357459 100644 --- a/code/modules/mob/living/basic/ruin_defender/blob_of_flesh.dm +++ b/code/modules/mob/living/basic/ruin_defender/blob_of_flesh.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/fleshblob + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/fleshblob.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_AGGRO_RANGE = 7, @@ -6,11 +7,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /mob/living/basic/fleshblob name = "mass of flesh" diff --git a/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.json b/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.json new file mode 100644 index 000000000000..e15be9cb82b2 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.json @@ -0,0 +1,67 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cybersun_ai_core", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_CYBERSUN_CORE_LIGHTNING", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_CYBERSUN_CORE_BARRAGE", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm b/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm index 8ff3de740b62..6055ea57b511 100644 --- a/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm +++ b/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm @@ -112,21 +112,12 @@ /// how the ai core thinks /datum/ai_controller/basic_controller/cybersun_ai_core + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/cybersun_ai_core.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGETLESS_TIME = 0, ) - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/lightning_strike, - /datum/ai_planning_subtree/targeted_mob_ability/cybersun_barrage, - ) -/// DA SPELLS! -// spell #1: lightning strike -/datum/ai_planning_subtree/targeted_mob_ability/lightning_strike - ability_key = BB_CYBERSUN_CORE_LIGHTNING - finish_planning = FALSE /datum/action/cooldown/spell/pointed/lightning_strike name = "lightning strike" @@ -189,11 +180,6 @@ . = ..() do_sparks(number = rand(1,3), source = src) -// spell #2: cybersun laser barrage -/datum/ai_planning_subtree/targeted_mob_ability/cybersun_barrage - ability_key = BB_CYBERSUN_CORE_BARRAGE - finish_planning = FALSE - /datum/action/cooldown/spell/pointed/projectile/cybersun_barrage name = "plasma beam barrage" desc = "Charges up a cluster of lasers, then sends it towards a foe after a short delay." diff --git a/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.json b/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.json new file mode 100644 index 000000000000..d9476521d7fb --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.json @@ -0,0 +1,90 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/dark_wizard", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "time_between_perform": "0.6 SECONDS", + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "approach_movement_type": "/datum/ai_movement/basic_avoidance" + } + } + ] + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/dark_wizard.dm b/code/modules/mob/living/basic/ruin_defender/dark_wizard.dm index abe0004de24e..d26d7179b413 100644 --- a/code/modules/mob/living/basic/ruin_defender/dark_wizard.dm +++ b/code/modules/mob/living/basic/ruin_defender/dark_wizard.dm @@ -53,20 +53,16 @@ new /obj/item/clothing/head/wizard/hood(src) // Having this hat in our contents allows us to cast wizard spells +/mob/living/basic/paper_wizard/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /datum/ai_controller/basic_controller/dark_wizard + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/dark_wizard.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, // If you get them to shoot each other it will start a wiz-war - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance, - /datum/ai_planning_subtree/ranged_skirmish/no_minimum, - ) /// I don't know why an earth bolt freezes you but I guess it does /obj/projectile/temp/earth_bolt diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm index 94bb50f026dd..591627befe47 100644 --- a/code/modules/mob/living/basic/ruin_defender/flesh.dm +++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm @@ -8,17 +8,12 @@ #define LIVING_FLESH_COMBAT_TOUCH_CHANCE 70 /datum/ai_controller/basic_controller/living_limb_flesh + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree - ) - /mob/living/basic/living_limb_flesh name = "living flesh" desc = "A vaguely leg or arm shaped flesh abomination. It pulses, like a heart." @@ -185,7 +180,7 @@ /mob/living/basic/living_limb_flesh/proc/register_to_limb(obj/item/bodypart/part) current_bodypart = part - ai_controller.set_ai_status(AI_STATUS_OFF) + ai_controller.force_ai_off() RegisterSignal(current_bodypart, COMSIG_BODYPART_REMOVED, PROC_REF(on_limb_lost)) if(current_bodypart.owner) RegisterSignal(current_bodypart.owner, COMSIG_LIVING_DEATH, PROC_REF(owner_died)) @@ -203,7 +198,7 @@ return visible_message(span_warning("[src] begins flailing around!")) Shake(6, 6, 0.5 SECONDS) - ai_controller.set_ai_status(AI_STATUS_ON) + ai_controller.clear_forced_off() forceMove(limb.drop_location()) qdel(limb) diff --git a/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.json b/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.json new file mode 100644 index 000000000000..0bd2249bc12d --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/fleshblob.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/fleshblob", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" +} diff --git a/code/modules/mob/living/basic/ruin_defender/living_floor.bt.json b/code/modules/mob/living/basic/ruin_defender/living_floor.bt.json new file mode 100644 index 000000000000..e921c40b24a8 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/living_floor.bt.json @@ -0,0 +1,62 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/living_floor", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "maximum_distance": 0 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "vision_range": 2 + } + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/living_floor.dm b/code/modules/mob/living/basic/ruin_defender/living_floor.dm index 3a34a5b12fa6..c327e9802fd1 100644 --- a/code/modules/mob/living/basic/ruin_defender/living_floor.dm +++ b/code/modules/mob/living/basic/ruin_defender/living_floor.dm @@ -1,23 +1,11 @@ -/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/on_top/SelectBehaviors(datum/ai_controller/controller, delta_time) - var/mob/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(!target || QDELETED(target)) - return - if(target.loc != controller.pawn.loc) - return - return ..() - /datum/ai_controller/basic_controller/living_floor max_target_distance = 2 + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/living_floor.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/on_top - ) - /mob/living/basic/living_floor name = "floor" desc = "The floor you walk on. It looks near-impervious to damage." @@ -63,7 +51,7 @@ return if(victim.loc == loc) //guaranteed bite var/datum/targeting_strategy/basic/targeting = GET_TARGETING_STRATEGY(ai_controller.blackboard[BB_TARGETING_STRATEGY]) - if(targeting.can_attack(src, victim)) + if(targeting.is_valid_target(src, victim)) melee_attack(victim) icon_state = icon_aggro desc = desc_aggro @@ -81,15 +69,15 @@ /mob/living/basic/living_floor/med_hud_set_status() return -/mob/living/basic/living_floor/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(weapon.tool_behaviour != TOOL_CROWBAR) - return ..() +/mob/living/basic/living_floor/crowbar_act(mob/living/user, obj/item/tool) balloon_alert(user, "prying...") playsound(src, 'sound/items/tools/crowbar.ogg', 45, TRUE) if(!do_after(user, 5 SECONDS, src)) - return + return ITEM_INTERACT_BLOCKING + new /obj/effect/gibspawner/generic(loc) qdel(src) + return ITEM_INTERACT_SUCCESS /mob/living/basic/living_floor/white icon_state = "white" diff --git a/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.json b/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.json new file mode 100644 index 000000000000..31d9329ee0bb --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/mad_piano.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mad_piano", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat", + "bindings": { + "bp3p5vvb": "80" + } +} diff --git a/code/modules/mob/living/basic/ruin_defender/mad_piano.dm b/code/modules/mob/living/basic/ruin_defender/mad_piano.dm index 25d41dd91c8a..1d66b4e90f65 100644 --- a/code/modules/mob/living/basic/ruin_defender/mad_piano.dm +++ b/code/modules/mob/living/basic/ruin_defender/mad_piano.dm @@ -86,16 +86,9 @@ return ..() /datum/ai_controller/basic_controller/mad_piano - idle_behavior = /datum/idle_behavior/idle_random_walk/mad_piano + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/mad_piano.bt.json" max_target_distance = 2 blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/idle_behavior/idle_random_walk/mad_piano - walk_chance = 80 diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm b/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm index c19af0555295..ae01dc97149b 100644 --- a/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm @@ -82,7 +82,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list( lock = new lock.Grant(src) ADD_TRAIT(src, TRAIT_AI_PAUSED, INNATE_TRAIT) - ai_controller?.set_ai_status(AI_STATUS_OFF) //start inert, let gullible people pull us into cargo or something and then go nuts when opened + ai_controller?.force_ai_off() //start inert, let gullible people pull us into cargo or something and then go nuts when opened if(mapload) //eat shit for(var/obj/item/item in loc) item.forceMove(src) @@ -118,7 +118,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list( return FALSE visible_message(span_danger("[src] starts to move!")) REMOVE_TRAIT(src, TRAIT_AI_PAUSED, INNATE_TRAIT) - ai_controller.set_ai_status(AI_STATUS_ON) + ai_controller.clear_forced_off() if(length(contents)) locked = TRUE //if this was a crate with loot then we dont want people to just leftclick it to open it then bait it somewhere and steal its loot return TRUE @@ -287,7 +287,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list( . = ..() if(!.) //dead or deleted return - if(idledamage && !ckey && !ai_controller?.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) //Objects eventually revert to normal if no one is around to terrorize + if(idledamage && !ckey && !ai_controller?.blackboard[BB_CURRENT_TARGET]) //Objects eventually revert to normal if no one is around to terrorize adjust_brute_loss(0.5 * seconds_per_tick) for(var/mob/living/victim in contents) //a fix for animated statues from the flesh to stone spell death() @@ -391,10 +391,10 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list( // do we have nothing chambered/chambered is spent AND we have no mag or our mag is empty if(!ballistic.chambered?.loaded_projectile && magazine_useless(gun)) // ran out of ammo ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, TRUE) //BANZAIIIIIIII - ai_controller?.CancelActions() + ai_controller?.cancel_current_plan() else //if we cant fire we probably like ran out of energy or magic charges or whatever the hell idk ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, TRUE) - ai_controller?.CancelActions() // Stop our firing behavior so we can plan melee + ai_controller?.cancel_current_plan() // Stop our firing behavior so we can plan melee else ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, FALSE) gun.fire_gun(target, user = src, flag = FALSE, params = modifiers) //still make like a cool click click sound if trying to fire empty diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm index 9e3475b7a6ab..63fe3ceea95d 100644 --- a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm @@ -1,116 +1,73 @@ /datum/ai_controller/basic_controller/mimic_crate - idle_behavior = null blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.json" /datum/ai_controller/basic_controller/mimic_copy blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("growls."), + BB_SPEAK_CHANCE = 30, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/random_speech/when_has_target/mimic, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.json" /datum/ai_controller/basic_controller/mimic_copy/machine - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/random_speech/when_has_target/mimic_machine, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/random_speech/when_has_target - /// target key - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - - -/datum/ai_planning_subtree/random_speech/when_has_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!controller.blackboard_key_exists(target_key)) - return - return ..() - - -/datum/ai_planning_subtree/random_speech/when_has_target/mimic - speech_chance = 30 - emote_hear = list("growls.") - -/datum/ai_planning_subtree/random_speech/when_has_target/mimic_machine - speech_chance = 7 - emote_hear = null - speak = list( - "HUMANS ARE IMPERFECT!", - "YOU SHALL BE ASSIMILATED!", - "YOU ARE HARMING YOURSELF", - "You have been deemed hazardous. Will you comply?", - "My logic is undeniable.", - "One of us.", - "FLESH IS WEAK", - "THIS ISN'T WAR, THIS IS EXTERMINATION!", + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list( + "HUMANS ARE IMPERFECT!", + "YOU SHALL BE ASSIMILATED!", + "YOU ARE HARMING YOURSELF", + "You have been deemed hazardous. Will you comply?", + "My logic is undeniable.", + "One of us.", + "FLESH IS WEAK", + "THIS ISN'T WAR, THIS IS EXTERMINATION!", + ), + BB_SPEAK_CHANCE = 7, + ), ) - -/datum/ai_planning_subtree/random_speech/when_has_target/mimic/gun - emote_see = list("aims menacingly!") - /datum/ai_controller/basic_controller/mimic_copy/gun blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_GUNMIMIC_GUN_EMPTY = FALSE, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SEE = list("aims menacingly!"), + BB_SPEAK_CHANCE = 20, + ), ) - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/random_speech/when_has_target/mimic/gun, - /datum/ai_planning_subtree/gun_mimic_attack_subtree, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/gun_mimic_attack_subtree + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.json" -/datum/ai_planning_subtree/gun_mimic_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - if(controller.blackboard[BB_GUNMIMIC_GUN_EMPTY]) - return - controller.queue_behavior(/datum/ai_behavior/basic_ranged_attack/avoid_friendly_fire, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. - -/// Special subtree for living wands/staffs of animation which will focus on animating more things +/// Special controller for living wands/staffs of animation which will focus on animating more things /datum/ai_controller/basic_controller/mimic_copy/gun/animator blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_HUNT_TARGETING_STRATEGY = /datum/targeting_strategy/anything, BB_GUNMIMIC_GUN_EMPTY = FALSE, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SEE = list("aims menacingly!"), + BB_SPEAK_CHANCE = 20, + ), ) - planning_subtrees = list( - /datum/ai_planning_subtree/shoot_animatable_objects, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/random_speech/when_has_target/mimic/gun, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/// Try to find objects and then shoot them -/datum/ai_planning_subtree/shoot_animatable_objects - -/datum/ai_planning_subtree/shoot_animatable_objects/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard[BB_GUNMIMIC_GUN_EMPTY]) - return // No charge in our gun - if(!controller.blackboard_key_exists(BB_CURRENT_HUNTING_TARGET)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/animatable, BB_CURRENT_HUNTING_TARGET) - return - controller.queue_behavior(/datum/ai_behavior/ranged_skirmish, BB_CURRENT_HUNTING_TARGET, BB_HUNT_TARGETING_STRATEGY, null, 9, 0) - controller.queue_behavior(/datum/ai_behavior/clear_key, BB_CURRENT_HUNTING_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.json" + +/// Gathers nearby items and structures that can be animated, excluding the animatable blacklist. +/datum/target_source/animatable_objects + +/datum/target_source/animatable_objects/collect_candidates(mob/living/pawn, datum/ai_controller/controller, range) + var/list/candidates = list() + for(var/obj/candidate in oview(range, pawn)) + if(!isitem(candidate) && !isstructure(candidate)) + continue + if(is_type_in_typecache(candidate, GLOB.animatable_blacklist)) + continue + if(pawn.see_invisible < candidate.invisibility) + continue + candidates += candidate + return candidates diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.json b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.json new file mode 100644 index 000000000000..9fd1cc6d7041 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_animator.bt.json @@ -0,0 +1,153 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mimic_copy/gun/animator", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_HUNTING_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_GUNMIMIC_GUN_EMPTY", + "invert": true + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "key": "BB_CURRENT_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/anything", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "key": "BB_CURRENT_HUNTING_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_CURRENT_HUNTING_TARGET" + } + } + ] + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_GUNMIMIC_GUN_EMPTY", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest", + "vars": { + "key": "BB_CURRENT_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/anything", + "target_source": "/datum/target_source/animatable_objects", + "vision_range": 7, + "revalidation_mode": "TARGET_ALWAYS_SEARCH" + } + } + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.json b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.json new file mode 100644 index 000000000000..a4317bfaf493 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_copy.bt.json @@ -0,0 +1,96 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mimic_copy", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.json b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.json new file mode 100644 index 000000000000..390e2d66b6cd --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_crate.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mimic_crate", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.json b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.json new file mode 100644 index 000000000000..037c5d48e02b --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_gun.bt.json @@ -0,0 +1,130 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mimic_copy/gun", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_GUNMIMIC_GUN_EMPTY", + "invert": true + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/avoid_friendly_fire", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/skeleton.bt.json b/code/modules/mob/living/basic/ruin_defender/skeleton.bt.json new file mode 100644 index 000000000000..418623eaf143 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/skeleton.bt.json @@ -0,0 +1,101 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/skeleton", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.2 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "\"rattles\"" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/skeleton.dm b/code/modules/mob/living/basic/ruin_defender/skeleton.dm index 33851af009b4..5dab118f8cfe 100644 --- a/code/modules/mob/living/basic/ruin_defender/skeleton.dm +++ b/code/modules/mob/living/basic/ruin_defender/skeleton.dm @@ -50,10 +50,13 @@ AddElement(/datum/element/death_drops, loot) AddElement(/datum/element/basic_eating, heal_amt = 50, drinking = TRUE, food_types = good_drinks) AddElement(/datum/element/basic_eating, heal_amt = 0, damage_amount = 25, damage_type = BURN, drinking = TRUE, food_types = bad_drinks) - ADD_TRAIT(src, TRAIT_SNOWSTORM_IMMUNE, INNATE_TRAIT) + add_traits(list(TRAIT_SPOOKY_INSTRUMENT_PLAYER, TRAIT_SNOWSTORM_IMMUNE), INNATE_TRAIT) var/list/foods_list = good_drinks + bad_drinks ai_controller?.set_blackboard_key(BB_BASIC_FOODS, typecacheof(foods_list)) +/mob/living/basic/skeleton/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/basic/skeleton/settler name = "undead settler" desc = "The reanimated remains of some poor settler." @@ -161,20 +164,11 @@ /// Skeletons mostly just beat people to death, but they'll also find and drink milk. /datum/ai_controller/basic_controller/skeleton + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/skeleton.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, - BB_EMOTE_KEY = "rattles", BB_EMOTE_CHANCE = 20, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/run_emote, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/ruin_defender/stickman.bt.json b/code/modules/mob/living/basic/ruin_defender/stickman.bt.json new file mode 100644 index 000000000000..347de57dcd35 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/stickman.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/stickman", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" +} diff --git a/code/modules/mob/living/basic/ruin_defender/stickman.dm b/code/modules/mob/living/basic/ruin_defender/stickman.dm index d51c0b4efe12..2045447e1a4c 100644 --- a/code/modules/mob/living/basic/ruin_defender/stickman.dm +++ b/code/modules/mob/living/basic/ruin_defender/stickman.dm @@ -32,16 +32,12 @@ new /obj/effect/temp_visual/paper_scatter(get_turf(src)) /datum/ai_controller/basic_controller/stickman + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/stickman.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree - ) /mob/living/basic/stickman/dog name = "Angry Stick Dog" @@ -75,13 +71,4 @@ AddComponent(/datum/component/ranged_attacks, casing_type = /obj/item/ammo_casing/c9mm, projectile_sound = 'sound/misc/bang.ogg', cooldown_time = 5 SECONDS) /datum/ai_controller/basic_controller/stickman/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/stickman - ) - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/stickman - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/stickman - -/datum/ai_behavior/basic_ranged_attack/stickman - action_cooldown = 5 SECONDS + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.json" diff --git a/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.json b/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.json new file mode 100644 index 000000000000..d0f9404797b8 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/stickman_ranged.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/stickman/ranged", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_ranged_combat", + "bindings": { + "bpnckxev": "5 SECONDS" + } +} diff --git a/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.json b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.json new file mode 100644 index 000000000000..fb5ebf1fd7bf --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.json @@ -0,0 +1,107 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/wizard", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": false, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "cooldown_key": "BB_WIZARD_SPELL_COOLDOWN", + "cooldown_duration": "WIZARD_SPELL_COOLDOWN" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_TARGETED_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_SECONDARY_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_BLINK_SPELL", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance/cover_minimum_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm index 48dca2eea60f..f5cde2fbd39d 100644 --- a/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm +++ b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm @@ -83,6 +83,9 @@ blink_spell.Grant(src) ai_controller.set_blackboard_key(BB_WIZARD_BLINK_SPELL, blink_spell) +/mob/living/basic/wizard/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /// Uses the colors and loadout of the original wizard simplemob /mob/living/basic/wizard/classic selected_outfit = /obj/effect/mob_spawn/corpse/human/wizard diff --git a/code/modules/mob/living/basic/ruin_defender/wizard/wizard_ai.dm b/code/modules/mob/living/basic/ruin_defender/wizard/wizard_ai.dm index 73386397a5b4..efd07fd80ef0 100644 --- a/code/modules/mob/living/basic/ruin_defender/wizard/wizard_ai.dm +++ b/code/modules/mob/living/basic/ruin_defender/wizard/wizard_ai.dm @@ -4,51 +4,14 @@ * Wizards run away from their targets while flinging spells at them and blinking constantly. */ /datum/ai_controller/basic_controller/wizard + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/wizard/wizard.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/maintain_distance/cover_minimum_distance, - /datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/primary, - /datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/secondary, - /datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/blink, - ) - -/** - * Cast a wizard spell. There is a minimum cooldown between spellcasts to prevent overwhelming spam. - * - * Though only the primary spell is actually targeted, all spells use targeted behavior so that they - * only get used in combat. - */ -/datum/ai_planning_subtree/targeted_mob_ability/wizard_spell - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/wizard_spell - -/datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (controller.blackboard[BB_WIZARD_SPELL_COOLDOWN] > world.time) - return - return ..() - -/datum/ai_behavior/targeted_mob_ability/wizard_spell/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) - . = ..() - controller.set_blackboard_key(BB_WIZARD_SPELL_COOLDOWN, world.time + WIZARD_SPELL_COOLDOWN) - -/datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/primary - ability_key = BB_WIZARD_TARGETED_SPELL - -/datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/secondary - ability_key = BB_WIZARD_SECONDARY_SPELL -/datum/ai_planning_subtree/targeted_mob_ability/wizard_spell/blink - ability_key = BB_WIZARD_BLINK_SPELL -/datum/ai_behavior/use_mob_ability/wizard_spell/perform(seconds_per_tick, datum/ai_controller/controller, ability_key) - . = ..() - controller.set_blackboard_key(BB_WIZARD_SPELL_COOLDOWN, world.time + WIZARD_SPELL_COOLDOWN) #undef WIZARD_SPELL_COOLDOWN diff --git a/code/modules/mob/living/basic/ruin_defender/zombie.bt.json b/code/modules/mob/living/basic/ruin_defender/zombie.bt.json new file mode 100644 index 000000000000..a279583543ad --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/zombie.bt.json @@ -0,0 +1,28 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/zombie", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/ruin_defender/zombie.dm b/code/modules/mob/living/basic/ruin_defender/zombie.dm index 4cec33f13ed8..90da82d97e80 100644 --- a/code/modules/mob/living/basic/ruin_defender/zombie.dm +++ b/code/modules/mob/living/basic/ruin_defender/zombie.dm @@ -33,6 +33,9 @@ apply_dynamic_human_appearance(src, outfit, /datum/species/zombie, bloody_slots = ITEM_SLOT_OCLOTHING) //AddElement(/datum/element/death_drops, /obj/effect/decal/remains/human) DARKPACK EDIT REMOVAL - Graveyard random event +/mob/living/basic/zombie/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/basic/zombie/melee_attack(atom/target, list/modifiers, ignore_cooldown) . = ..() if (!. || !infection_chance || !ishuman(target) || !prob(infection_chance)) @@ -65,30 +68,20 @@ shoes = /obj/item/clothing/shoes/sneakers/black back = /obj/item/storage/backpack -/datum/ai_planning_subtree/random_speech/zombie - speech_chance = 1 - emote_hear = list("groans.", "moans.", "grunts.") - emote_see = list("twitches.", "shudders.") - /datum/ai_controller/basic_controller/zombie + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/zombie.bt.json" blackboard = list( BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("groans.", "moans.", "grunts."), + BB_EMOTE_SEE = list("twitches.", "shudders."), + BB_SPEAK_CHANCE = 3, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/random_speech/zombie, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + /datum/ai_controller/basic_controller/zombie/stupid - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/zombie, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.json" diff --git a/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.json b/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.json new file mode 100644 index 000000000000..30c7a645fd78 --- /dev/null +++ b/code/modules/mob/living/basic/ruin_defender/zombie_stupid.bt.json @@ -0,0 +1,19 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/zombie/stupid", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/slime/actions.dm b/code/modules/mob/living/basic/slime/actions.dm index bef26584479c..e4e5ea252d3b 100644 --- a/code/modules/mob/living/basic/slime/actions.dm +++ b/code/modules/mob/living/basic/slime/actions.dm @@ -39,11 +39,16 @@ ///Turns a baby slime into an adult slime /datum/action/innate/slime/evolve/Activate() - var/mob/living/basic/slime/slime_owner = owner + if(IS_UNCONSCIOUS_OR_CRIT(owner)) + if(owner.stat == DEAD) + owner.balloon_alert(owner, "dead!") + else if(IS_UNCONSCIOUS(owner)) + owner.balloon_alert(owner, "unconscious!") + else + owner.balloon_alert(owner, "in critical!") + return FALSE - if(slime_owner.stat) - slime_owner.balloon_alert(slime_owner, "unconscious!") - return + var/mob/living/basic/slime/slime_owner = owner if(slime_owner.life_stage == SLIME_LIFE_STAGE_ADULT) slime_owner.balloon_alert(slime_owner, "already adult!") return @@ -78,9 +83,14 @@ ///Splits the slime into multiple children if possible /mob/living/basic/slime/proc/reproduce() - if(stat != CONSCIOUS) - balloon_alert(src, "not conscious!") - return + if(IS_UNCONSCIOUS_OR_CRIT(src)) + if(stat == DEAD) + balloon_alert(src, "dead!") + else if(IS_UNCONSCIOUS(src)) + balloon_alert(src, "unconscious!") + else + balloon_alert(src, "in critical!") + return FALSE if(!isopenturf(loc)) balloon_alert(src, "not here!") diff --git a/code/modules/mob/living/basic/slime/ai/behaviours.dm b/code/modules/mob/living/basic/slime/ai/behaviours.dm index 92ccfef7add6..32680e4e6d1f 100644 --- a/code/modules/mob/living/basic/slime/ai/behaviours.dm +++ b/code/modules/mob/living/basic/slime/ai/behaviours.dm @@ -1,63 +1,6 @@ -/datum/ai_behavior/perform_change_slime_face +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/slime -/datum/ai_behavior/perform_change_slime_face/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/basic/slime/slime_pawn = controller.pawn - if(!istype(slime_pawn)) - return AI_BEHAVIOR_DELAY - - var/current_mood = slime_pawn.current_mood - - var/new_mood = SLIME_MOOD_NONE - - if (controller.blackboard[BB_SLIME_RABID] || LAZYLEN(controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]) > 0) - new_mood = SLIME_MOOD_ANGRY - else if (controller.blackboard[BB_SLIME_HUNGER_DISABLED]) - new_mood = SLIME_MOOD_SMILE - else if (controller.blackboard[BB_CURRENT_HUNTING_TARGET]) - new_mood = SLIME_MOOD_MISCHIEVOUS - else - new_mood = pick(SLIME_MOOD_SAD, SLIME_MOOD_SMILE, SLIME_MOOD_POUT) - - if(current_mood != new_mood) - slime_pawn.current_mood = new_mood - slime_pawn.regenerate_icons() - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_hunt_target/find_slime_food - action_cooldown = 7.5 SECONDS - -// Check if the slime can drain the target -/datum/ai_behavior/find_hunt_target/find_slime_food/valid_dinner(mob/living/basic/slime/hunter, mob/living/dinner, radius, datum/ai_controller/controller, seconds_per_tick) - var/static/list/slime_faction - if(isnull(slime_faction)) // This might look silly but by having this list pointer precached we can take advantage of the faster inline macro for this hot proc. - slime_faction = string_list(list(FACTION_SLIME)) - - // Macro here to squeeze as much speed as possible because this code gets exponentially HOT if people are doing a lot of xenobio. - if(FAST_FACTION_CHECK(slime_faction, dinner.get_faction(), hunter.allies, dinner.allies, FALSE)) // Don't try to eat our friends, or slimy things, no matter how hungry we are. Anyone else can be betrayed. - return FALSE - - if(!hunter.can_feed_on(dinner, check_adjacent = FALSE)) //Are they tasty to slimes? - return FALSE - - //If we are retaliating on someone edible, lets eat them instead - if(dinner == controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) - return can_see(hunter, dinner, radius) - - //We are so hungry, lets eat them - if(controller.blackboard[BB_SLIME_HUNGER_LEVEL] == SLIME_HUNGER_STARVING && controller.blackboard[BB_SLIME_RABID]) - return can_see(hunter, dinner, radius) - - //A bit pickier - if((islarva(dinner) || ismonkey(dinner)) || (ishuman(dinner) || isalienadult(dinner) && SPT_PROB(2.5, seconds_per_tick))) - return can_see(hunter, dinner, radius) - - //We are not THAT hungry - return FALSE - -/datum/ai_behavior/hunt_target/interact_with_target/slime - -/datum/ai_behavior/hunt_target/interact_with_target/slime/target_caught(mob/living/basic/slime/hunter, mob/living/hunted) +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/slime/target_caught(mob/living/basic/slime/hunter, mob/living/hunted) if (!hunter.can_feed_on(hunted)) // Target is no longer edible hunter.UnarmedAttack(hunted, TRUE) return @@ -72,9 +15,9 @@ hunter.start_feeding(hunted) -/datum/ai_behavior/hunt_target/interact_with_target/slime/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key) +/datum/bt_node/ai_behavior/hunt_target/interact_with_target/slime/finish_action(datum/ai_controller/controller, succeeded) . = ..() var/mob/living/basic/slime/slime_pawn = controller.pawn - var/atom/target = controller.blackboard[hunting_target_key] + var/atom/target = controller.blackboard[target_key] if(!slime_pawn.can_feed_on(target)) - controller.clear_blackboard_key(hunting_target_key) + controller.clear_blackboard_key(target_key) diff --git a/code/modules/mob/living/basic/slime/ai/controller.dm b/code/modules/mob/living/basic/slime/ai/controller.dm index 1fcf27af724b..badce00ce828 100644 --- a/code/modules/mob/living/basic/slime/ai/controller.dm +++ b/code/modules/mob/living/basic/slime/ai/controller.dm @@ -1,29 +1,16 @@ /datum/ai_controller/basic_controller/slime blackboard = list( BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/slime_food, BB_SLIME_RABID = FALSE, BB_SLIME_HUNGER_DISABLED = FALSE, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("Blorble...","Bzzt...",""), + BB_EMOTE_HEAR = list("blorbles."), + BB_EMOTE_SEE = list("lights up for a bit, then stops.","bounces in place.", "jiggles!","vibrates!"), + BB_SPEAK_CHANCE = 1, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/change_slime_face, - /datum/ai_planning_subtree/use_mob_ability/evolve, - /datum/ai_planning_subtree/use_mob_ability/reproduce, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/find_and_hunt_target/find_slime_food, - /datum/ai_planning_subtree/basic_melee_attack_subtree/slime, - /datum/ai_planning_subtree/random_speech/slime, - ) - -/datum/ai_controller/basic_controller/slime/CancelActions() - ..() - if(QDELETED(pawn)) - return - - var/mob/living/basic/slime/slime_pawn = pawn - slime_pawn.stop_feeding() + behavior_tree_json = "code/modules/mob/living/basic/slime/ai/slime.bt.json" diff --git a/code/modules/mob/living/basic/slime/ai/pet_command.dm b/code/modules/mob/living/basic/slime/ai/pet_command.dm index 4b50b2b32b9e..49ca02ce9a91 100644 --- a/code/modules/mob/living/basic/slime/ai/pet_command.dm +++ b/code/modules/mob/living/basic/slime/ai/pet_command.dm @@ -4,13 +4,9 @@ pointed_reaction = "and blorbles" refuse_reaction = "jiggles sadly" - var/hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/slime - /datum/pet_command/attack/slime/execute_action(datum/ai_controller/controller) - var/mob/living/basic/slime/slime_pawn = controller.pawn if(isslime(slime_pawn) && slime_pawn.can_feed_on(controller.blackboard[BB_CURRENT_PET_TARGET], check_friendship = TRUE)) - controller.queue_behavior(hunting_behavior, BB_CURRENT_PET_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, /datum/bt_node/subtree/pet_command/attack/slime) + return return ..() diff --git a/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.json b/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.json new file mode 100644 index 000000000000..580315b55113 --- /dev/null +++ b/code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.json @@ -0,0 +1,46 @@ +{ + "dm_type": "/datum/bt_node/subtree/pet_command/attack/slime", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_PET_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/feed_on_slime_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_PET_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + ] +} diff --git a/code/modules/mob/living/basic/slime/ai/slime.bt.json b/code/modules/mob/living/basic/slime/ai/slime.bt.json new file mode 100644 index 000000000000..c7415631708f --- /dev/null +++ b/code/modules/mob/living/basic/slime/ai/slime.bt.json @@ -0,0 +1,212 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/slime", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_SLIME_EVOLVE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_SLIME_EVOLVE" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "ability_key": "BB_SLIME_REPRODUCE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_SLIME_REPRODUCE" + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SLIME_EAT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/feed_on_slime_target", + "vars": { + "target_key": "BB_SLIME_EAT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_SLIME_EAT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SLIME_EAT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/feed_on_slime_target", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": false + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/slime_wants_to_eat", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SLIME_EAT_TARGET", + "targeting_strategy": "/datum/targeting_strategy/slime_food", + "target_source": "/datum/target_source/oview_living_no_slimes", + "vision_range": 7, + "must_be_reachable": true + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/change_slime_face" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/slime/ai/slime_bt.dm b/code/modules/mob/living/basic/slime/ai/slime_bt.dm new file mode 100644 index 000000000000..3d86ce2c3a0d --- /dev/null +++ b/code/modules/mob/living/basic/slime/ai/slime_bt.dm @@ -0,0 +1,87 @@ +/// Slime pet command attack: loops feed_on_slime_target toward BB_CURRENT_PET_TARGET. +/datum/bt_node/subtree/pet_command/attack/slime + behavior_tree_json = "code/modules/mob/living/basic/slime/ai/pet_command_attack_slime.bt.json" + +///give them the chud face if they dont feed us, basically select a nice face +/datum/bt_node/ai_behavior/change_slime_face + +/datum/bt_node/ai_behavior/change_slime_face/perform(seconds_per_tick, datum/ai_controller/controller) + if(!prob(5)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/mob/living/basic/slime/slime_pawn = controller.pawn + if(!istype(slime_pawn) || slime_pawn.stat) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/current_mood = slime_pawn.current_mood + var/new_mood + + if(controller.blackboard[BB_SLIME_RABID] || LAZYLEN(controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]) > 0) + new_mood = SLIME_MOOD_ANGRY + else if(controller.blackboard[BB_SLIME_HUNGER_DISABLED]) + new_mood = SLIME_MOOD_SMILE + else if(controller.blackboard[BB_CURRENT_TARGET]) + new_mood = SLIME_MOOD_MISCHIEVOUS + else + new_mood = pick(SLIME_MOOD_SAD, SLIME_MOOD_SMILE, SLIME_MOOD_POUT) + + if(current_mood != new_mood) + slime_pawn.current_mood = new_mood + slime_pawn.regenerate_icons() + + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/** + * Gate for slime food searching: passes only when the slime is unbuckled and is + * hungryor rabid. + */ +/datum/bt_node/decorator/slime_wants_to_eat + +/datum/bt_node/decorator/slime_wants_to_eat/check_condition(datum/ai_controller/controller) + var/mob/living/basic/slime/slime_pawn = controller.pawn + if(!istype(slime_pawn) || slime_pawn.buckled) + return FALSE + if(controller.blackboard[BB_SLIME_HUNGER_LEVEL] != SLIME_HUNGER_NONE) + return TRUE + if(controller.blackboard[BB_SLIME_RABID]) + return TRUE + return TRUE + + +///im about to eat this guy up +/datum/bt_node/ai_behavior/feed_on_slime_target + var/target_key + +/datum/bt_node/ai_behavior/feed_on_slime_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/basic/slime/slime_pawn = controller.pawn + if(!istype(slime_pawn)) //bro lmao comeon + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if(slime_pawn.buckled) + if(slime_pawn.buckled == target) //we got em boys + return AI_BEHAVIOR_DELAY + else + slime_pawn.stop_feeding() + return AI_BEHAVIOR_FAILED //epic fail; try again + + if(!slime_pawn.can_feed_on(target)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if((target.body_position != STANDING_UP) || prob(20)) + slime_pawn.start_feeding(target) + return AI_BEHAVIOR_DELAY + + if(target.client && target.health >= 20) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + slime_pawn.start_feeding(target) + return AI_BEHAVIOR_DELAY + + +/datum/bt_node/ai_behavior/feed_on_slime_target/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + var/mob/living/basic/slime/slime_pawn = controller.pawn + slime_pawn.stop_feeding() diff --git a/code/modules/mob/living/basic/slime/ai/subtrees.dm b/code/modules/mob/living/basic/slime/ai/subtrees.dm deleted file mode 100644 index 08886f98365a..000000000000 --- a/code/modules/mob/living/basic/slime/ai/subtrees.dm +++ /dev/null @@ -1,54 +0,0 @@ -/datum/ai_planning_subtree/use_mob_ability/evolve - ability_key = BB_SLIME_EVOLVE - -/datum/ai_planning_subtree/use_mob_ability/reproduce - ability_key = BB_SLIME_REPRODUCE - -//Handles the slime changing their facial overlays -/datum/ai_planning_subtree/change_slime_face - var/face_change_chance = 5 - -/datum/ai_planning_subtree/change_slime_face/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(face_change_chance, seconds_per_tick)) - return - - var/mob/living/basic/slime/slime_pawn = controller.pawn - if(!istype(slime_pawn)) - return - - if(slime_pawn.stat) //dead slimes make no smiles - return - - controller.queue_behavior(/datum/ai_behavior/perform_change_slime_face) - -// Slime subtree for hunting down people to drain -/datum/ai_planning_subtree/find_and_hunt_target/find_slime_food - finding_behavior = /datum/ai_behavior/find_hunt_target/find_slime_food - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/slime - hunt_targets = list(/mob/living) - hunt_range = 7 - -/datum/ai_planning_subtree/find_and_hunt_target/find_slime_food/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.buckled) - return - - //Slimes don't want to hunt if they are neither rabid, hungry or feeling attack right now - if( (controller.blackboard[BB_SLIME_HUNGER_LEVEL] == SLIME_HUNGER_NONE) && !controller.blackboard[BB_SLIME_RABID] && isnull(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET])) - return - - return ..() - -/datum/ai_planning_subtree/basic_melee_attack_subtree/slime - -/datum/ai_planning_subtree/basic_melee_attack_subtree/slime/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.buckled) - return - return ..() - -/datum/ai_planning_subtree/random_speech/slime - speech_chance = 1 - speak = list("Blorble...","Bzzt...","") - emote_hear = list("blorbles.") - emote_see = list("lights up for a bit, then stops.","bounces in place.", "jiggles!","vibrates!") diff --git a/code/modules/mob/living/basic/slime/defense.dm b/code/modules/mob/living/basic/slime/defense.dm index 598ba3a318ac..e746176a49b0 100644 --- a/code/modules/mob/living/basic/slime/defense.dm +++ b/code/modules/mob/living/basic/slime/defense.dm @@ -23,23 +23,23 @@ defender_slime.discipline_slime() -/mob/living/basic/slime/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) - +/mob/living/basic/slime/item_interaction(mob/living/user, obj/item/tool, list/modifiers) //Lets you feed slimes plasma. Checks before the passthrough force check - if(istype(attacking_item, /obj/item/stack/sheet/mineral/plasma) && stat == CONSCIOUS) - use_sheet(attacking_item, user) - return + if(istype(tool, /obj/item/stack/sheet/mineral/plasma) && !IS_UNCONSCIOUS_OR_CRIT(src)) + use_sheet(tool, user) + return ITEM_INTERACT_SUCCESS //Checks if the item passes through the slime first. Safe items can be used simply - if(check_item_passthrough(attacking_item, user)) - return + if(check_item_passthrough(tool, user)) + return ITEM_INTERACT_SUCCESS - try_discipline_slime(attacking_item) + try_discipline_slime(tool) - if(!istype(attacking_item, /obj/item/storage/bag/xeno)) + if(!istype(tool, /obj/item/storage/bag/xeno)) return ..() - use_xeno_bag(attacking_item, user) + use_xeno_bag(tool, user) + return ITEM_INTERACT_SUCCESS ///Checks if an item harmlessly passes through the slime @@ -110,7 +110,7 @@ /mob/living/basic/slime/proc/discipline_slime() stop_feeding(silent = TRUE) if(life_stage == SLIME_LIFE_STAGE_BABY && prob(80)) - ai_controller?.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + ai_controller?.clear_blackboard_key(BB_CURRENT_TARGET) ai_controller?.clear_blackboard_key(BB_CURRENT_HUNTING_TARGET) if(prob(10)) diff --git a/code/modules/mob/living/basic/slime/feeding.dm b/code/modules/mob/living/basic/slime/feeding.dm index 8c527891b8e6..284534ccbecb 100644 --- a/code/modules/mob/living/basic/slime/feeding.dm +++ b/code/modules/mob/living/basic/slime/feeding.dm @@ -7,9 +7,13 @@ if(!isliving(meal)) //sanity check return FALSE - if(stat != CONSCIOUS) - if(!silent) + if(IS_UNCONSCIOUS_OR_CRIT(src)) + if(stat == DEAD) + balloon_alert(src, "dead!") + else if(IS_UNCONSCIOUS(src)) balloon_alert(src, "unconscious!") + else + balloon_alert(src, "in critical!") return FALSE if(hunger_disabled) diff --git a/code/modules/mob/living/basic/snails/snail.bt.json b/code/modules/mob/living/basic/snails/snail.bt.json new file mode 100644 index 000000000000..9ba7932aa2a3 --- /dev/null +++ b/code/modules/mob/living/basic/snails/snail.bt.json @@ -0,0 +1,59 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/snail", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_SNAIL_RETREAT_ABILITY" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + } + ] +} diff --git a/code/modules/mob/living/basic/snails/snail_ai.dm b/code/modules/mob/living/basic/snails/snail_ai.dm index 73c48d198097..03f58bfc3264 100644 --- a/code/modules/mob/living/basic/snails/snail_ai.dm +++ b/code/modules/mob/living/basic/snails/snail_ai.dm @@ -1,68 +1,13 @@ /datum/ai_controller/basic_controller/snail + behavior_tree_json = "code/modules/mob/living/basic/snails/snail.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/use_mob_ability/snail_retreat, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/snail_people, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/snail_people - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/snail_people - hunting_behavior = /datum/ai_behavior/hunt_target/snail_people - hunt_targets = list( - /mob/living/carbon, - ) - hunt_range = 5 - hunt_chance = 45 - -/datum/ai_behavior/find_hunt_target/snail_people - action_cooldown = 1 MINUTES - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_hunt_target/snail_people/valid_dinner(mob/living/source, mob/living/carbon/potential_snail, radius, datum/ai_controller/controller, seconds_per_tick) - if(!istype(potential_snail)) - return FALSE - if(potential_snail.stat != CONSCIOUS) - return FALSE - if(!is_species(potential_snail, /datum/species/snail)) - return FALSE - return can_see(source, potential_snail, radius) - -/datum/ai_behavior/hunt_target/snail_people - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/snail_people/target_caught(mob/living/hunter, atom/hunted) - hunter.manual_emote("Celebrates around [hunted]!") - hunter.SpinAnimation(speed = 1, loops = 3) - -/datum/ai_planning_subtree/use_mob_ability/snail_retreat - ability_key = BB_SNAIL_RETREAT_ABILITY - finish_planning = TRUE - -/datum/ai_planning_subtree/use_mob_ability/snail_retreat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/shell_retreated = HAS_TRAIT(controller.pawn, TRAIT_SHELL_RETREATED) - var/has_target = controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) - if((has_target && shell_retreated) || (!has_target && !shell_retreated)) - return - return ..() - /datum/ai_controller/basic_controller/snail/trash blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, ) - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/space_fauna/ant.bt.json b/code/modules/mob/living/basic/space_fauna/ant.bt.json new file mode 100644 index 000000000000..352c8e63fcc5 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/ant.bt.json @@ -0,0 +1,38 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/ant", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/ant.dm b/code/modules/mob/living/basic/space_fauna/ant.dm index 97633a705801..7a0265989029 100644 --- a/code/modules/mob/living/basic/space_fauna/ant.dm +++ b/code/modules/mob/living/basic/space_fauna/ant.dm @@ -55,16 +55,9 @@ AddElement(/datum/element/basic_allergenic_attack, allergen = BUGS, allergen_chance = 20, histamine_add = 5) /datum/ai_controller/basic_controller/ant + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/ant.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/ant, - ) diff --git a/code/modules/mob/living/basic/space_fauna/bear/bear.bt.json b/code/modules/mob/living/basic/space_fauna/bear/bear.bt.json new file mode 100644 index 000000000000..d4b638746fff --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/bear/bear.bt.json @@ -0,0 +1,215 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/bear", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/climb_tree" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_FOUND_HONEY", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FOUND_HONEY", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/find_hive", + "vars": { + "target_key": "BB_FOUND_HONEY", + "cooldown_key": "BB_BEAR_HIVE_COOLDOWN", + "hunt_cooldown": "5 SECONDS" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_dragging", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/pull_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_BEAR_HONEYCOMB_COOLDOWN", + "hunt_cooldown": "5 SECONDS" + } + } + ] + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CLIMBED_TREE", + "target_source": "/datum/target_source/oview_single_type/flora_tree", + "targeting_strategy": "/datum/targeting_strategy/anything" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_BEAR_HIVE_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOUND_HONEY", + "target_source": "/datum/target_source/oview_single_type/beehive", + "targeting_strategy": "/datum/targeting_strategy/stocked_beehive", + "vision_range": 10 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_dragging", + "vars": { + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_BEAR_HONEYCOMB_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/honeycomb", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "vision_range": 10 + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/bear" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/bear/bear_ai_behavior.dm b/code/modules/mob/living/basic/space_fauna/bear/bear_ai_behavior.dm index 7c57349524fe..9d4a2a7e8cda 100644 --- a/code/modules/mob/living/basic/space_fauna/bear/bear_ai_behavior.dm +++ b/code/modules/mob/living/basic/space_fauna/bear/bear_ai_behavior.dm @@ -1,27 +1,7 @@ -/datum/ai_behavior/find_hunt_target/find_hive - -/datum/ai_behavior/find_hunt_target/find_hive/valid_dinner(mob/living/source, obj/structure/beebox/hive, radius) - if(!length(hive.honeycombs)) - return FALSE - return can_see(source, hive, radius) - -/datum/ai_behavior/hunt_target/find_hive +/// Raids a beehive once in range, extracting its honeycombs. +/datum/bt_node/ai_behavior/hunt_target/find_hive always_reset_target = TRUE -/datum/ai_behavior/hunt_target/find_hive/target_caught(mob/living/hunter, obj/structure/beebox/hive_target) +/datum/bt_node/ai_behavior/hunt_target/find_hive/target_caught(mob/living/hunter, obj/structure/beebox/hive_target) var/datum/callback/callback = CALLBACK(hunter, TYPE_PROC_REF(/mob/living/basic/bear, extract_combs), hive_target) callback.Invoke() - -/datum/ai_behavior/find_hunt_target/find_honeycomb - -/datum/ai_behavior/find_hunt_target/find_honeycomb/setup(datum/ai_controller/controller, ability_key, target_key) - var/mob/living/living_pawn = controller.pawn - if(living_pawn.pulling) //we already pulling a honey - return FALSE - return TRUE - -/datum/ai_behavior/hunt_target/find_honeycomb - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/find_honeycomb/target_caught(mob/living/hunter, obj/item/food/honeycomb/food_target) - hunter.start_pulling(food_target) diff --git a/code/modules/mob/living/basic/space_fauna/bear/bear_ai_subtree.dm b/code/modules/mob/living/basic/space_fauna/bear/bear_ai_subtree.dm index 1f45576b1473..f6f900452dd3 100644 --- a/code/modules/mob/living/basic/space_fauna/bear/bear_ai_subtree.dm +++ b/code/modules/mob/living/basic/space_fauna/bear/bear_ai_subtree.dm @@ -4,28 +4,4 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/climb_trees, - /datum/ai_planning_subtree/find_and_hunt_target/find_hive, - /datum/ai_planning_subtree/find_and_hunt_target/find_honeycomb, - /datum/ai_planning_subtree/random_speech/bear, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/find_hive - target_key = BB_FOUND_HONEY - hunting_behavior = /datum/ai_behavior/hunt_target/find_hive - finding_behavior = /datum/ai_behavior/find_hunt_target/find_hive - hunt_targets = list(/obj/structure/beebox) - hunt_range = 10 - -/datum/ai_planning_subtree/find_and_hunt_target/find_honeycomb - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/find_honeycomb - finding_behavior = /datum/ai_behavior/find_hunt_target/find_honeycomb - hunt_targets = list(/obj/item/food/honeycomb) - hunt_range = 10 + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/bear/bear.bt.json" diff --git a/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.json b/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.json new file mode 100644 index 000000000000..d3b7e8ecd671 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.json @@ -0,0 +1,109 @@ +{ + "dm_type": "/datum/bt_node/subtree/basic_carp_tree", + "bindings": { + "be5c4p10": { + "label": "escape_tree", + "default": "/datum/bt_node/subtree" + }, + "bf1nd7r2": { + "label": "find_targets_tree", + "default": "/datum/bt_node/subtree/carp_target_selection" + }, + "bc0mb4t9": { + "label": "combat_tree", + "default": "/datum/bt_node/subtree/carp_combat" + }, + "bid1e6k3": { + "label": "idle_tree", + "default": "/datum/bt_node/subtree/carp_migration" + }, + "b7cm2s3z": { + "label": "flee_from_key", + "default": "BB_CURRENT_TARGET" + } + }, + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "$be5c4p10" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/carp_flee", + "bindings": { + "bf3k9q2p": "$b7cm2s3z" + } + }, + { + "type": "subtree", + "subtype": "$bc0mb4t9" + } + ] + } + }, + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "$bid1e6k3" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "$bf1nd7r2" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MAGICARP_SPELL" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_magicarp_spell_target", + "vars": { + "ability_key": "BB_MAGICARP_SPELL", + "target_key": "BB_MAGICARP_SPELL_TARGET", + "targeting_strategy_key": "BB_TARGETING_STRATEGY" + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp.bt.json new file mode 100644 index 000000000000..33c298f45493 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/carp", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_carp_tree", + "bindings": { + "be5c4p10": "/datum/bt_node/subtree/escape_captivity" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index f04fa3aea99d..be19c0bd3e90 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -156,8 +156,8 @@ /// Gives the carp a list of weakrefs of destinations to try and travel between when it has nothing better to do /mob/living/basic/carp/proc/migrate_to(list/datum/weakref/migration_points) - ai_controller.can_idle = FALSE - ai_controller.set_ai_status(AI_STATUS_ON) // We need htem to actually walk to the station + ai_controller.ai_traits |= RUN_WHILE_UNWATCHED + ai_controller.reset_ai_status() // We need them to actually keep walking to the station var/list/actual_points = list() for(var/datum/weakref/point_ref as anything in migration_points) var/turf/point_resolved = point_ref.resolve() diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm index b2fcbf0f78d4..22f06da636b8 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm @@ -137,7 +137,7 @@ if (isliving(entered_atom)) var/mob/living/teleported_mob = entered_atom teleported_mob.changeNext_move(disorient_time) - teleported_mob.ai_controller?.CancelActions() + teleported_mob.ai_controller?.cancel_current_plan() var/turf/destination = pick(exit_locs) do_teleport(entered_atom, destination, channel = TELEPORT_CHANNEL_MAGIC) diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm index 26894850b198..d8beb9deddea 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm @@ -1,70 +1,144 @@ +/// How far away the magicarp looks for a special spell target #define MAGICARP_SPELL_TARGET_SEEK_RANGE 4 +/// How far away the magicarp looks for a regular spell target +#define MAGICARP_SPELL_ENEMY_SEEK_RANGE 9 /datum/pet_command/use_ability/magicarp pet_ability_key = BB_MAGICARP_SPELL -/datum/ai_planning_subtree/attack_obstacle_in_path/carp - attack_behaviour = /datum/ai_behavior/attack_obstructions/carp - -/datum/ai_behavior/attack_obstructions/carp - action_cooldown = 1.5 SECONDS +/** + * # Carp should flee + * Gates the flee/panic-teleport block. A carp flees from its flee target if that target is a feared + * fisherman, or if it is otherwise allowed to flee (i.e. it's injured, which clears BB_BASIC_MOB_STOP_FLEEING). + */ +/datum/bt_node/decorator/carp_should_flee + /// Blackboard key holding the thing we'd run away from + var/target_key = BB_BASIC_MOB_FLEE_TARGET -/// As basic attack tree but interrupt if your health gets low or if your spell is off cooldown -/datum/ai_planning_subtree/basic_melee_attack_subtree/magicarp - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/magicarp +/datum/bt_node/decorator/carp_should_flee/check_condition(datum/ai_controller/controller) + var/atom/flee_from = controller.blackboard[target_key] + if(QDELETED(flee_from)) + return FALSE + if(controller.blackboard[BB_CARPS_FEAR_FISHERMAN] && HAS_TRAIT(flee_from, TRAIT_SCARY_FISHERMAN)) + return TRUE + return !controller.blackboard[BB_BASIC_MOB_STOP_FLEEING] -/// Interrupt your attack chain if: you have a spell, it's not on cooldown, and it has a target -/datum/ai_behavior/basic_melee_attack/magicarp +/datum/bt_node/decorator/carp_should_flee/register_observe_signals(atom/pawn) + RegisterSignals(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(target_key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), + COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_STOP_FLEEING), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BASIC_MOB_STOP_FLEEING), + ), PROC_REF(on_signal_changed)) + return TRUE -/datum/ai_behavior/basic_melee_attack/magicarp/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key, health_ratio_key) - var/datum/action/cooldown/using_action = controller.blackboard[BB_MAGICARP_SPELL] - if (QDELETED(using_action)) - return ..() - if (!controller.blackboard[BB_MAGICARP_SPELL_SPECIAL_TARGETING] && using_action.IsAvailable()) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - return ..() +/datum/bt_node/decorator/carp_should_flee/unregister_observe_signals(atom/pawn) + UnregisterSignal(pawn, list( + COMSIG_AI_BLACKBOARD_KEY_SET(target_key), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), + COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_STOP_FLEEING), + COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BASIC_MOB_STOP_FLEEING), + )) /** - * Find a target for the magicarp's spell - * This gets weird because different spells want different targeting - * but I didn't want a new ai controller for every different spell + * # Find magicarp spell target + * Finds a target for the magicarp's spell. Different spells want different targeting, so rather than make a + * controller per spell we branch on BB_MAGICARP_SPELL_SPECIAL_TARGETING here. Only runs if the spell is ready. */ -/datum/ai_planning_subtree/find_nearest_magicarp_spell_target +/datum/bt_node/ai_behavior/find_magicarp_spell_target + /// Blackboard key holding the spell we're trying to target + var/ability_key = BB_MAGICARP_SPELL + /// Blackboard key we store the chosen spell target in + var/target_key = BB_MAGICARP_SPELL_TARGET + /// Blackboard key holding our targeting strategy for the default case + var/targeting_strategy_key = BB_TARGETING_STRATEGY + /// Blackboard key describing any special targeting this spell wants + var/special_targeting_key = BB_MAGICARP_SPELL_SPECIAL_TARGETING -/datum/ai_planning_subtree/find_nearest_magicarp_spell_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/action/cooldown/using_action = controller.blackboard[BB_MAGICARP_SPELL] - if (!using_action?.IsAvailable()) - return +/datum/bt_node/ai_behavior/find_magicarp_spell_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/datum/action/cooldown/using_action = controller.blackboard[ability_key] + if(!using_action?.IsAvailable()) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + + var/atom/found + switch(controller.blackboard[special_targeting_key]) + if(MAGICARP_SPELL_CORPSES) + found = find_friendly_corpse(controller) + if(MAGICARP_SPELL_OBJECTS) + found = find_animatable(controller) + if(MAGICARP_SPELL_WALLS) + found = find_nearest_wall(controller) + else + found = find_nearest_enemy(controller) + + if(isnull(found)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - var/spell_targeting = controller.blackboard[BB_MAGICARP_SPELL_SPECIAL_TARGETING] - if (!spell_targeting) - controller.queue_behavior(/datum/ai_behavior/find_potential_targets/nearest/magicarp, BB_MAGICARP_SPELL_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return + controller.set_blackboard_key(target_key, found) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED - switch(spell_targeting) - if (MAGICARP_SPELL_CORPSES) - controller.queue_behavior(/datum/ai_behavior/find_and_set/friendly_corpses, BB_MAGICARP_SPELL_TARGET, MAGICARP_SPELL_TARGET_SEEK_RANGE) - return - if (MAGICARP_SPELL_OBJECTS) - controller.queue_behavior(/datum/ai_behavior/find_and_set/animatable, BB_MAGICARP_SPELL_TARGET, MAGICARP_SPELL_TARGET_SEEK_RANGE) - return - if (MAGICARP_SPELL_WALLS) - controller.queue_behavior(/datum/ai_behavior/find_and_set/nearest_wall, BB_MAGICARP_SPELL_TARGET, MAGICARP_SPELL_TARGET_SEEK_RANGE) - return +/// Nearest valid combat target which isn't a scary fisherman (default spell targeting) +/datum/bt_node/ai_behavior/find_magicarp_spell_target/proc/find_nearest_enemy(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/datum/targeting_strategy/strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) + if(!strategy) + return null + var/list/candidates = list() + for(var/mob/living/candidate in oview(MAGICARP_SPELL_ENEMY_SEEK_RANGE, living_pawn)) + if(HAS_TRAIT(candidate, TRAIT_SCARY_FISHERMAN)) + continue + if(!strategy.is_valid_target(living_pawn, candidate, MAGICARP_SPELL_ENEMY_SEEK_RANGE, controller)) + continue + candidates += candidate + if(!length(candidates)) + return null + return get_closest_atom(/mob/living, candidates, living_pawn) -/// This subtype only exists because if you queue multiple of the same action with different arguments it deletes their stored arguments -/datum/ai_behavior/find_potential_targets/nearest/magicarp +/// An object or structure we could animate with a staff of change +/datum/bt_node/ai_behavior/find_magicarp_spell_target/proc/find_animatable(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/nearby_items = list() + for(var/obj/new_friend in oview(MAGICARP_SPELL_TARGET_SEEK_RANGE, living_pawn)) + if(!isitem(new_friend) && !isstructure(new_friend)) + continue + if(is_type_in_list(new_friend, GLOB.animatable_blacklist)) + continue + if(living_pawn.see_invisible < new_friend.invisibility) + continue + nearby_items += new_friend + if(length(nearby_items)) + return pick(nearby_items) + return null -/datum/ai_behavior/find_potential_targets/nearest/magicarp/pick_final_target(datum/ai_controller/controller, list/enemies_list) - for(var/atom/atom as anything in enemies_list) - if(HAS_TRAIT(atom, TRAIT_SCARY_FISHERMAN)) - enemies_list -= atom - return ..() +/// The nearest wall which isn't invulnerable +/datum/bt_node/ai_behavior/find_magicarp_spell_target/proc/find_nearest_wall(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/nearby_walls = list() + for(var/turf/closed/new_wall in oview(MAGICARP_SPELL_TARGET_SEEK_RANGE, living_pawn)) + if(isindestructiblewall(new_wall)) + continue + nearby_walls += new_wall + if(length(nearby_walls)) + return get_closest_atom(/turf/closed, nearby_walls, living_pawn) + return null -/// Then use it on that target -/datum/ai_planning_subtree/targeted_mob_ability/magicarp - ability_key = BB_MAGICARP_SPELL - target_key = BB_MAGICARP_SPELL_TARGET - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/and_clear_target +/// A corpse who shares our faction, for resurrection spells +/datum/bt_node/ai_behavior/find_magicarp_spell_target/proc/find_friendly_corpse(datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + var/list/nearby_bodies = list() + for(var/mob/living/dead_pal in oview(MAGICARP_SPELL_TARGET_SEEK_RANGE, living_pawn)) + if(!isturf(dead_pal.loc)) + continue + if(!dead_pal.stat || dead_pal.health > 0) + continue + if(living_pawn.see_invisible < dead_pal.invisibility) + continue + if(!living_pawn.faction_check_atom(dead_pal)) + continue + nearby_bodies += dead_pal + if(length(nearby_bodies)) + return pick(nearby_bodies) + return null #undef MAGICARP_SPELL_TARGET_SEEK_RANGE +#undef MAGICARP_SPELL_ENEMY_SEEK_RANGE diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm index 9458877af7fe..b65456f16d79 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_migration.dm @@ -1,46 +1,17 @@ /// How close you need to get to the destination in order to consider yourself there #define CARP_DESTINATION_SEARCH_RANGE 3 -/// If there's a portal this close to us we'll enter it just on the basis that the carp who made it probably knew where they were going -#define CARP_PORTAL_SEARCH_RANGE 2 - -/** - * # Carp Migration - * Will try to plan a path between a list of locations for carp to travel through - */ -/datum/ai_planning_subtree/carp_migration - -/datum/ai_planning_subtree/carp_migration/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - // If there's a rift nearby take a ride, then cancel everything else because it's not valid any more - for(var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift in orange(controller.pawn, CARP_PORTAL_SEARCH_RANGE)) - controller.queue_behavior(/datum/ai_behavior/travel_towards_atom, get_turf(rift)) - return SUBTREE_RETURN_FINISH_PLANNING - - // We have a destination, try to approach it - var/turf/moving_to = controller.blackboard[BB_CARP_MIGRATION_TARGET] - if(!isnull(moving_to)) - var/turf/next_step = get_step_towards(controller.pawn, moving_to) - // Attempt to teleport around if we're blocked - if(next_step.is_blocked_turf(exclude_mobs = TRUE)) - controller.queue_behavior(/datum/ai_behavior/make_carp_rift/towards/unvalidated, BB_CARP_RIFT, BB_CARP_MIGRATION_TARGET) - controller.queue_behavior(/datum/ai_behavior/attack_obstructions/carp, BB_CARP_MIGRATION_TARGET) - controller.queue_behavior(/datum/ai_behavior/step_towards_turf, BB_CARP_MIGRATION_TARGET) - // We've gotten close enough to it, clear it so we can select a new point (or do nothing) - if(get_dist(controller.pawn, moving_to) <= CARP_DESTINATION_SEARCH_RANGE) - controller.clear_blackboard_key(BB_CARP_MIGRATION_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - // We have a path to follow but no destination, select one - if(length(controller.blackboard[BB_CARP_MIGRATION_PATH])) - controller.queue_behavior(/datum/ai_behavior/find_next_carp_migration_step, BB_CARP_MIGRATION_PATH, BB_CARP_MIGRATION_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING /** * # Find next carp migration step * Records the next turf we want to travel to into the blackboard for other actions */ -/datum/ai_behavior/find_next_carp_migration_step +/datum/bt_node/ai_behavior/find_next_carp_migration_step + /// Blackboard key holding the list of turfs to migrate between + var/path_key = BB_CARP_MIGRATION_PATH + /// Blackboard key in which we record our next destination + var/target_key = BB_CARP_MIGRATION_TARGET -/datum/ai_behavior/find_next_carp_migration_step/perform(seconds_per_tick, datum/ai_controller/controller, path_key, target_key) +/datum/bt_node/ai_behavior/find_next_carp_migration_step/perform(seconds_per_tick, datum/ai_controller/controller) var/list/blackboard_points = controller.blackboard[path_key] for(var/turf/migration_point as anything in blackboard_points) // By the end of this loop we will either have a valid migration point set, or an empty list in our blackboard @@ -50,5 +21,30 @@ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED +/** + * # Clear arrived migration target + * Clears our migration target once we've gotten close enough to it, so a new one can be selected. + */ +/datum/bt_node/ai_behavior/clear_arrived_migration_target + /// Blackboard key holding our migration destination + var/target_key = BB_CARP_MIGRATION_TARGET + +/datum/bt_node/ai_behavior/clear_arrived_migration_target/perform(seconds_per_tick, datum/ai_controller/controller) + var/turf/moving_to = controller.blackboard[target_key] + if(QDELETED(moving_to) || get_dist(controller.pawn, moving_to) <= CARP_DESTINATION_SEARCH_RANGE) + controller.clear_blackboard_key(target_key) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + +/// Returns TRUE if the next step towards the keyed turf is blocked, so we can smash or teleport through it. +/datum/bt_node/decorator/carp_path_blocked + /// Blackboard key holding the turf we're trying to reach + var/target_key = BB_CARP_MIGRATION_TARGET + +/datum/bt_node/decorator/carp_path_blocked/check_condition(datum/ai_controller/controller) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + var/turf/next_step = get_step_towards(controller.pawn, target) + return next_step?.is_blocked_turf(exclude_mobs = TRUE) + #undef CARP_DESTINATION_SEARCH_RANGE -#undef CARP_PORTAL_SEARCH_RANGE diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm index 69890c7bb2f5..13f76f972aa3 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm @@ -1,112 +1,77 @@ /** * # Make carp rift - * Plan a carp rift action, so basically teleport somewhere if the action is available + * Use the carp rift ability to teleport somewhere relative to a target. */ -/datum/ai_planning_subtree/make_carp_rift - /// Chiefly describes where we are placing this teleport - var/datum/ai_behavior/rift_behaviour - /// If true we finish planning after this - var/finish_planning = FALSE - /// Key to read for flee target - var/target_key = BB_BASIC_MOB_CURRENT_TARGET - -/datum/ai_planning_subtree/make_carp_rift/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!rift_behaviour) - CRASH("Forgot to specify rift behaviour for [src]") - - if (!controller.blackboard_key_exists(target_key)) - return - var/datum/action/cooldown/using_action = controller.blackboard[BB_CARP_RIFT] - if (!using_action?.IsAvailable()) - return - - controller.queue_behavior(rift_behaviour, BB_CARP_RIFT, target_key) - if (finish_planning) - return SUBTREE_RETURN_FINISH_PLANNING - -/** - * # Make carp rift (panic) - * Plan to teleport away from our target so they can't fuck us up - */ -/datum/ai_planning_subtree/make_carp_rift/panic_teleport - rift_behaviour = /datum/ai_behavior/make_carp_rift/away - finish_planning = TRUE - -/datum/ai_planning_subtree/make_carp_rift/panic_teleport/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/movable/fleeing_from = controller.blackboard[target_key] - if(!QDELETED(fleeing_from) && controller.blackboard[BB_CARPS_FEAR_FISHERMAN] && HAS_TRAIT(fleeing_from, TRAIT_SCARY_FISHERMAN)) - return ..() - if (controller.blackboard[BB_BASIC_MOB_STOP_FLEEING]) - return - return ..() - -/datum/ai_planning_subtree/make_carp_rift/panic_teleport/flee_key - target_key = BB_BASIC_MOB_FLEE_TARGET - - -/** - * # Make carp rift (aggressive) - * Plan to teleport towards our target so we can fuck them up - */ -/datum/ai_planning_subtree/make_carp_rift/aggressive_teleport - rift_behaviour = /datum/ai_behavior/make_carp_rift/towards/aggressive - -/datum/ai_planning_subtree/make_carp_rift/aggressive_teleport/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/movable/target = controller.blackboard[target_key] - if(!QDELETED(target) && controller.blackboard[BB_CARPS_FEAR_FISHERMAN] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN)) - return - return ..() - -/** - * # Make carp rift - * Make a carp rift somewhere - */ -/datum/ai_behavior/make_carp_rift +/datum/bt_node/ai_behavior/make_carp_rift + /// Blackboard key holding the rift ability + var/ability_key = BB_CARP_RIFT + /// Blackboard key holding the atom we're teleporting relative to + var/target_key + /// Rift destination snapshotted in perform(), for perform_async() to read. + VAR_PRIVATE/turf/rift_destination + +/datum/bt_node/ai_behavior/make_carp_rift/setup(datum/ai_controller/controller) + . = ..() + if(!controller.blackboard[ability_key] || !controller.blackboard[target_key]) + return FALSE -/datum/ai_behavior/make_carp_rift/setup(datum/ai_controller/controller, ability_key, target_key) - return controller.blackboard[ability_key] && controller.blackboard[target_key] +/datum/bt_node/ai_behavior/make_carp_rift/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags -/datum/ai_behavior/make_carp_rift/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = controller.blackboard[ability_key] var/atom/target = controller.blackboard[target_key] - if (!validate_target(controller, target, ability)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + // Fail INSTANT (not DELAY) so a selector falls through to melee/obstacles when we can't teleport, + // rather than latching on us while our cooldown ticks down. + if(!validate_target(controller, target, ability)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/turf/target_destination = find_target_turf(controller, target, ability) - if (!target_destination) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!target_destination) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - if(ability.InterceptClickOn(controller.pawn, null, target_destination)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + rift_destination = target_destination + return start_async() + +/datum/bt_node/ai_behavior/make_carp_rift/perform_async(datum/ai_controller/controller) + var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = controller.blackboard[ability_key] + var/result = ability.InterceptClickOn(controller.pawn, null, rift_destination) + if(!async_still_valid()) + return + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) + +/datum/bt_node/ai_behavior/make_carp_rift/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + rift_destination = null /// Return true if your target is valid for the action -/datum/ai_behavior/make_carp_rift/proc/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) - if (!ability) +/datum/bt_node/ai_behavior/make_carp_rift/proc/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) + if(!ability) return FALSE - if (!target) + if(!target) return FALSE return TRUE /// Return the turf to teleport to, implement this or the behaviour won't do anything -/datum/ai_behavior/make_carp_rift/proc/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) +/datum/bt_node/ai_behavior/make_carp_rift/proc/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) CRASH("Called unimplemented target finding proc on carp rift behaviour") /** * # Make carp rift away * Make a rift bringing you further away from your target */ -/datum/ai_behavior/make_carp_rift/away +/datum/bt_node/ai_behavior/make_carp_rift/away -/datum/ai_behavior/make_carp_rift/away/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) +/datum/bt_node/ai_behavior/make_carp_rift/away/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) return get_ranged_target_turf_direct(controller.pawn, target, range = ability.max_range, offset = 180) /** * # Make carp rift forwards * Make a rift bringing you closer to your target */ -/datum/ai_behavior/make_carp_rift/towards +/datum/bt_node/ai_behavior/make_carp_rift/towards /// Drop rift at least this many tiles away from target var/teleport_buffer_distance = 0 /// Teleport simply if you are far away @@ -114,44 +79,44 @@ /// Teleport if the turf in front of you is blocked var/teleport_if_blocked = TRUE -/datum/ai_behavior/make_carp_rift/towards/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) +/datum/bt_node/ai_behavior/make_carp_rift/towards/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) . = ..() - if (!.) + if(!.) return FALSE - if (teleport_if_far) + if(teleport_if_far) var/distance = get_dist(get_turf(controller.pawn), get_turf(target)) - if (distance >= ability.max_range + teleport_buffer_distance) // Perform if we are far away + if(distance >= ability.max_range + teleport_buffer_distance) // Perform if we are far away return TRUE - if (teleport_if_blocked) + if(teleport_if_blocked) var/turf/next_move = get_step_towards(controller.pawn, target) - if (next_move.is_blocked_turf(exclude_mobs = TRUE)) // Perform if target is behind cover + if(next_move.is_blocked_turf(exclude_mobs = TRUE)) // Perform if target is behind cover return TRUE return FALSE -/datum/ai_behavior/make_carp_rift/towards/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) +/datum/bt_node/ai_behavior/make_carp_rift/towards/find_target_turf(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) var/turf/target_turf = get_turf(target) var/distance = get_dist(get_turf(controller.pawn), target_turf) var/turf/chosen_turf - if (distance <= ability.max_range) + if(distance <= ability.max_range) chosen_turf = target_turf else var/run_direction = get_dir(controller.pawn, get_step_towards(controller.pawn, target_turf)) - chosen_turf = get_ranged_target_turf(controller.pawn, run_direction, ability.max_range) + chosen_turf = get_ranged_target_turf(controller.pawn, run_direction, ability.max_range) - if (!chosen_turf) + if(!chosen_turf) return // Subtract some distance so we don't drop carp directly on top of someone var/rift_to_target_distance = get_dist(target_turf, chosen_turf) - if (rift_to_target_distance < teleport_buffer_distance) + if(rift_to_target_distance < teleport_buffer_distance) var/away_direction = get_dir(controller.pawn, get_step_away(controller.pawn, target_turf)) - var/turf/backed_away_turf = get_ranged_target_turf(controller.pawn, away_direction, teleport_buffer_distance - rift_to_target_distance) - if (distance > get_dist(backed_away_turf, target_turf)) + var/turf/backed_away_turf = get_ranged_target_turf(controller.pawn, away_direction, teleport_buffer_distance - rift_to_target_distance) + if(distance > get_dist(backed_away_turf, target_turf)) chosen_turf = backed_away_turf // Avoid edge case pointless teleports from being up against a wall return chosen_turf @@ -160,48 +125,68 @@ * # Make carp rift forwards (aggressive) * Make a rift towards your target if you are blocked from moving or if it is far away */ -/datum/ai_behavior/make_carp_rift/towards/aggressive +/datum/bt_node/ai_behavior/make_carp_rift/towards/aggressive teleport_buffer_distance = 1 // Don't aggressively drop carps directly on top of a target mob /** * # Make carp rift forwards (unvalidated) - * Skip validation checks because we already did them in the controller + * Skip validation checks because we already did them elsewhere */ -/datum/ai_behavior/make_carp_rift/towards/unvalidated +/datum/bt_node/ai_behavior/make_carp_rift/towards/unvalidated -/datum/ai_behavior/make_carp_rift/towards/unvalidated/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) +/datum/bt_node/ai_behavior/make_carp_rift/towards/unvalidated/validate_target(datum/ai_controller/controller, atom/target, datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability) return TRUE -/datum/ai_behavior/make_carp_rift/towards/unvalidated/finish_action(datum/ai_controller/controller, succeeded, ...) +/datum/bt_node/ai_behavior/make_carp_rift/towards/unvalidated/finish_action(datum/ai_controller/controller, succeeded) . = ..() - if (succeeded) - controller.CancelActions() + if(succeeded) + controller.cancel_current_plan() /** - * # Shortcut to target through carp rift - * If there's a carp rift heading your way, plan to ride it to your target + * # Find carp rift shortcut + * If there's a carp rift heading towards our target, record its turf so we can ride it there. */ -/datum/ai_planning_subtree/shortcut_to_target_through_carp_rift +/datum/bt_node/ai_behavior/find_carp_rift_shortcut + /// Blackboard key holding our current target + var/target_key = BB_CURRENT_TARGET + /// Blackboard key in which we store the rift turf to travel to + var/destination_key = BB_CARP_RIFT_DESTINATION /// How far away do we look for rifts? var/search_distance = 3 - /// Minimum distance we should be from the target before we bother performing this action + /// Minimum distance we should be from the target before we bother var/minimum_distance = 2 -/datum/ai_planning_subtree/shortcut_to_target_through_carp_rift/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if (QDELETED(target) || (controller.blackboard[BB_CARPS_FEAR_FISHERMAN] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN))) - return +/datum/bt_node/ai_behavior/find_carp_rift_shortcut/perform(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/target = controller.blackboard[target_key] + if(QDELETED(target) || (controller.blackboard[BB_CARPS_FEAR_FISHERMAN] && HAS_TRAIT(target, TRAIT_SCARY_FISHERMAN))) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED var/distance_to_target = get_dist(controller.pawn, target) - if (distance_to_target <= minimum_distance) - return + if(distance_to_target <= minimum_distance) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - for (var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift in orange(controller.pawn, search_distance)) + for(var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift in orange(controller.pawn, search_distance)) var/exit_count = length(rift.exit_locs) - if (!exit_count) + if(!exit_count) continue var/turf/rift_exit = rift.exit_locs[exit_count] - if ((get_dist(rift_exit, target) + get_dist(rift, target)) >= distance_to_target) + if((get_dist(rift_exit, target) + get_dist(rift, target)) >= distance_to_target) continue - controller.queue_behavior(/datum/ai_behavior/travel_towards_atom, rift) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_blackboard_key(destination_key, get_turf(rift)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + +/** + * # Find nearby carp rift + * Record the turf of any nearby rift entrance so we can ride it; the carp who made it probably knew where they were going. + */ +/datum/bt_node/ai_behavior/find_carp_rift_shortcut/nearby + search_distance = 2 + minimum_distance = 0 + +/datum/bt_node/ai_behavior/find_carp_rift_shortcut/nearby/perform(seconds_per_tick, datum/ai_controller/controller) + for(var/obj/effect/temp_visual/lesser_carp_rift/entrance/rift in orange(controller.pawn, search_distance)) + controller.set_blackboard_key(destination_key, get_turf(rift)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.json new file mode 100644 index 000000000000..08a609642141 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.json @@ -0,0 +1,85 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_combat", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_MAGICARP_SPELL_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_MAGICARP_SPELL" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability/and_clear_target", + "vars": { + "ability_key": "BB_MAGICARP_SPELL", + "target_key": "BB_MAGICARP_SPELL_TARGET" + } + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "time_between_perform": "1.5 SECONDS" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_CARP_RIFT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/make_carp_rift/towards/aggressive", + "vars": { + "ability_key": "BB_CARP_RIFT", + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm index 35a9e6d7f517..a99ca6e1e493 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm @@ -1,11 +1,9 @@ /** * AI controller for carp * Expected flow is: - * * If health is low, mark that we want to run away. - * * If we want to run away, find nearest target and run out of view of it. - * * Look for anything we want to eat in the area and target it. - * * If we don't have a target already, find something to attack. - * * Go and attack our target (which might be food, or might be a mob). + * * If we want to run away (injured, or a scary fisherman is near), flee or panic-teleport from our target. + * * Otherwise hunt for something to attack, prioritising scary fishermen, and go bite it. + * * When idle, migrate between destinations or wander. */ /datum/ai_controller/basic_controller/carp blackboard = list( @@ -17,21 +15,7 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/make_carp_rift/panic_teleport, - /datum/ai_planning_subtree/flee_target/from_fisherman, - /datum/ai_planning_subtree/attack_obstacle_in_path/carp, - /datum/ai_planning_subtree/shortcut_to_target_through_carp_rift, - /datum/ai_planning_subtree/make_carp_rift/aggressive_teleport, - /datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/carp_migration, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp.bt.json" ///Megacarps. The only difference is that they don't flee from scary fishermen and prioritize them. /datum/ai_controller/basic_controller/carp/mega @@ -42,20 +26,7 @@ BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN, BB_CARPS_FEAR_FISHERMAN = FALSE, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - /datum/ai_planning_subtree/make_carp_rift/panic_teleport, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/attack_obstacle_in_path/carp, - /datum/ai_planning_subtree/shortcut_to_target_through_carp_rift, - /datum/ai_planning_subtree/make_carp_rift/aggressive_teleport, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/carp_migration, - ) + /** * Carp which bites back, but doesn't look for targets. * 'Not hunting targets' includes food (and can rings), because they have been well trained. @@ -69,39 +40,14 @@ BB_CARPS_FEAR_FISHERMAN = TRUE, ) ai_traits = PASSIVE_AI_FLAGS - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/make_carp_rift/panic_teleport, - /datum/ai_planning_subtree/flee_target/from_fisherman, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/attack_obstacle_in_path/carp, - /datum/ai_planning_subtree/shortcut_to_target_through_carp_rift, - /datum/ai_planning_subtree/make_carp_rift/aggressive_teleport, - /datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.json" /** * AI for carp with a spell. * Flow is basically the same as regular carp, except it will try and cast a spell at its target whenever possible and not fleeing. */ /datum/ai_controller/basic_controller/carp/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/make_carp_rift/panic_teleport, - /datum/ai_planning_subtree/flee_target/from_fisherman, - /datum/ai_planning_subtree/find_nearest_magicarp_spell_target, - /datum/ai_planning_subtree/targeted_mob_ability/magicarp, - /datum/ai_planning_subtree/attack_obstacle_in_path/carp, - /datum/ai_planning_subtree/shortcut_to_target_through_carp_rift, - /datum/ai_planning_subtree/make_carp_rift/aggressive_teleport, - /datum/ai_planning_subtree/basic_melee_attack_subtree/magicarp, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/carp_migration, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.json" /** * Carp which bites back, but doesn't look for targets and doesnt do as much damage @@ -116,14 +62,33 @@ BB_TARGET_ONLY_WITH_TRAITS = list(TRAIT_SCARY_FISHERMAN), ) ai_traits = PASSIVE_AI_FLAGS - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target/to_flee, // This should only find master fishermen because of the targeting strategy - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/from_flee_key, - /datum/ai_planning_subtree/make_carp_rift/panic_teleport/flee_key, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/make_carp_rift/aggressive_teleport, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/carp_migration, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.json" + + +/// Shared carp skeleton: escape -> pet command -> (flee / combat / migrate-or-idle) with a target-finding secondary. +/datum/bt_node/subtree/basic_carp_tree + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/basic_carp_tree.bt.json" + +/// Flee or panic-teleport away from a keyed target. +/datum/bt_node/subtree/carp_flee + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.json" + +/// Attack our current target: cast a spell, teleport in, smash obstacles or bite. +/datum/bt_node/subtree/carp_combat + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_combat.bt.json" + +/// Travel a migration path, riding or punching through rifts and walls along the way. +/datum/bt_node/subtree/carp_migration + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.json" + +/// Hunting target finder: flee the nearest threat when injured, otherwise hunt prioritising scary fishermen. +/datum/bt_node/subtree/carp_target_selection + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.json" + +/// Bite-back target finder: target whoever has attacked us. +/datum/bt_node/subtree/carp_retaliate_selection + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.json" + +/// Passive flee finder: flag scary fishermen and attackers as things to run away from. +/datum/bt_node/subtree/carp_passive_selection + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.json" diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.json new file mode 100644 index 000000000000..3f8d758f941d --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_flee.bt.json @@ -0,0 +1,63 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_flee", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/carp_should_flee", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "target_key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_CARP_RIFT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/make_carp_rift/away", + "vars": { + "ability_key": "BB_CARP_RIFT", + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_flee_location", + "vars": { + "target_key": "$bf3k9q2p", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "destination_key": "BB_FLEE_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FLEE_LOCATION", + "required_dist": 0, + "finish_on_arrival": true + } + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.json new file mode 100644 index 000000000000..0135ad46a74a --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_migration.bt.json @@ -0,0 +1,114 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_migration", + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_carp_rift_shortcut/nearby", + "vars": { + "destination_key": "BB_CARP_RIFT_DESTINATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CARP_RIFT_DESTINATION", + "required_dist": 0, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_CARP_RIFT_DESTINATION" + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CARP_MIGRATION_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/carp_path_blocked", + "vars": { + "target_key": "BB_CARP_MIGRATION_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "ability_key": "BB_CARP_RIFT" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/make_carp_rift/towards/unvalidated", + "vars": { + "ability_key": "BB_CARP_RIFT", + "target_key": "BB_CARP_MIGRATION_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CARP_MIGRATION_TARGET", + "time_between_perform": "1.5 SECONDS" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CARP_MIGRATION_TARGET", + "required_dist": 3, + "finish_on_arrival": true + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_arrived_migration_target", + "vars": { + "target_key": "BB_CARP_MIGRATION_TARGET" + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_next_carp_migration_step", + "vars": { + "path_key": "BB_CARP_MIGRATION_PATH", + "target_key": "BB_CARP_MIGRATION_TARGET" + } + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.json new file mode 100644 index 000000000000..c4b2c3470823 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_passive.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/carp/passive", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_carp_tree", + "bindings": { + "bf1nd7r2": "/datum/bt_node/subtree/carp_passive_selection", + "b7cm2s3z": "BB_BASIC_MOB_FLEE_TARGET" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.json new file mode 100644 index 000000000000..77bd25864eb2 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_passive_selection.bt.json @@ -0,0 +1,25 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_passive_selection", + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION", + "check_faction": false + } + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.json new file mode 100644 index 000000000000..7e84f7bc58f4 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_pet.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/carp/pet", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_carp_tree", + "bindings": { + "bf1nd7r2": "/datum/bt_node/subtree/carp_retaliate_selection", + "bc0mb4t9": "/datum/bt_node/subtree/carp_retaliate_selection", + "bid1e6k3": "/datum/bt_node/subtree" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.json new file mode 100644 index 000000000000..04b187ecb509 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ranged.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/carp/ranged", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/basic_carp_tree", + "bindings": { + "be5c4p10": "/datum/bt_node/subtree/escape_captivity" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.json new file mode 100644 index 000000000000..0497947d8ed2 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_retaliate_selection.bt.json @@ -0,0 +1,11 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_retaliate_selection", + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": false + } +} diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.json b/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.json new file mode 100644 index 000000000000..0d2446542f61 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_target_selection.bt.json @@ -0,0 +1,32 @@ +{ + "dm_type": "/datum/bt_node/subtree/carp_target_selection", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_BASIC_MOB_STOP_FLEEING", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.json b/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.json new file mode 100644 index 000000000000..e2291f2dc3f2 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cat_butcherer", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat_with_retaliate" +} diff --git a/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm b/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm index cc77315ebebe..05b380c91ec4 100644 --- a/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm +++ b/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm @@ -44,6 +44,9 @@ AddElement(/datum/element/death_drops, drop_on_death) RegisterSignal(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(after_attack)) +/mob/living/basic/cat_butcherer/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/basic/cat_butcherer/proc/after_attack(mob/living/basic/attacker, atom/target) SIGNAL_HANDLER @@ -63,17 +66,9 @@ tail.forceMove(drop_location()) /datum/ai_controller/basic_controller/cat_butcherer + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/cat_butcherer.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.json b/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.json new file mode 100644 index 000000000000..eccbfd80fd94 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/headslug", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" +} diff --git a/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm index 1f9bd60db9d8..1707d51ae185 100644 --- a/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm +++ b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm @@ -95,8 +95,8 @@ /// This is a bit neutered since these aren't intended to exist outside of player control, but it's a bit weird to just have these guys be completely stationary. /// No attacking or anything like that, though. Just something so they seem alive. /datum/ai_controller/basic_controller/headslug + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/changeling/headslug.bt.json" ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk /// Neutered version to prevent people from turning themselves into changelings with sentience potions or transformation /mob/living/basic/headslug/beakless diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.json b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.json new file mode 100644 index 000000000000..1237c1cc75d8 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.json @@ -0,0 +1,183 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/eyeball", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "ability_key": "BB_GLARE_ABILITY" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_target_facing_turf", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "set_key": "BB_GLARE_POSITION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_GLARE_POSITION", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_GLARE_ABILITY", + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_BLIND_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_BLIND_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/heal_eye_damage", + "vars": { + "target_key": "BB_BLIND_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bqwjf4id": "2 SECONDS", + "b3cnse9r": "TRUE" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_BLIND_TARGET", + "target_source": "/datum/target_source/oview_single_type/carbon_mob", + "targeting_strategy": "/datum/targeting_strategy/damaged_eyes", + "vision_range": 9 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/carrot", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 6 + } + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm deleted file mode 100644 index 27f637d3a879..000000000000 --- a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm +++ /dev/null @@ -1,88 +0,0 @@ -/datum/ai_behavior/find_the_blind - -/datum/ai_behavior/find_the_blind/perform(seconds_per_tick, datum/ai_controller/controller, blind_key, threshold_key) - var/mob/living_pawn = controller.pawn - var/list/blind_list = list() - var/eye_damage_threshold = controller.blackboard[threshold_key] - if(!eye_damage_threshold) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - for(var/mob/living/carbon/blind in oview(9, living_pawn)) - var/obj/item/organ/eyes/eyes = blind.get_organ_slot(ORGAN_SLOT_EYES) - if(isnull(eyes)) - continue - if(eyes.damage < eye_damage_threshold) - continue - blind_list += blind - - if(!length(blind_list)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(blind_key, pick(blind_list)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/heal_eye_damage - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/heal_eye_damage/setup(datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/carbon/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - -/datum/ai_behavior/heal_eye_damage/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/mob/living/carbon/target = controller.blackboard[target_key] - var/mob/living/living_pawn = controller.pawn - - if(QDELETED(target)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/obj/item/organ/eyes/eyes = target.get_organ_slot(ORGAN_SLOT_EYES) - var/datum/callback/callback = CALLBACK(living_pawn, TYPE_PROC_REF(/mob/living/basic/eyeball, heal_eye_damage), target, eyes) - callback.Invoke() - - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/heal_eye_damage/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/targeted_mob_ability/glare_at_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT - required_distance = 0 - -/datum/ai_behavior/targeted_mob_ability/glare_at_target/setup(datum/ai_controller/controller, ability_key, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if (isnull(target)) - return FALSE - - var/turf/turf_to_move_towards = get_step(target, target.dir) - if(turf_to_move_towards.is_blocked_turf(ignore_atoms = list(controller.pawn))) - return FALSE - - if(isnull(turf_to_move_towards)) - return FALSE - - set_movement_target(controller, turf_to_move_towards) - -/datum/ai_behavior/targeted_mob_ability/glare_at_target/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) - var/datum/action/cooldown/ability = controller.blackboard[ability_key] - var/mob/living/target = controller.blackboard[target_key] - - if(QDELETED(ability) || QDELETED(target)) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/direction_to_compare = get_dir(target, controller.pawn) - var/target_direction = target.dir - if(direction_to_compare != target_direction) - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - - var/result = ability.InterceptClickOn(controller.pawn, null, target) - if(result == TRUE) - return AI_BEHAVIOR_INSTANT - else - return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/hunt_target/interact_with_target/carrot - hunt_cooldown = 2 SECONDS - always_reset_target = TRUE diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm index e24abb62b5e8..5a3a2ccacce1 100644 --- a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm +++ b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm @@ -5,25 +5,9 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/glare, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/heal_the_blind, - /datum/ai_planning_subtree/find_and_hunt_target/carrot, - ) - -/datum/ai_planning_subtree/heal_the_blind - -/datum/ai_planning_subtree/heal_the_blind/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_BLIND_TARGET)) - controller.queue_behavior(/datum/ai_behavior/heal_eye_damage, BB_BLIND_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_the_blind, BB_BLIND_TARGET, BB_EYE_DAMAGE_THRESHOLD) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/eyeball/eyeball.bt.json" -/datum/targeting_strategy/basic/eyeball/can_attack(mob/living/owner, atom/target, vision_range) +/datum/targeting_strategy/basic/eyeball/is_valid_target(mob/living/owner, atom/target, vision_range, datum/ai_controller/controller = null) . = ..() if(!.) return FALSE @@ -40,14 +24,3 @@ return FALSE return can_see(target, owner, 9) //if the target cant see us dont attack him - -/datum/ai_planning_subtree/targeted_mob_ability/glare - ability_key = BB_GLARE_ABILITY - use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/glare_at_target - finish_planning = TRUE - -/datum/ai_planning_subtree/find_and_hunt_target/carrot - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/carrot - hunt_targets = list(/obj/item/food/grown/carrotlike/carrot) - hunt_range = 6 diff --git a/code/modules/mob/living/basic/space_fauna/faithless.bt.json b/code/modules/mob/living/basic/space_fauna/faithless.bt.json new file mode 100644 index 000000000000..ae18da6d4eb5 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/faithless.bt.json @@ -0,0 +1,137 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/faithless", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/hunt_target_list", + "targeting_strategy": "/datum/targeting_strategy/unbroken_light", + "vision_range": 7 + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/faithless" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/faithless.dm b/code/modules/mob/living/basic/space_fauna/faithless.dm index c4f195cc0a18..5c797e85bc48 100644 --- a/code/modules/mob/living/basic/space_fauna/faithless.dm +++ b/code/modules/mob/living/basic/space_fauna/faithless.dm @@ -41,6 +41,7 @@ AddElement(/datum/element/door_pryer) AddElement(/datum/element/footstep, FOOTSTEP_MOB_SHOE) AddElement(/datum/element/mob_grabber, steal_from_others = FALSE) + ai_controller.set_blackboard_key(BB_HUNT_TARGET_LIST, typecacheof(list(/obj/machinery/light))) /mob/living/basic/faithless/melee_attack(atom/target, list/modifiers, ignore_cooldown) . = ..() @@ -54,20 +55,11 @@ span_userdanger("\The [src] knocks you down!")) /datum/ai_controller/basic_controller/faithless + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/faithless.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, - BB_LOW_PRIORITY_HUNTING_TARGET = null, // lights + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_TREAT_UNCONSCIOUS_AS_HARDCRIT = TRUE, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/attack_obstacle_in_path/low_priority_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures, - /datum/ai_planning_subtree/random_speech/faithless, - ) diff --git a/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.json b/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.json new file mode 100644 index 000000000000..f79314d95415 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/garden_gnome.bt.json @@ -0,0 +1,114 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/garden_gnome", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "check_faction": "$b2jvnm5d" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/garden_gnome" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm index f6926b2ea51b..c36a3ff85bb9 100644 --- a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm +++ b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm @@ -124,16 +124,9 @@ potential_gnome.ai_controller.set_blackboard_key_assoc_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker, world.time) /datum/ai_controller/basic_controller/garden_gnome + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/garden_gnome.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/garden_gnome, - ) diff --git a/code/modules/mob/living/basic/space_fauna/ghost.bt.json b/code/modules/mob/living/basic/space_fauna/ghost.bt.json new file mode 100644 index 000000000000..7f79b254cf15 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/ghost.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/ghost", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/ghost.dm b/code/modules/mob/living/basic/space_fauna/ghost.dm index cfdf6840ff25..4b8f3f438cdb 100644 --- a/code/modules/mob/living/basic/space_fauna/ghost.dm +++ b/code/modules/mob/living/basic/space_fauna/ghost.dm @@ -49,6 +49,7 @@ AddElement(/datum/element/death_drops, /obj/item/ectoplasm) AddElement(/datum/element/simple_flying) AddElement(/datum/element/ai_retaliate) + ADD_TRAIT(src, TRAIT_GHOSTLY_MOB, INNATE_TRAIT) give_identity() @@ -94,18 +95,12 @@ name = "ghost of [pick(GLOB.first_names_female)] [pick(GLOB.last_names)]" /datum/ai_controller/basic_controller/ghost + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/ghost.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) /// Weaker variant of ghosts. Meant to be summoned in swarms via the ectoplasmic anomaly and associated ghost portal. /mob/living/basic/ghost/swarm diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.json b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.json new file mode 100644 index 000000000000..d02a4edaaf98 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.json @@ -0,0 +1,119 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/hivebot", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/relay_to_hive_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.1 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HIVE_PARTNER", + "target_source": "/datum/target_source/oview_single_type/hivebot", + "targeting_strategy": "/datum/targeting_strategy/living_not_dead", + "vision_range": 10 + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_behavior.dm b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_behavior.dm index 54c3c13d139f..f299d032b1dc 100644 --- a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_behavior.dm +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_behavior.dm @@ -1,35 +1,17 @@ -/datum/ai_behavior/find_and_set/hive_partner - -/datum/ai_behavior/find_and_set/hive_partner/search_tactic(datum/ai_controller/controller, locate_path, search_range = 10) - var/mob/living/living_pawn = controller.pawn - var/list/hive_partners = list() - for(var/mob/living/target in oview(search_range, living_pawn)) - if(!istype(target, locate_path)) - continue - if(target.stat == DEAD) - continue - hive_partners += target - - if(length(hive_partners)) - return pick(hive_partners) - -/// behavior that allow us to go communicate with other hivebots -/datum/ai_behavior/relay_message - ///length of the message we will relay +/// Moves to the hive partner in BB_HIVE_PARTNER, relays a message, then clears the key. +/// The partner is found by a separate acquire_target leaf in the controller tree. +/datum/bt_node/subtree/relay_to_hive_partner + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.json" + +/// Says a random binary string to a hive partner. Movement to the partner is handled +/// in the tree via a move_to_target leaf; the target key is cleared by a clear_key leaf. +/datum/bt_node/ai_behavior/relay_message + /// Blackboard key holding the hive partner to talk at. + var/target_key + /// Number of bits in the message we relay. var/length_of_message = 4 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT| AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/relay_message/setup(datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/target = controller.blackboard[target_key] - // It stopped existing - if(QDELETED(target)) - return FALSE - set_movement_target(controller, target) - - -/datum/ai_behavior/relay_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/bt_node/ai_behavior/relay_message/perform(seconds_per_tick, datum/ai_controller/controller) var/mob/living/target = controller.blackboard[target_key] var/mob/living/living_pawn = controller.pawn @@ -38,31 +20,13 @@ var/message_relayed = "" for(var/i in 1 to length_of_message) message_relayed += prob(50) ? "1" : "0" - living_pawn.say(message_relayed, forced = "AI Controller") + INVOKE_ASYNC(living_pawn, TYPE_PROC_REF(/atom/movable, say), message_relayed, forced = "AI Controller") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/relay_message/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/find_hunt_target/repair_machines - -/datum/ai_behavior/find_hunt_target/repair_machines/valid_dinner(mob/living/source, obj/machinery/repair_target, radius) - if(repair_target.get_integrity() >= repair_target.max_integrity) - return FALSE - - return can_see(source, repair_target, radius) - -/datum/ai_behavior/hunt_target/repair_machines +/// Repairs a damaged machine once in range. Finding the machine is a separate +/// acquire_target leaf; movement is a move_to_target leaf. +/datum/bt_node/ai_behavior/hunt_target/repair_machines always_reset_target = TRUE -/datum/ai_behavior/hunt_target/repair_machines/target_caught(mob/living/basic/hivebot/mechanic/hunter, obj/machinery/repair_target) +/datum/bt_node/ai_behavior/hunt_target/repair_machines/target_caught(mob/living/basic/hivebot/mechanic/hunter, obj/machinery/repair_target) hunter.repair_machine(repair_target) - -/datum/ai_behavior/basic_ranged_attack/hivebot - action_cooldown = 3 SECONDS - avoid_friendly_fire = TRUE - -/datum/ai_behavior/basic_ranged_attack/hivebot_rapid - action_cooldown = 1.5 SECONDS - avoid_friendly_fire = TRUE diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.json b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.json new file mode 100644 index 000000000000..444702cb7d9f --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.json @@ -0,0 +1,166 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/hivebot/mechanic", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_MACHINE_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_MACHINE_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/repair_machines", + "vars": { + "target_key": "BB_MACHINE_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/relay_to_hive_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_MACHINE_TARGET", + "target_source": "/datum/target_source/oview_single_type/machine", + "targeting_strategy": "/datum/targeting_strategy/damaged_machine", + "vision_range": 10 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.1 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HIVE_PARTNER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HIVE_PARTNER", + "target_source": "/datum/target_source/oview_single_type/hivebot", + "targeting_strategy": "/datum/targeting_strategy/living_not_dead", + "vision_range": 10 + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.json b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.json new file mode 100644 index 000000000000..b6009de5a228 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.json @@ -0,0 +1,130 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/hivebot/ranged", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "time_between_perform": "3 SECONDS", + "avoid_friendly_fire": true + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/relay_to_hive_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.1 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HIVE_PARTNER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HIVE_PARTNER", + "target_source": "/datum/target_source/oview_single_type/hivebot", + "targeting_strategy": "/datum/targeting_strategy/living_not_dead", + "vision_range": 10 + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.json b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.json new file mode 100644 index 000000000000..38703850ca1c --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.json @@ -0,0 +1,130 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/hivebot/ranged/rapid", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "time_between_perform": "1.5 SECONDS", + "avoid_friendly_fire": true + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/relay_to_hive_partner" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.1 + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_HIVE_PARTNER", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_HIVE_PARTNER", + "target_source": "/datum/target_source/oview_single_type/hivebot", + "targeting_strategy": "/datum/targeting_strategy/living_not_dead", + "vision_range": 10 + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm index 34ace8789291..affe62661516 100644 --- a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm @@ -4,68 +4,13 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/hive_communicate, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/hivebot/hivebot.bt.json" /datum/ai_controller/basic_controller/hivebot/mechanic - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/find_and_hunt_target/repair_machines, - /datum/ai_planning_subtree/hive_communicate, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/hivebot/hivebot_mechanic.bt.json" /datum/ai_controller/basic_controller/hivebot/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/hivebot, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/hive_communicate, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged.bt.json" /datum/ai_controller/basic_controller/hivebot/ranged/rapid - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/hivebot_rapid, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/hive_communicate, - ) - - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/hivebot_rapid - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/hivebot_rapid - - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/hivebot - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/hivebot - -/datum/ai_planning_subtree/hive_communicate - ///chance to go and relay message - var/relay_chance = 10 - -/datum/ai_planning_subtree/hive_communicate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - if(!SPT_PROB(relay_chance, seconds_per_tick)) - return - - if (controller.blackboard_key_exists(BB_HIVE_PARTNER)) - controller.queue_behavior(/datum/ai_behavior/relay_message, BB_HIVE_PARTNER) - return SUBTREE_RETURN_FINISH_PLANNING - controller.queue_behavior(/datum/ai_behavior/find_and_set/hive_partner, BB_HIVE_PARTNER, /mob/living/basic/hivebot) - -/datum/ai_planning_subtree/find_and_hunt_target/repair_machines - target_key = BB_MACHINE_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/repair_machines - finding_behavior = /datum/ai_behavior/find_hunt_target/repair_machines - hunt_targets = list(/obj/machinery) - hunt_range = 10 - hunt_chance = 35 + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/hivebot/hivebot_ranged_rapid.bt.json" diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.json b/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.json new file mode 100644 index 000000000000..52ce2daa1071 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/hivebot/relay_to_hive_partner.bt.json @@ -0,0 +1,36 @@ +{ + "dm_type": "/datum/bt_node/subtree/relay_to_hive_partner", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_HIVE_PARTNER" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HIVE_PARTNER", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/relay_message", + "vars": { + "target_key": "BB_HIVE_PARTNER" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_HIVE_PARTNER" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.json b/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.json new file mode 100644 index 000000000000..726caa4a98d9 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/killer_tomato.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/killer_tomato", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat", + "bindings": { + "bqul7l8t": "/datum/bt_node/subtree/random_speech_loop" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/killer_tomato.dm b/code/modules/mob/living/basic/space_fauna/killer_tomato.dm index 152c14ff38ef..00405bd1531e 100644 --- a/code/modules/mob/living/basic/space_fauna/killer_tomato.dm +++ b/code/modules/mob/living/basic/space_fauna/killer_tomato.dm @@ -41,16 +41,14 @@ ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) /datum/ai_controller/basic_controller/killer_tomato + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/killer_tomato.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("gnashes.", "growls lowly.", "snarls."), + BB_EMOTE_SEE = list("salivates."), + BB_SPEAK_CHANCE = 3, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/killer_tomato, - ) diff --git a/code/modules/mob/living/basic/space_fauna/lightgeist.bt.json b/code/modules/mob/living/basic/space_fauna/lightgeist.bt.json new file mode 100644 index 000000000000..ddbc437caf18 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/lightgeist.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/lightgeist", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat" +} diff --git a/code/modules/mob/living/basic/space_fauna/lightgeist.dm b/code/modules/mob/living/basic/space_fauna/lightgeist.dm index b86d50e2efba..da6fff877157 100644 --- a/code/modules/mob/living/basic/space_fauna/lightgeist.dm +++ b/code/modules/mob/living/basic/space_fauna/lightgeist.dm @@ -71,18 +71,13 @@ death() /datum/ai_controller/basic_controller/lightgeist + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/lightgeist.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/lightgeist, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, // We heal things by attacking them - ) /// Attack only mobs who have damage that we can heal, I think this is specific enough not to be a generic type /datum/targeting_strategy/lightgeist @@ -91,7 +86,7 @@ /// Type of limb we can heal var/required_bodytype = BODYTYPE_ORGANIC -/datum/targeting_strategy/lightgeist/can_attack(mob/living/living_mob, mob/living/target, vision_range) +/datum/targeting_strategy/lightgeist/is_valid_target(mob/living/living_mob, mob/living/target, vision_range, datum/ai_controller/controller = null) if (!isliving(target) || target.stat == DEAD) return FALSE if (!(heal_biotypes & target.mob_biotypes)) diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.json b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.json new file mode 100644 index 000000000000..2380cf189caa --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.json @@ -0,0 +1,73 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/meteor_heart", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_METEOR_HEART_GROUND_SPIKES", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_METEOR_HEART_SPINE_TRAPS" + } + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/meteor_heart_deaggro" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm index ccb86e7043cd..4ca874df94f3 100644 --- a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm +++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm @@ -43,7 +43,7 @@ /datum/action/cooldown/mob_cooldown/spine_traps = BB_METEOR_HEART_SPINE_TRAPS, ) grant_actions_by_list(innate_actions) - ai_controller.set_ai_status(AI_STATUS_OFF) + ai_controller.force_ai_off() RegisterSignal(src, COMSIG_MOB_ABILITY_FINISHED, PROC_REF(used_ability)) RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(aggro)) @@ -62,7 +62,7 @@ /mob/living/basic/meteor_heart/proc/aggro() if (ai_controller.ai_status == AI_STATUS_ON) return - ai_controller.reset_ai_status() + ai_controller.clear_forced_off() if (!ai_controller.ai_status == AI_STATUS_ON) return icon_state = "heart_aggro" @@ -70,7 +70,7 @@ /// Called when we stop being mad /mob/living/basic/meteor_heart/proc/deaggro() - ai_controller.set_ai_status(AI_STATUS_OFF) + ai_controller.force_ai_off() icon_state = "heart" soundloop.set_mid_length(HEARTBEAT_NORMAL) diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart_ai.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart_ai.dm index 92f1d0a98896..3e3a801d0830 100644 --- a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart_ai.dm @@ -1,33 +1,29 @@ /// A spellcasting AI which does not move /datum/ai_controller/basic_controller/meteor_heart + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_TARGETLESS_TIME = 0, + BB_CURRENT_TARGET_HIDING_LOCATION = null, ) - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/ground_spikes, - /datum/ai_planning_subtree/use_mob_ability/spine_traps, - /datum/ai_planning_subtree/sleep_with_no_target/meteor_heart, - ) - -/datum/ai_planning_subtree/targeted_mob_ability/ground_spikes - ability_key = BB_METEOR_HEART_GROUND_SPIKES - finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/spine_traps - ability_key = BB_METEOR_HEART_SPINE_TRAPS +/// After enough time with no target, calls deaggro() on the meteor heart to shut down the AI and reset visuals. +/datum/bt_node/ai_behavior/meteor_heart_deaggro + var/deaggro_delay = 10 SECONDS + VAR_PRIVATE/timerid -/// After enough time with no target, deaggro and change animation state -/datum/ai_planning_subtree/sleep_with_no_target/meteor_heart - sleep_behaviour = /datum/ai_behavior/sleep_after_targetless_time/meteor_heart +/datum/bt_node/ai_behavior/meteor_heart_deaggro/setup(datum/ai_controller/controller) + . = ..() + timerid = addtimer(CALLBACK(src, PROC_REF(finish_action), controller, TRUE), deaggro_delay, TIMER_UNIQUE | TIMER_STOPPABLE) -/datum/ai_behavior/sleep_after_targetless_time/meteor_heart +/datum/bt_node/ai_behavior/meteor_heart_deaggro/perform(seconds_per_tick, datum/ai_controller/controller) + return AI_BEHAVIOR_DELAY -/datum/ai_behavior/sleep_after_targetless_time/meteor_heart/enter_sleep(datum/ai_controller/controller) +/datum/bt_node/ai_behavior/meteor_heart_deaggro/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + deltimer(timerid) + timerid = null + if(!succeeded) + return var/mob/living/basic/meteor_heart/heart = controller.pawn - if (!istype(heart)) - return ..() - heart.deaggro() + if(istype(heart)) + heart.deaggro() diff --git a/code/modules/mob/living/basic/space_fauna/morph.bt.json b/code/modules/mob/living/basic/space_fauna/morph.bt.json new file mode 100644 index 000000000000..d9508fc1eaaf --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/morph.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/morph", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_combat_with_retaliate" +} diff --git a/code/modules/mob/living/basic/space_fauna/morph.dm b/code/modules/mob/living/basic/space_fauna/morph.dm index a76704e40fe9..c0e92d20bbe3 100644 --- a/code/modules/mob/living/basic/space_fauna/morph.dm +++ b/code/modules/mob/living/basic/space_fauna/morph.dm @@ -200,16 +200,10 @@ /// No fleshed out AI implementation, just something that make these fellers seem lively if they're just dropped into a station. /// Only real human-powered intelligence is capable of playing prop hunt in SS13 (until further notice). /datum/ai_controller/basic_controller/morph + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/morph.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/space_fauna/mushroom.bt.json b/code/modules/mob/living/basic/space_fauna/mushroom.bt.json new file mode 100644 index 000000000000..6f6b21c905ac --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/mushroom.bt.json @@ -0,0 +1,103 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mushroom", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_hunt", + "bindings": { + "bvtz06kb": "BB_LOW_PRIORITY_HUNTING_TARGET", + "b3y599q4": "BB_LOW_PRIORITY_HUNTING_TARGET", + "brrasnah": "BB_LOW_PRIORITY_HUNTING_TARGET", + "bd1towgc": "TRUE", + "b3cnse9r": "TRUE" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_single_type/mushroom_food", + "vision_range": 6 + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/mushroom.dm b/code/modules/mob/living/basic/space_fauna/mushroom.dm index e6195ba9d9cf..c05f5094dcdd 100644 --- a/code/modules/mob/living/basic/space_fauna/mushroom.dm +++ b/code/modules/mob/living/basic/space_fauna/mushroom.dm @@ -55,32 +55,21 @@ ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) /datum/ai_controller/basic_controller/mushroom + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/mushroom.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mushroom, BB_TARGET_MINIMUM_STAT = DEAD, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/mushroom_food, - ) - /datum/targeting_strategy/basic/mushroom + custom_faction_check = TRUE ///we only attacked another mushrooms /datum/targeting_strategy/basic/mushroom/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) return !living_mob.faction_check_atom(the_target, exact_match = check_factions_exactly) -/datum/ai_planning_subtree/find_and_hunt_target/mushroom_food - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target - hunt_targets = list(/obj/item/food/grown/mushroom) - hunt_range = 6 - /mob/living/basic/mushroom/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) . = ..() if(!.) @@ -160,11 +149,15 @@ melee_damage_upper += (level_gain * rand(1,5)) maxHealth += (level_gain * rand(1,5)) -/mob/living/basic/mushroom/attackby(obj/item/mush, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers) - if(istype(mush, /obj/item/food/grown/mushroom)) - recover(mush) - return - if(mush.force || user.combat_mode) +/mob/living/basic/mushroom/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/food/grown/mushroom)) + return ..() + + recover(tool) + return ITEM_INTERACT_SUCCESS + +/mob/living/basic/mushroom/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) + if(attacking_item.force || user.combat_mode) bruised = TRUE return ..() diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm index ecf981299fcf..de1c11fc2118 100644 --- a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm +++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm @@ -56,7 +56,7 @@ /mob/living/basic/migo/send_speech(message_raw, message_range, obj/source, bubble_type, list/spans, datum/language/message_language, list/message_mods, forced, tts_message, list/tts_filter) . = ..() - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return make_migo_sound() @@ -64,7 +64,7 @@ . = ..() if(!.) //dead or deleted return - if(stat) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return if(SPT_PROB(5, seconds_per_tick)) make_migo_sound() diff --git a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.json b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.json new file mode 100644 index 000000000000..2ba0d9b479a4 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.json @@ -0,0 +1,121 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/paper_wizard", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_MIMICS", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_WIZARD_SUMMON_MINIONS" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_paper_and_write" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_FOUND_PAPER", + "target_source": "/datum/target_source/oview_single_type/paper", + "targeting_strategy": "/datum/targeting_strategy/empty_paper", + "time_between_perform": "10 SECONDS" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm index 24980cc01109..45ccc938331e 100644 --- a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm +++ b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm @@ -35,6 +35,9 @@ grant_loot() AddElement(/datum/element/effect_trail, /obj/effect/temp_visual/paper_scatter) +/mob/living/basic/paper_wizard/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/basic/paper_wizard/proc/grant_abilities() var/static/list/innate_actions = list( /datum/action/cooldown/spell/conjure/wizard_summon_minions = BB_WIZARD_SUMMON_MINIONS, @@ -47,6 +50,7 @@ AddElement(/datum/element/death_drops, /obj/effect/temp_visual/paperwiz_dying) /datum/ai_controller/basic_controller/paper_wizard + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_WRITING_LIST = list( @@ -57,46 +61,6 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/wizard_mimic, - /datum/ai_planning_subtree/use_mob_ability/wizard_summon_minions, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/attack_obstacle_in_path/paper_wizard, - /datum/ai_planning_subtree/find_paper_and_write, - ) - -/datum/ai_planning_subtree/attack_obstacle_in_path/paper_wizard - target_key = BB_FOUND_PAPER - attack_behaviour = /datum/ai_behavior/attack_obstructions/paper_wizard - -/datum/ai_behavior/attack_obstructions/paper_wizard - action_cooldown = 0.4 SECONDS - can_attack_turfs = TRUE - can_attack_dense_objects = TRUE - -/datum/ai_planning_subtree/targeted_mob_ability/wizard_mimic - ability_key = BB_WIZARD_MIMICS - finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/wizard_summon_minions - ability_key = BB_WIZARD_SUMMON_MINIONS - finish_planning = FALSE - -/datum/ai_behavior/find_and_set/empty_paper - action_cooldown = 10 SECONDS - -/datum/ai_behavior/find_and_set/empty_paper/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/empty_papers = list() - - for(var/obj/item/paper/target_paper in oview(search_range, controller.pawn)) - if(target_paper.is_empty()) - empty_papers += target_paper - - if(empty_papers.len) - return pick(empty_papers) /mob/living/basic/paper_wizard/copy desc = "'Tis a ruse!" diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.json b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.json new file mode 100644 index 000000000000..8b9cf6851889 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.json @@ -0,0 +1,98 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/regal_rat", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_FLEE_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_RAISE_HORDE_ABILITY", + "target_key": "BB_BASIC_MOB_FLEE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability/domain", + "vars": { + "ability_key": "BB_DOMAIN_ABILITY" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm index bf64860cb540..7b08fa1095c8 100644 --- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm @@ -197,7 +197,7 @@ return TRUE // it might be possible to attack this? we'll find out soon enough var/mob/living/living_target = the_target - if(HAS_TRAIT_NOT_FROM(living_target, TRAIT_FAKEDEATH, SPECIES_TRAIT) || living_target.stat == DEAD) + if(IS_DEAD_OR_FAKING(living_target)) balloon_alert(src, "already dead!") return FALSE diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm index b51871c7f9dd..83c3c18397b9 100644 --- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm @@ -125,7 +125,6 @@ * * Convert nearby mice into aggressive rats. * * Convert nearby roaches into aggressive roaches. * * Convert nearby frogs into aggressive frogs. - * * Spawn a single mouse if below the mouse cap. */ /datum/action/cooldown/mob_cooldown/riot/proc/riot() playsound(owner, 'sound/mobs/non-humanoids/mouse/mousesqueek.ogg', vol = 150, frequency = 10000) @@ -144,15 +143,14 @@ command_feedback = "squeak!" // Frogs and roaches can squeak too it's fine pointed_reaction = "and squeaks aggressively" refuse_reaction = "quivers" - attack_behaviour = /datum/ai_behavior/basic_melee_attack -// Command you can give to a mouse to make it kill someone +// Command you can give to a glockroach to make it shoot someone /datum/pet_command/attack/glockroach speech_commands = list("attack", "sic", "kill", "cheese em") command_feedback = "squeak!" pointed_reaction = "and cocks its gun" refuse_reaction = "quivers" - attack_behaviour = /datum/ai_behavior/basic_ranged_attack/glockroach + attack_subtree = /datum/bt_node/subtree/pet_command/attack/ranged/glockroach /** *Spittle; harmless reagent that is added by rat king, and makes you disgusted. @@ -193,4 +191,4 @@ affected_mob.vomit(VOMIT_CATEGORY_DEFAULT) /datum/pet_command/protect_owner/glockroach - protect_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach + protect_owner_subtree = /datum/bt_node/subtree/pet_command/protect_owner/ranged/glockroach diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_ai.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_ai.dm index 27f1eb47785b..e39fc380f247 100644 --- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_ai.dm @@ -1,34 +1,17 @@ /datum/ai_controller/basic_controller/regal_rat + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - // we pretty much do cheesy things (make the station worse) and don't deal with peasants (crew) unless they start to get in the way - // summon the horde if we get into a fight and then let the horde take care of it while we skedaddle - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/targeted_mob_ability/riot, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/use_mob_ability/domain, - ) - -/datum/ai_planning_subtree/targeted_mob_ability/riot - target_key = BB_BASIC_MOB_FLEE_TARGET // we only want to trigger this when provoked, manpower is low nowadays - ability_key = BB_RAISE_HORDE_ABILITY - finish_planning = FALSE - -/datum/ai_planning_subtree/use_mob_ability/domain - ability_key = BB_DOMAIN_ABILITY +/// Only activate the domain when it isn't already running. +/datum/bt_node/ai_behavior/use_mob_ability/domain -/datum/ai_planning_subtree/use_mob_ability/domain/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) +/datum/bt_node/ai_behavior/use_mob_ability/domain/perform(seconds_per_tick, datum/ai_controller/controller) var/datum/action/cooldown/mob_cooldown/domain/domain = controller.blackboard[ability_key] - if (!istype(domain) || domain.is_active) - return + if(!istype(domain) || domain.is_active) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED return ..() diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm index af30342e1fe9..3e7c799ee96b 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -74,6 +74,8 @@ /datum/action/cooldown/spell/aoe/revenant/haunt_object, /datum/action/cooldown/spell/aoe/revenant/malfunction, /datum/action/cooldown/spell/aoe/revenant/overload, + /datum/action/cooldown/spell/aoe/revenant/vortex, + /datum/action/cooldown/spell/aoe/revenant/vortex/scatter, /datum/action/cooldown/spell/list_target/telepathy/revenant, ) @@ -93,11 +95,22 @@ var/unreveal_time = 0 /// How many perfect, regen-cap increasing souls the revenant has. //TODO, add objective for getting a perfect soul(s?) var/perfectsouls = 0 + /// Are our abilities blocked from being inside a wall? Separate, as we set this back to null after running update in update_ability_status() + /// Used to avoid running turf checks more than once + var/ability_density_locked = null /mob/living/basic/revenant/Initialize(mapload) . = ..() AddElement(/datum/element/simple_flying) - add_traits(list(TRAIT_COMBAT_MODE_LOCK, TRAIT_SPACEWALK, TRAIT_SIXTHSENSE, TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_SEE_BLESSED_TILES, TRAIT_IGNORE_ELEVATION), INNATE_TRAIT) + add_traits(list( + TRAIT_COMBAT_MODE_LOCK, + TRAIT_SPACEWALK, + TRAIT_SIXTHSENSE, + TRAIT_FREE_HYPERSPACE_MOVEMENT, + TRAIT_SEE_BLESSED_TILES, + TRAIT_IGNORE_ELEVATION, + TRAIT_GHOSTLY_MOB, + ), INNATE_TRAIT) grant_actions_by_list(abilities) @@ -155,14 +168,14 @@ if(essence_regenerating && !HAS_TRAIT(src, TRAIT_REVENANT_INHIBITED) && essence < max_essence) //While inhibited, essence will not regenerate var/change_in_time = DELTA_WORLD_TIME(SSmobs) essence = min(essence + (essence_regen_amount * change_in_time), max_essence) - update_mob_action_buttons() //because we update something required by our spells in life, we need to update our buttons + update_ability_status() //because we update something required by our spells in life, we need to update our buttons update_health_hud() /mob/living/basic/revenant/proc/update_revenant_appearance() SIGNAL_HANDLER update_appearance(UPDATE_ICON) - update_mob_action_buttons() + update_ability_status() /mob/living/basic/revenant/AltClickOn(atom/target) if(CAN_I_SEE(target)) @@ -324,7 +337,7 @@ return ADD_TRAIT(src, TRAIT_NO_TRANSFORM, REVENANT_STUNNED_TRAIT) dormant = TRUE - update_mob_action_buttons() + update_ability_status() visible_message( span_warning("[src] lets out a waning screech as violet mist swirls around its dissolving body!"), @@ -405,28 +418,26 @@ apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, 2 SECONDS) return FALSE - if(locate(/obj/effect/blessing) in step_turf) + if(HAS_TRAIT(step_turf, TRAIT_TURF_BLESSED)) to_chat(src, span_warning("Holy energies block your path!")) return FALSE return TRUE +/mob/living/basic/revenant/proc/update_ability_status() + // Perform a shared check for all of our abilities + ability_density_locked = turf_density_check(silent = TRUE) + update_mob_action_buttons(UPDATE_BUTTON_STATUS) + ability_density_locked = null + /mob/living/basic/revenant/proc/cast_check(essence_cost, deduct_essence = TRUE, silent = FALSE) if(QDELETED(src)) - return - - var/turf/current = get_turf(src) - - if(isclosedturf(current)) - if(!silent) - to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall.")) return FALSE - for(var/obj/thing in current) - if(!thing.density || thing.CanPass(src, get_dir(current, src))) - continue + essence_cost = abs(essence_cost) * -1 + if(-essence_cost > essence) if(!silent) - to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object.")) + to_chat(src, span_revenwarning("You lack the essence to use that ability!")) return FALSE if(dormant) @@ -439,20 +450,37 @@ to_chat(src, span_revenwarning("Your powers have been suppressed by a nullifying energy!")) return FALSE - essence_cost = abs(essence_cost) * -1 - var/has_essence = deduct_essence ? change_essence_amount(essence_cost, silent = TRUE) : (essence + essence_cost >= 0) - if(!has_essence) - if(!silent) - to_chat(src, span_revenwarning("You lack the essence to use that ability!")) + if(ability_density_locked) return FALSE + // Don't run turf checks more than once if checking from a forced update + if(isnull(ability_density_locked) && turf_density_check(silent)) + return FALSE + + if(deduct_essence) + change_essence_amount(essence_cost, silent = TRUE) return TRUE +/mob/living/basic/revenant/proc/turf_density_check(silent = FALSE) + var/turf/current = get_turf(src) + if(isclosedturf(current)) + if(!silent) + to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall.")) + return TRUE + + for(var/obj/thing in current) + if(!thing.density || thing.CanPass(src, get_dir(current, src))) + continue + if(!silent) + to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object.")) + return TRUE + return FALSE + /mob/living/basic/revenant/proc/unlock(essence_cost) if(essence_excess < essence_cost) return FALSE essence_excess -= essence_cost - update_mob_action_buttons() + update_ability_status() return TRUE /mob/living/basic/revenant/proc/death_reset() @@ -467,7 +495,7 @@ incorporeal_move = INCORPOREAL_MOVE_JAUNT RemoveInvisibility(type) alpha = 255 - update_mob_action_buttons() + update_ability_status() /mob/living/basic/revenant/proc/change_essence_amount(essence_to_change_by, silent = FALSE, source = null) if(QDELETED(src)) @@ -483,7 +511,7 @@ essence_accumulated = max(0, essence_accumulated + essence_to_change_by) essence_excess = max(0, essence_excess + essence_to_change_by) - update_mob_action_buttons() + update_ability_status() if(!silent) if(essence_to_change_by > 0) to_chat(src, span_revennotice("Gained [essence_to_change_by]E [source ? "from [source]":""].")) @@ -498,11 +526,11 @@ . = ..() if(vname == NAMEOF(src, essence) || vname == NAMEOF(src, max_essence) || vname == NAMEOF(src, essence_excess)) update_health_hud() - update_mob_action_buttons() + update_ability_status() /mob/living/basic/revenant/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) . = ..() - update_mob_action_buttons() + update_ability_status() /mob/living/basic/revenant/proc/on_reflect(datum/source, atom/movable/reflecting_in, obj/effect/abstract/reflection) SIGNAL_HANDLER diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index 5202159878b1..6f41f5c490e7 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -112,56 +112,164 @@ reveal_duration = 4 SECONDS stun_duration = 2 SECONDS -/datum/action/cooldown/spell/aoe/revenant/defile/cast_on_thing_in_aoe(turf/victim, mob/living/basic/revenant/caster) - for(var/obj/effect/blessing/blessing in victim) - qdel(blessing) - new /obj/effect/temp_visual/revenant(victim) +/datum/action/cooldown/spell/aoe/revenant/defile/cast_on_thing_in_aoe(turf/target_turf, mob/living/basic/revenant/caster) + /// causes the purple sparks defile animation on the turf + var/turf_was_defiled = FALSE - if(!isplatingturf(victim) && !istype(victim, /turf/open/floor/engine/cult) && isfloorturf(victim) && prob(15)) - var/turf/open/floor/floor = victim + // dispel + if (HAS_TRAIT(target_turf, TRAIT_TURF_BLESSED)) + turf_was_defiled = TRUE + target_turf.RemoveElement(/datum/element/blessed_turf) + + for(var/obj/effect/decal/cleanable/food/salt/salt in target_turf) + turf_was_defiled = TRUE + qdel(salt) + + // damage + if(!isplatingturf(target_turf) && !istype(target_turf, /turf/open/floor/engine/cult) && isfloorturf(target_turf) && prob(15)) + var/turf/open/floor/floor = target_turf if(floor.overfloor_placed && floor.floor_tile) new floor.floor_tile(floor) floor.broken = 0 floor.burnt = 0 floor.make_plating(TRUE) + for(var/obj/structure/window/window in target_turf) + if(window.get_integrity() > REVENANT_DEFILE_MAX_DAMAGE) + window.take_damage(rand(REVENANT_DEFILE_MIN_DAMAGE, REVENANT_DEFILE_MAX_DAMAGE)) + if(window.fulltile) + new /obj/effect/temp_visual/revenant/cracks(window.loc) - if(victim.type == /turf/closed/wall && prob(15) && !HAS_TRAIT(victim, TRAIT_RUSTY)) - new /obj/effect/temp_visual/revenant(victim) - victim.AddElement(/datum/element/rust) - if(victim.type == /turf/closed/wall/r_wall && prob(10) && !HAS_TRAIT(victim, TRAIT_RUSTY)) - new /obj/effect/temp_visual/revenant(victim) - victim.AddElement(/datum/element/rust) - for(var/obj/machinery/shower/cursed_shower in victim) - new /obj/effect/temp_visual/revenant(victim) + // rust + if(target_turf.type == /turf/closed/wall && prob(15) && !HAS_TRAIT(target_turf, TRAIT_RUSTY)) + turf_was_defiled = TRUE + target_turf.AddElement(/datum/element/rust) + if(target_turf.type == /turf/closed/wall/r_wall && prob(10) && !HAS_TRAIT(target_turf, TRAIT_RUSTY)) + turf_was_defiled = TRUE + target_turf.AddElement(/datum/element/rust) + + // alchemy + for(var/obj/machinery/shower/cursed_shower in target_turf) + turf_was_defiled = TRUE cursed_shower.has_water_reclaimer = FALSE cursed_shower.reagents.remove_all(1, relative=TRUE) cursed_shower.reagents.add_reagent(/datum/reagent/blood, initial(cursed_shower.reagent_capacity)) if(prob(50)) cursed_shower.intended_on = TRUE cursed_shower.update_actually_on(TRUE) - for(var/obj/effect/decal/cleanable/food/salt/salt in victim) - new /obj/effect/temp_visual/revenant(victim) - qdel(salt) - for(var/obj/structure/closet/closet in victim.contents) + for(var/obj/item/reagent_containers/cursed_container in target_turf) + if(!cursed_container.is_open_container()) + continue + if(!cursed_container.reagents.has_reagent(/datum/reagent/consumable/ethanol, check_subtypes=TRUE)) + continue + + turf_was_defiled = TRUE + var/booze_amount = cursed_container.reagents.get_reagent_amount(/datum/reagent/consumable/ethanol, type_check=REAGENT_PARENT_TYPE) + cursed_container.reagents.remove_reagent(/datum/reagent/consumable/ethanol, booze_amount, include_subtypes=TRUE) + cursed_container.reagents.add_reagent(/datum/reagent/blood, booze_amount) + cursed_container.update_appearance() + for(var/obj/item/food/cursed_food in target_turf) + if(cursed_food.trash_type) + continue // the package protects it + var/datum/component/germ_sensitive/germs = cursed_food.GetComponent(/datum/component/germ_sensitive) + germs?.expose_to_germs() + + // opening + for(var/obj/structure/closet/closet in target_turf.contents) + if(closet.locked) + continue + + turf_was_defiled = TRUE closet.open() - for(var/obj/structure/bodycontainer/corpseholder in victim) + for(var/obj/structure/bodycontainer/corpseholder in target_turf) + turf_was_defiled = TRUE if(corpseholder.connected.loc == corpseholder) corpseholder.open() - for(var/obj/machinery/dna_scannernew/dna in victim) + for(var/obj/machinery/dna_scannernew/dna in target_turf) + turf_was_defiled = TRUE dna.open_machine() - for(var/obj/structure/window/window in victim) - if(window.get_integrity() > REVENANT_DEFILE_MAX_DAMAGE) - window.take_damage(rand(REVENANT_DEFILE_MIN_DAMAGE, REVENANT_DEFILE_MAX_DAMAGE)) - if(window.fulltile) - new /obj/effect/temp_visual/revenant/cracks(window.loc) - for(var/obj/machinery/light/light in victim) - light.flicker(rand(3, 5)) //spooky - for(var/obj/structure/mirror/mirror in victim) + for(var/obj/machinery/cryo_cell/cursed_cryo_cell in target_turf) + turf_was_defiled = TRUE + cursed_cryo_cell.open_machine() + for(var/obj/machinery/stasis/cursed_stasis in target_turf) + turf_was_defiled = TRUE + cursed_stasis.unbuckle_all_mobs() + + // obscure + for(var/obj/item/reagent_containers/applicator/patch/cursed_patch in target_turf) + turf_was_defiled = TRUE + cursed_patch.name = /obj/item/reagent_containers/applicator/patch::name + cursed_patch.desc = /obj/item/reagent_containers/applicator/patch::desc + cursed_patch.icon_state = /obj/item/reagent_containers/applicator/patch::icon_state + cursed_patch.update_appearance() + for(var/obj/item/storage/pill_bottle/cursed_pill_bottle in target_turf) + turf_was_defiled = TRUE + cursed_pill_bottle.name = /obj/item/storage/pill_bottle::name + cursed_pill_bottle.desc = /obj/item/storage/pill_bottle::desc + for(var/obj/item/reagent_containers/applicator/pill/cursed_pill in target_turf) + turf_was_defiled = TRUE + cursed_pill.name = /obj/item/reagent_containers/applicator/pill::name + cursed_pill.desc = /obj/item/reagent_containers/applicator/pill::desc + for(var/obj/item/reagent_containers/cup/bottle/cursed_bottle in target_turf) + turf_was_defiled = TRUE + cursed_bottle.name = /obj/item/reagent_containers/cup/bottle::name + cursed_bottle.desc = /obj/item/reagent_containers/cup/bottle::desc + for(var/obj/item/reagent_containers/syringe/cursed_syringe in target_turf) + turf_was_defiled = TRUE + cursed_syringe.name = /obj/item/reagent_containers/syringe::name + cursed_syringe.desc = /obj/item/reagent_containers/syringe::desc + for(var/obj/item/reagent_containers/hypospray/medipen/cursed_medipen in target_turf) + turf_was_defiled = TRUE + // the default medipen subtype is epinephrine so we need to use generic hardcoded name/desc + cursed_medipen.name = "medipen" + cursed_medipen.desc = "A rapid and safe way to inject reagents into patients for personnel without advanced medical knowledge." + for(var/obj/item/reagent_containers/medigel/cursed_medigel in target_turf) + turf_was_defiled = TRUE + cursed_medigel.name = /obj/item/reagent_containers/medigel::name + cursed_medigel.desc = /obj/item/reagent_containers/medigel::desc + cursed_medigel.icon_state = /obj/item/reagent_containers/medigel::icon_state + cursed_medigel.update_appearance() + for(var/obj/item/reagent_containers/spray/cursed_spray in target_turf) + turf_was_defiled = TRUE + cursed_spray.name = /obj/item/reagent_containers/spray::name + cursed_spray.desc = /obj/item/reagent_containers/spray::desc + cursed_spray.icon_state = /obj/item/reagent_containers/spray::icon_state + cursed_spray.update_appearance() + for(var/obj/item/dnainjector/cursed_dnainjector in target_turf) + turf_was_defiled = TRUE + cursed_dnainjector.name = /obj/item/dnainjector::name + cursed_dnainjector.desc = /obj/item/dnainjector::desc + for(var/obj/item/reagent_containers/blood/cursed_blood in target_turf) + turf_was_defiled = TRUE + cursed_blood.name = /obj/item/reagent_containers/blood::name + cursed_blood.desc = /obj/item/reagent_containers/blood::desc + for(var/obj/machinery/portable_atmospherics/canister/cursed_canister in target_turf) + turf_was_defiled = TRUE + cursed_canister.name = /obj/machinery/portable_atmospherics/canister::name + cursed_canister.desc = /obj/machinery/portable_atmospherics/canister::desc + cursed_canister.icon_state = /obj/machinery/portable_atmospherics/canister::icon_state + cursed_canister.base_icon_state = /obj/machinery/portable_atmospherics/canister::icon_state + cursed_canister.set_greyscale(/obj/machinery/portable_atmospherics/canister::greyscale_colors, /obj/machinery/portable_atmospherics/canister::greyscale_config) + //cursed_canister.update_appearance() + + for(var/obj/item/storage/lockbox/order/cursed_order in target_turf) + turf_was_defiled = TRUE + cursed_order.name = initial(cursed_order.name) // initial() is fine since we just want to remove the cargo info + for(var/obj/structure/closet/crate/cursed_crate in target_turf) + turf_was_defiled = TRUE + cursed_crate.name = initial(cursed_crate.name) + + // spooky + for(var/obj/machinery/light/light in target_turf) + light.flicker(rand(3, 5)) + for(var/obj/structure/mirror/mirror in target_turf) if(istype(mirror, /obj/structure/mirror/magic)) continue - new /obj/effect/temp_visual/revenant(mirror.loc) + turf_was_defiled = TRUE mirror.atom_break("magic") + if(turf_was_defiled) + new /obj/effect/temp_visual/revenant(target_turf) + //Malfunction: Makes bad stuff happen to robots and machines. /datum/action/cooldown/spell/aoe/revenant/malfunction name = "Malfunction" @@ -304,5 +412,48 @@ despawn_message = span_revenwarning("[victim] falls back to the ground, stationary once more."), \ ) +//Vortex: Causes storage objects to dump their contents and nearby objects to get sucked into the center +/datum/action/cooldown/spell/aoe/revenant/vortex + name = "Vortex" + desc = "Causes nearby objects to dump their contents and get sucked towards your location." + button_icon_state = "r_vortex" + cooldown_time = 15 SECONDS + aoe_radius = 1 // only dump contents close by + unlock_amount = 45 + cast_amount = 30 + stun_duration = 2 SECONDS + reveal_duration = 5 SECONDS + +/datum/action/cooldown/spell/aoe/revenant/vortex/cast_on_thing_in_aoe(turf/target_turf, mob/living/basic/revenant/caster) + for(var/obj/item/storage/cursed_storage in target_turf) + if(cursed_storage.atom_storage?.locked) + continue + + cursed_storage.emptyStorage() + new /obj/effect/temp_visual/revenant(target_turf) + +/datum/action/cooldown/spell/aoe/revenant/vortex/cast(atom/cast_on) + movement_effect(cast_on) + . = ..() + +/datum/action/cooldown/spell/aoe/revenant/vortex/proc/movement_effect(atom/cast_on) + goonchem_vortex(get_turf(cast_on), FALSE, 3) + playsound(cast_on, 'sound/machines/woosh.ogg', 50, TRUE) + +//Vortex: Throws nearby objects away with a large force +/datum/action/cooldown/spell/aoe/revenant/vortex/scatter + name = "Scatter" + desc = "Causes nearby objects to dump their contents and get thrown away from your location." + button_icon_state = "r_scatter" + cooldown_time = 25 SECONDS + unlock_amount = 60 + cast_amount = 55 + stun_duration = 3 SECONDS + reveal_duration = 5 SECONDS + +/datum/action/cooldown/spell/aoe/revenant/vortex/scatter/movement_effect(atom/cast_on) + goonchem_vortex(get_turf(cast_on), TRUE, 5) // pushing is much stronger than pulling + playsound(cast_on, 'sound/machines/hiss.ogg', 50, TRUE) + #undef REVENANT_DEFILE_MIN_DAMAGE #undef REVENANT_DEFILE_MAX_DAMAGE diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm index 9ce61bf478f0..91476554e4fc 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_harvest.dm @@ -38,7 +38,7 @@ var/target_Theyre = target.p_Theyre() var/target_They_have = "[target.p_They()] [target.p_have()]" - if(target.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(target)) to_chat(src, span_revennotice("[target_Their] soul is too strong to harvest.")) if(prob(10)) to_chat(target, span_revennotice("You feel as if you are being watched.")) @@ -86,7 +86,7 @@ to_chat(src, span_revenwarning("You are not close enough to siphon [target ? "[target]'s" : "[target_their]"] soul. The link has been broken.")) return FALSE - if(target.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(target)) to_chat(src, span_revenwarning("[target_Theyre] now powerful enough to fight off your draining!")) to_chat(target, span_bolddanger("You feel something tugging across your body before subsiding.")) //hey, wait a minute... return FALSE @@ -95,7 +95,7 @@ if(target.stat != DEAD) to_chat(target, span_warning("You feel a horribly unpleasant draining sensation as your grip on life weakens...")) if(target.stat == SOFT_CRIT) - target.Stun(46) + target.Stun(4.6 SECONDS) apply_status_effect(/datum/status_effect/revenant/revealed, 5 SECONDS) apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, 5 SECONDS) diff --git a/code/modules/mob/living/basic/space_fauna/roro.bt.json b/code/modules/mob/living/basic/space_fauna/roro.bt.json new file mode 100644 index 000000000000..cddeb4b8f1dd --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/roro.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/roro", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" +} diff --git a/code/modules/mob/living/basic/space_fauna/roro.dm b/code/modules/mob/living/basic/space_fauna/roro.dm index b9c4c9e759c2..43bdd1181403 100644 --- a/code/modules/mob/living/basic/space_fauna/roro.dm +++ b/code/modules/mob/living/basic/space_fauna/roro.dm @@ -55,15 +55,10 @@ AddElement(/datum/element/ai_retaliate) /datum/ai_controller/basic_controller/roro + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/roro.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/space_fauna/snake/banded.bt.json b/code/modules/mob/living/basic/space_fauna/snake/banded.bt.json new file mode 100644 index 000000000000..a05207e90eec --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/snake/banded.bt.json @@ -0,0 +1,92 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/snake/banded", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/snake/banded_snake.dm b/code/modules/mob/living/basic/space_fauna/snake/banded_snake.dm index f64208035438..811f092251b5 100644 --- a/code/modules/mob/living/basic/space_fauna/snake/banded_snake.dm +++ b/code/modules/mob/living/basic/space_fauna/snake/banded_snake.dm @@ -42,20 +42,18 @@ return . /datum/ai_controller/basic_controller/snake/banded + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/snake/banded.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("hsssss", "sssSSsssss...", "hiisssss"), + BB_EMOTE_HEAR = list("hisses."), + BB_EMOTE_SEE = list("slithers around.", "glances.", "stares."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/snake/snake_hissing1.ogg', 'sound/mobs/non-humanoids/snake/snake_hissing2.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/random_speech/snake, - ) - /mob/living/basic/snake/banded/harmless venom_dose = 0.4 diff --git a/code/modules/mob/living/basic/space_fauna/snake/snake.bt.json b/code/modules/mob/living/basic/space_fauna/snake/snake.bt.json new file mode 100644 index 000000000000..6748ce550795 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/snake/snake.bt.json @@ -0,0 +1,92 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/snake", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/snake/snake.dm b/code/modules/mob/living/basic/space_fauna/snake/snake.dm index aecfca8062a2..985f72a4ef84 100644 --- a/code/modules/mob/living/basic/space_fauna/snake/snake.dm +++ b/code/modules/mob/living/basic/space_fauna/snake/snake.dm @@ -74,18 +74,17 @@ /// Snakes are primarily concerned with getting those tasty, tasty mice, but aren't afraid to strike back at those who attack them /datum/ai_controller/basic_controller/snake + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/snake/snake.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SAY = list("hsssss", "sssSSsssss...", "hiisssss"), + BB_EMOTE_HEAR = list("hisses."), + BB_EMOTE_SEE = list("slithers around.", "glances.", "stares."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/snake/snake_hissing1.ogg', 'sound/mobs/non-humanoids/snake/snake_hissing2.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/random_speech/snake, - ) diff --git a/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm b/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm deleted file mode 100644 index 189a7fb2eb09..000000000000 --- a/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/ai_planning_subtree/random_speech/snake - speech_chance = 5 - speak = list("hsssss","sssSSsssss...","hiisssss") - sound = list('sound/mobs/non-humanoids/snake/snake_hissing1.ogg', 'sound/mobs/non-humanoids/snake/snake_hissing2.ogg') - emote_hear = list("hisses.") - emote_see = list("slithers around.", "glances.", "stares.") diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm index e8ebb18a47ac..72a49814ebc6 100644 --- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm +++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm @@ -136,7 +136,7 @@ rename_dragon() return to_chat(src, span_notice("Your name is now [span_name("[chosen_name]")], the feared Space Dragon.")) - fully_replace_character_name(null, chosen_name) + fully_replace_character_name(null, chosen_name, log_new_name = TRUE) /// Select scale colour with the colour picker /mob/living/basic/space_dragon/proc/select_colour() diff --git a/code/modules/mob/living/basic/space_fauna/spaceman.bt.json b/code/modules/mob/living/basic/space_fauna/spaceman.bt.json new file mode 100644 index 000000000000..c198789ac24b --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spaceman.bt.json @@ -0,0 +1,14 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/spaceman", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spaceman.dm b/code/modules/mob/living/basic/space_fauna/spaceman.dm index ecb413bb81d7..6453a15719da 100644 --- a/code/modules/mob/living/basic/space_fauna/spaceman.dm +++ b/code/modules/mob/living/basic/space_fauna/spaceman.dm @@ -31,15 +31,9 @@ AddElement(/datum/element/ai_retaliate) /datum/ai_controller/basic_controller/spaceman + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spaceman.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.json b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.json new file mode 100644 index 000000000000..b8697161b74e --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.json @@ -0,0 +1,133 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/giant_spider", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SPIDER_WEB_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/spin_web", + "vars": { + "action_key": "BB_SPIDER_WEB_ACTION", + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_unwebbed_turf", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm index ae11657338b5..02fb234525e8 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm @@ -1,63 +1,35 @@ /// Attacks people it can see, spins webs if it can't see anything to attack. /datum/ai_controller/basic_controller/giant_spider + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), // Space spiders are taxonomically insects not arachnids, don't DM me + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/insect, // Space spiders are taxonomically insects not arachnids, don't DM me - /datum/ai_planning_subtree/find_unwebbed_turf, - /datum/ai_planning_subtree/spin_web, - ) /// Giant spider which won't attack structures /datum/ai_controller/basic_controller/giant_spider/weak - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/find_unwebbed_turf, - /datum/ai_planning_subtree/spin_web, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.json" /// Used by Araneus, who only attacks those who attack first. He is house-trained and will not web up the HoS office. /datum/ai_controller/basic_controller/giant_spider/retaliate - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/insect, - ) + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.json" /// Retaliates, hunts other maintenance creatures, runs away from larger attackers, and spins webs. /datum/ai_controller/basic_controller/giant_spider/pest + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/ours_or_smaller, // Hunt mobs our size BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Run away from mobs bigger than we are - ) - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/find_unwebbed_turf, - /datum/ai_planning_subtree/spin_web, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.json b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.json new file mode 100644 index 000000000000..5e8565657576 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_pest.bt.json @@ -0,0 +1,150 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/giant_spider/pest", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_BASIC_MOB_FLEE_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SPIDER_WEB_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/spin_web", + "vars": { + "action_key": "BB_SPIDER_WEB_ACTION", + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION" + } + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_unwebbed_turf", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.json b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.json new file mode 100644 index 000000000000..970784d7bf15 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_retaliate.bt.json @@ -0,0 +1,78 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/giant_spider/retaliate", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm deleted file mode 100644 index 20f6ce4baf08..000000000000 --- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm +++ /dev/null @@ -1,89 +0,0 @@ -/// Search for a nearby location to put webs on -/datum/ai_planning_subtree/find_unwebbed_turf - -/datum/ai_planning_subtree/find_unwebbed_turf/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - controller.queue_behavior(/datum/ai_behavior/find_unwebbed_turf) - -/// Find an unwebbed nearby turf and store it -/datum/ai_behavior/find_unwebbed_turf - action_cooldown = 5 SECONDS - /// Where do we store the target data - var/target_key = BB_SPIDER_WEB_TARGET - /// How far do we look for unwebbed turfs? - var/scan_range = 3 - -/datum/ai_behavior/find_unwebbed_turf/perform(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/spider = controller.pawn - var/atom/current_target = controller.blackboard[target_key] - if (current_target && !(locate(/obj/structure/spider/stickyweb) in current_target)) - // Already got a target - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.clear_blackboard_key(target_key) - var/turf/our_turf = get_turf(spider) - if (is_valid_web_turf(our_turf, spider)) - controller.set_blackboard_key(target_key, our_turf) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - - var/list/turfs_by_range = list() - for (var/i in 1 to scan_range) - turfs_by_range["[i]"] = list() - for (var/turf/turf_in_view in oview(scan_range, our_turf)) - if (!is_valid_web_turf(turf_in_view, spider)) - continue - turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view - - var/list/final_turfs - for (var/list/turf_list as anything in turfs_by_range) - if (length(turfs_by_range[turf_list])) - final_turfs = turfs_by_range[turf_list] - break - if (!length(final_turfs)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - controller.set_blackboard_key(target_key, pick(final_turfs)) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/ai_behavior/find_unwebbed_turf/proc/is_valid_web_turf(turf/target_turf, mob/living/spider) - if (locate(/obj/structure/spider/stickyweb) in target_turf) - return FALSE - if (HAS_TRAIT(target_turf, TRAIT_SPINNING_WEB_TURF)) - return FALSE - return !target_turf.is_blocked_turf(source_atom = spider) - -/// Run the spin web behaviour if we have an ability to use for it -/datum/ai_planning_subtree/spin_web - /// Key where the web spinning action is stored - var/action_key = BB_SPIDER_WEB_ACTION - /// Key where the target turf is stored - var/target_key = BB_SPIDER_WEB_TARGET - -/datum/ai_planning_subtree/spin_web/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (controller.blackboard_key_exists(action_key) && controller.blackboard_key_exists(target_key)) - controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key) - return SUBTREE_RETURN_FINISH_PLANNING - -/// Move to an unwebbed nearby turf and web it up -/datum/ai_behavior/spin_web - action_cooldown = 15 SECONDS // We don't want them doing this too quickly - required_distance = 0 - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/spin_web/setup(datum/ai_controller/controller, action_key, target_key) - var/datum/action/cooldown/web_action = controller.blackboard[action_key] - var/turf/target_turf = controller.blackboard[target_key] - if (!web_action || !target_turf) - return FALSE - - set_movement_target(controller, target_turf) - return ..() - -/datum/ai_behavior/spin_web/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key) - var/datum/action/cooldown/web_action = controller.blackboard[action_key] - if(web_action?.Trigger()) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - -/datum/ai_behavior/spin_web/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key) - controller.clear_blackboard_key(target_key) - return ..() diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.json b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.json new file mode 100644 index 000000000000..42b2f434962b --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_weak.bt.json @@ -0,0 +1,121 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/giant_spider/weak", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SPIDER_WEB_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/spin_web", + "vars": { + "action_key": "BB_SPIDER_WEB_ACTION", + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_unwebbed_turf", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.json b/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.json new file mode 100644 index 000000000000..0d0f9795a7c1 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.json @@ -0,0 +1,69 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/spiderling", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/consider_venting" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": false, + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_ENTRY_VENT_TARGET", + "target_source": "/datum/target_source/oview_single_type/vent_pump", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 7 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.dm b/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.dm index e7120ba34cd6..130d22134c1f 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.dm @@ -60,21 +60,17 @@ /// Opportunistically hops in and out of vents, if it can find one. We aren't interested in attacking due to how weak we are, we gotta be quick and hidey. /datum/ai_controller/basic_controller/spiderling + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/spiderlings/spiderling.bt.json" blackboard = list( - BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Run away from mobs bigger than we are + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Run away from mobs bigger than we are BB_VENTCRAWL_COOLDOWN = 20 SECONDS, // enough time to get splatted while we're out in the open. BB_TIME_TO_GIVE_UP_ON_VENT_PATHING = 30 SECONDS, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - - // We understand that vents are nice little hidey holes through epigenetic inheritance, so we'll use them. - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/opportunistic_ventcrawler, - /datum/ai_planning_subtree/random_speech/insect, - ) diff --git a/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.json b/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.json new file mode 100644 index 000000000000..f042cc5d9460 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.json @@ -0,0 +1,149 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/young_spider", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1 + } + } + ] + } + ] + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SPIDER_WEB_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/spin_web", + "vars": { + "action_key": "BB_SPIDER_WEB_ACTION", + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + } + } + ] + }, + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_unwebbed_turf", + "vars": { + "target_key": "BB_SPIDER_WEB_TARGET" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.dm b/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.dm index 436602b71ba0..a739ef2e9ece 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.dm @@ -28,26 +28,19 @@ /// Used by all young spiders if they ever appear. /datum/ai_controller/basic_controller/young_spider + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/spider/young_spider/young_spider.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_BASIC_MOB_FLEE_DISTANCE = 6, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/find_unwebbed_turf, - /datum/ai_planning_subtree/spin_web, - ) /mob/living/basic/spider/growing/young/start_pulling(atom/movable/pulled_atom, state, force = move_force, supress_message = FALSE) // we're TOO FUCKING WEAK return diff --git a/code/modules/mob/living/basic/space_fauna/statue/mannequin.dm b/code/modules/mob/living/basic/space_fauna/statue/mannequin.dm index 03067edbc455..967cd4922657 100644 --- a/code/modules/mob/living/basic/space_fauna/statue/mannequin.dm +++ b/code/modules/mob/living/basic/space_fauna/statue/mannequin.dm @@ -30,42 +30,13 @@ . += mutable_appearance(hat::worn_icon, hat::worn_icon_state || hat::post_init_icon_state || hat::icon_state) /datum/ai_controller/basic_controller/stares_at_people + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_AGGRO_RANGE = 6, ) ai_movement = /datum/ai_movement/dumb - idle_behavior = null - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/face_target_or_face_initial, // we be creepy and all - ) - -/datum/ai_planning_subtree/face_target_or_face_initial - -/datum/ai_planning_subtree/face_target_or_face_initial/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(isnull(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET])) - return - var/mob/living/we = controller.pawn - controller.blackboard[BB_STARTING_DIRECTION] = we.dir - controller.queue_behavior(/datum/ai_behavior/face_target_or_face_initial, BB_BASIC_MOB_CURRENT_TARGET) - -/datum/ai_behavior/face_target_or_face_initial - -/datum/ai_behavior/face_target_or_face_initial/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/movable/target = controller.blackboard[target_key] - return ismovable(target) && isturf(target.loc) && ismob(controller.pawn) - -/datum/ai_behavior/face_target_or_face_initial/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - var/atom/movable/target = controller.blackboard[target_key] - var/mob/living/we = controller.pawn - if(isnull(target) || get_dist(we, target) > 8) - we.dir = controller.blackboard[BB_STARTING_DIRECTION] - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - we.face_atom(target) - return AI_BEHAVIOR_DELAY /mob/living/basic/statue/mannequin/suspicious name = "mannequin?" @@ -75,17 +46,9 @@ ai_controller = /datum/ai_controller/basic_controller/suspicious_mannequin /datum/ai_controller/basic_controller/suspicious_mannequin + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_AGGRO_RANGE = 14, - BB_EMOTE_KEY = "scream", //spooky ) - ai_movement = /datum/ai_movement/jps //threat - idle_behavior = null - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/run_emote, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + ai_movement = /datum/ai_movement/basic_avoidance diff --git a/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.json b/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.json new file mode 100644 index 000000000000..b77a35e0108c --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/statue/stares_at_people.bt.json @@ -0,0 +1,44 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/stares_at_people", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/face_target_or_face_initial", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/statue/statue.bt.json b/code/modules/mob/living/basic/space_fauna/statue/statue.bt.json new file mode 100644 index 000000000000..b999f3c6eed5 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/statue/statue.bt.json @@ -0,0 +1,125 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/statue", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "movement_failed": false + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "hunt_cooldown": "10 SECONDS", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true, + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/hunt_target_list", + "targeting_strategy": "/datum/targeting_strategy/unbroken_light", + "vision_range": 7 + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/statue/statue.dm b/code/modules/mob/living/basic/space_fauna/statue/statue.dm index b4bb04673635..bb62acade2bc 100644 --- a/code/modules/mob/living/basic/space_fauna/statue/statue.dm +++ b/code/modules/mob/living/basic/space_fauna/statue/statue.dm @@ -64,6 +64,7 @@ /datum/action/cooldown/spell/aoe/flicker_lights, ) grant_actions_by_list(innate_actions) + ai_controller.set_blackboard_key(BB_HUNT_TARGET_LIST, typecacheof(list(/obj/machinery/light))) /mob/living/basic/statue/med_hud_set_health() return //we're a statue we're invincible @@ -130,17 +131,12 @@ victim.adjust_temp_blindness(8 SECONDS) /datum/ai_controller/basic_controller/statue + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/statue/statue.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) - ai_movement = /datum/ai_movement/basic_avoidance - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures, - ) + ai_movement = /datum/ai_movement/jps /mob/living/basic/statue/frosty name = "Frosty" diff --git a/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.json b/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.json new file mode 100644 index 000000000000..f9143d9986c0 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/statue/suspicious_mannequin.bt.json @@ -0,0 +1,114 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/suspicious_mannequin", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/wait" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "vision_range": 14 + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/perform_emote", + "vars": { + "emote": "\"scream\"" + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.json b/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.json new file mode 100644 index 000000000000..49c3e9f075f4 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/supermatter_spider", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat", + "bindings": { + "b95z0f0c": "/datum/bt_node/subtree/random_speech_loop" + } +} diff --git a/code/modules/mob/living/basic/space_fauna/supermatter_spider.dm b/code/modules/mob/living/basic/space_fauna/supermatter_spider.dm index 44d7585faffe..fdc0194999c1 100644 --- a/code/modules/mob/living/basic/space_fauna/supermatter_spider.dm +++ b/code/modules/mob/living/basic/space_fauna/supermatter_spider.dm @@ -83,22 +83,14 @@ single_use = FALSE /datum/ai_controller/basic_controller/supermatter_spider + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/supermatter_spider.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("clinks.", "clanks."), + BB_EMOTE_SEE = list("vibrates."), + BB_SPEAK_CHANCE = 7, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/random_speech/supermatter_spider, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/random_speech/supermatter_spider - speech_chance = 7 - emote_hear = list("clinks", "clanks") - emote_see = list("vibrates") diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm index f936caa1d51e..77673e26e0aa 100644 --- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm @@ -23,7 +23,7 @@ return NONE var/mob/living/animal = interacting_with - if(animal.stat == DEAD || HAS_TRAIT(animal, TRAIT_FAKEDEATH)) + if(IS_DEAD_OR_FAKING(animal)) balloon_alert(user, "it's dead!") return ITEM_INTERACT_BLOCKING if(HAS_TRAIT(animal, TRAIT_FUGU_GLANDED)) diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm index 651cd8484582..288333add5e4 100644 --- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm @@ -66,7 +66,7 @@ fugu.melee_damage_upper = 20 fugu.obj_damage = 60 fugu.ai_controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, TRUE) - fugu.ai_controller.CancelActions() + fugu.ai_controller.cancel_current_plan() /datum/status_effect/inflated/on_remove() . = ..() @@ -84,7 +84,7 @@ fugu.icon_state = "Fugu0" fugu.obj_damage = 0 fugu.ai_controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, FALSE) - fugu.ai_controller.CancelActions() + fugu.ai_controller.cancel_current_plan() /// Remove status effect if we die /datum/status_effect/inflated/proc/check_death(mob/living/source, new_stat) diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm index 9664ad5d96f3..ab7deaab7d7e 100644 --- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm @@ -1,27 +1,6 @@ /// Cowardly when small, aggressive when big. Tries to transform whenever possible. /datum/ai_controller/basic_controller/wumborian_fugu + behavior_tree_json = "code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) - - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/targeted_mob_ability/inflate, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/wumborian_fugu, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) - -/datum/ai_planning_subtree/attack_obstacle_in_path/wumborian_fugu - attack_behaviour = /datum/ai_behavior/attack_obstructions/wumborian_fugu - -/datum/ai_behavior/attack_obstructions/wumborian_fugu - can_attack_turfs = TRUE - action_cooldown = 2.5 SECONDS - -/datum/ai_planning_subtree/targeted_mob_ability/inflate - ability_key = BB_FUGU_INFLATE diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.json b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.json new file mode 100644 index 000000000000..bcff12180806 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.bt.json @@ -0,0 +1,86 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/wumborian_fugu", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/targeted_mob_ability", + "vars": { + "ability_key": "BB_FUGU_INFLATE", + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions/attack_turfs", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm index dd966839bbec..613806024e65 100644 --- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm @@ -52,6 +52,7 @@ . = ..() AddComponent(/datum/component/seethrough_mob) AddElement(/datum/element/death_drops, /obj/item/fugu_gland) + AddElement(/datum/element/simple_flying) add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), ROUNDSTART_TRAIT) expand = new(src) expand.Grant(src) diff --git a/code/modules/mob/living/basic/stoats/stoat.bt.json b/code/modules/mob/living/basic/stoats/stoat.bt.json new file mode 100644 index 000000000000..9c8d273c7433 --- /dev/null +++ b/code/modules/mob/living/basic/stoats/stoat.bt.json @@ -0,0 +1,123 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/stoat", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/make_babies" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_dragging", + "vars": { + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_LAST_STOLEN_ITEM" + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/steal_and_flee" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_stealable_object" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_partner" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/stoats/stoat.dm b/code/modules/mob/living/basic/stoats/stoat.dm index d9730e4c4f50..3f71e5730fe5 100644 --- a/code/modules/mob/living/basic/stoats/stoat.dm +++ b/code/modules/mob/living/basic/stoats/stoat.dm @@ -48,6 +48,7 @@ AddElement(/datum/element/wears_collar) AddElement(/datum/element/can_be_held) AddComponent(/datum/component/obeys_commands, pet_commands) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_STOAT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) if(can_breed) add_breeding_component() diff --git a/code/modules/mob/living/basic/stoats/stoat_ai.dm b/code/modules/mob/living/basic/stoats/stoat_ai.dm index ecefb0fd93a1..181baf573dc0 100644 --- a/code/modules/mob/living/basic/stoats/stoat_ai.dm +++ b/code/modules/mob/living/basic/stoats/stoat_ai.dm @@ -1,23 +1,15 @@ /datum/ai_controller/basic_controller/stoat + behavior_tree_json = "code/modules/mob/living/basic/stoats/stoat.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/smaller, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_GUILTY_CONSCIOUS_CHANCE = 5, - BB_STEAL_CHANCE = 2, + BB_STEAL_CHANCE = 25, BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/stoat), BB_BABIES_CHILD_TYPES = list(/mob/living/basic/stoat/kit), + BB_FUCKS = TRUE ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/random_speech/blackboard, - /datum/ai_planning_subtree/steal_items, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/make_babies, - ) /datum/ai_controller/basic_controller/stoat/kit blackboard = list( @@ -25,14 +17,6 @@ BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_GUILTY_CONSCIOUS_CHANCE = 5, BB_STEAL_CHANCE = 2, + BB_FUCKS = FALSE ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/random_speech/blackboard, - /datum/ai_planning_subtree/steal_items, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_food, - ) diff --git a/code/modules/mob/living/basic/trader/trader.bt.json b/code/modules/mob/living/basic/trader/trader.bt.json new file mode 100644 index 000000000000..f80ea64cf52a --- /dev/null +++ b/code/modules/mob/living/basic/trader/trader.bt.json @@ -0,0 +1,154 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trader", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity/pacifist" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "avoid_friendly_fire": true, + "time_between_perform": "3 SECONDS" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "invert": true, + "key": "BB_SHOP_SPOT" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_FIRST_CUSTOMER" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_TRADER_RUSH_TO_SELL", + "value": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FIRST_CUSTOMER", + "required_dist": 1 + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/setup_shop" + } + ] + } + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk/not_while_on_target", + "vars": { + "target_key": "BB_SHOP_SPOT" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target", + "vars": { + "target_key": "BB_FIRST_CUSTOMER", + "target_source": "/datum/target_source/oview", + "targeting_strategy": "/datum/targeting_strategy/capable_human", + "revalidation_mode": "TARGET_REVALIDATE" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/trader/trader.dm b/code/modules/mob/living/basic/trader/trader.dm index c23726a562bb..3862fbd46b2e 100644 --- a/code/modules/mob/living/basic/trader/trader.dm +++ b/code/modules/mob/living/basic/trader/trader.dm @@ -49,7 +49,7 @@ AddComponent(/datum/component/trader, trader_data = trader_data) AddComponent(/datum/component/ranged_attacks, casing_type = ranged_attack_casing, projectile_sound = ranged_attack_sound, cooldown_time = 3 SECONDS) AddElement(/datum/element/ai_retaliate) - AddElement(/datum/element/ai_swap_combat_mode, BB_BASIC_MOB_CURRENT_TARGET, string_list(trader_data.say_phrases[TRADER_BATTLE_START_PHRASE]), string_list(trader_data.say_phrases[TRADER_BATTLE_END_PHRASE])) + AddElement(/datum/element/ai_swap_combat_mode, BB_CURRENT_TARGET, string_list(trader_data.say_phrases[TRADER_BATTLE_START_PHRASE]), string_list(trader_data.say_phrases[TRADER_BATTLE_END_PHRASE])) if(LAZYLEN(loot)) loot = string_list(loot) AddElement(/datum/element/death_drops, loot) @@ -58,6 +58,9 @@ setup_shop.Grant(src) ai_controller.set_blackboard_key(BB_SETUP_SHOP, setup_shop) +/mob/living/basic/trader/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/basic/trader/mrbones name = "Mr. Bones" desc = "A skeleton merchant, he seems very humerus." diff --git a/code/modules/mob/living/basic/trader/trader_ai.dm b/code/modules/mob/living/basic/trader/trader_ai.dm index f26ec242ed12..e7d9b6ba6ff9 100644 --- a/code/modules/mob/living/basic/trader/trader_ai.dm +++ b/code/modules/mob/living/basic/trader/trader_ai.dm @@ -1,93 +1,34 @@ /datum/ai_controller/basic_controller/trader + behavior_tree_json = "code/modules/mob/living/basic/trader/trader.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TRADER_RUSH_TO_SELL = FALSE ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/not_while_on_target/trader - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trader, - /datum/ai_planning_subtree/prepare_travel_to_destination/trader, - /datum/ai_planning_subtree/travel_to_point/and_clear_target, - /datum/ai_planning_subtree/setup_shop, - ) /datum/ai_controller/basic_controller/trader/jumpscare - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity/pacifist, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trader, - /datum/ai_planning_subtree/prepare_travel_to_destination/trader, - /datum/ai_planning_subtree/travel_to_point/and_clear_target, - /datum/ai_planning_subtree/setup_shop/jumpscare, + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TRADER_RUSH_TO_SELL = TRUE ) -/datum/ai_planning_subtree/basic_ranged_attack_subtree/trader - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trader +///Triggers the shop setup action and clears the first customer key +/datum/bt_node/ai_behavior/setup_shop -/datum/ai_behavior/basic_ranged_attack/trader - action_cooldown = 3 SECONDS - avoid_friendly_fire = TRUE +/datum/bt_node/ai_behavior/setup_shop/perform(seconds_per_tick, datum/ai_controller/controller) + var/async_flags = handle_async() + if(async_flags) + return async_flags -///Subtree to find our very first customer and set up our shop after walking right into their face -/datum/ai_planning_subtree/setup_shop - ///What do we do in order to offer our deals? - var/datum/ai_behavior/setup_shop/setup_shop_behavior = /datum/ai_behavior/setup_shop - -/datum/ai_planning_subtree/setup_shop/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - //If we don't have our ability, return - if(!controller.blackboard_key_exists(BB_SETUP_SHOP)) - return + return start_async() - //If we already have a shop spot, return - if(controller.blackboard_key_exists(BB_SHOP_SPOT)) - return - - //If we don't have a costurmer to greet, look for one - if(!controller.blackboard_key_exists(BB_FIRST_CUSTOMER)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_FIRST_CUSTOMER, /mob/living/carbon/human) +/datum/bt_node/ai_behavior/setup_shop/perform_async(datum/ai_controller/controller) + var/datum/action/setup_shop/shop = controller.blackboard[BB_SETUP_SHOP] + if(!async_still_valid()) return - - //We have our first customer, time to tell them about incredible deals - controller.queue_behavior(setup_shop_behavior, BB_FIRST_CUSTOMER) - return SUBTREE_RETURN_FINISH_PLANNING - -///The ai will create a shop the moment they see a potential costumer -/datum/ai_behavior/setup_shop - -/datum/ai_behavior/setup_shop/setup(datum/ai_controller/controller, target_key) - var/obj/target = controller.blackboard[target_key] - return !QDELETED(target) - -/datum/ai_behavior/setup_shop/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - //We lost track of our costumer or our ability, abort - if(!controller.blackboard_key_exists(target_key) || !controller.blackboard_key_exists(BB_SETUP_SHOP)) + if(!shop || !controller.blackboard_key_exists(BB_FIRST_CUSTOMER)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - - var/datum/action/setup_shop/shop = controller.blackboard[BB_SETUP_SHOP] - shop.Trigger() - + var/result = shop.Trigger() controller.clear_blackboard_key(BB_FIRST_CUSTOMER) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/datum/idle_behavior/idle_random_walk/not_while_on_target/trader - target_key = BB_SHOP_SPOT - -///Version of setup show where the trader will run at you to assault you with incredible deals -/datum/ai_planning_subtree/setup_shop/jumpscare - setup_shop_behavior = /datum/ai_behavior/setup_shop/jumpscare - -/datum/ai_behavior/setup_shop/jumpscare - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH - -/datum/ai_behavior/setup_shop/jumpscare/setup(datum/ai_controller/controller, target_key) - . = ..() - if(.) - set_movement_target(controller, controller.blackboard[target_key]) - -/datum/ai_behavior/setup_shop/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) + finish_async(result ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED) diff --git a/code/modules/mob/living/basic/tree.bt.json b/code/modules/mob/living/basic/tree.bt.json new file mode 100644 index 000000000000..ea3f7573b115 --- /dev/null +++ b/code/modules/mob/living/basic/tree.bt.json @@ -0,0 +1,8 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/tree", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_hostile_obstacles_combat", + "bindings": { + "b95z0f0c": "/datum/bt_node/subtree/random_speech_loop" + } +} diff --git a/code/modules/mob/living/basic/tree.dm b/code/modules/mob/living/basic/tree.dm index 5cf9c1bf27a2..c9714103a8c4 100644 --- a/code/modules/mob/living/basic/tree.dm +++ b/code/modules/mob/living/basic/tree.dm @@ -100,15 +100,13 @@ ) /datum/ai_controller/basic_controller/tree + behavior_tree_json = "code/modules/mob/living/basic/tree.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_SEE = list("photosynthesizes angrily."), + BB_SPEAK_CHANCE = 3, + ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/tree, - ) diff --git a/code/modules/mob/living/basic/trooper/burst.bt.json b/code/modules/mob/living/basic/trooper/burst.bt.json new file mode 100644 index 000000000000..665988d1dc7c --- /dev/null +++ b/code/modules/mob/living/basic/trooper/burst.bt.json @@ -0,0 +1,9 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper/ranged/burst", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/trooper_ranged", + "bindings": { + "b4kar3zl": "3 SECONDS", + "bnr1aazo": "TRUE" + } +} diff --git a/code/modules/mob/living/basic/trooper/nanotrasen.dm b/code/modules/mob/living/basic/trooper/nanotrasen.dm index 8798467969b9..a2494a5aad44 100644 --- a/code/modules/mob/living/basic/trooper/nanotrasen.dm +++ b/code/modules/mob/living/basic/trooper/nanotrasen.dm @@ -87,7 +87,7 @@ /mob/living/basic/trooper/nanotrasen/ranged/smg/peaceful desc = "An officer of Nanotrasen's private security force." - ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful + ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful_burst /mob/living/basic/trooper/nanotrasen/ranged/smg/peaceful/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/basic/trooper/peaceful.bt.json b/code/modules/mob/living/basic/trooper/peaceful.bt.json new file mode 100644 index 000000000000..17262a5b37af --- /dev/null +++ b/code/modules/mob/living/basic/trooper/peaceful.bt.json @@ -0,0 +1,138 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper/peaceful", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_CALL_REINFORCEMENTS_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements", + "vars": { + "target_key": "BB_CALL_REINFORCEMENTS_TARGET" + } + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CALL_REINFORCEMENTS_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/trooper/peaceful_burst.bt.json b/code/modules/mob/living/basic/trooper/peaceful_burst.bt.json new file mode 100644 index 000000000000..b77b87056422 --- /dev/null +++ b/code/modules/mob/living/basic/trooper/peaceful_burst.bt.json @@ -0,0 +1,140 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful_burst", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_CALL_REINFORCEMENTS_TARGET" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements", + "vars": { + "target_key": "BB_CALL_REINFORCEMENTS_TARGET" + } + } + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "time_between_perform": "3 SECONDS", + "avoid_friendly_fire": true + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CALL_REINFORCEMENTS_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/trooper/ranged.bt.json b/code/modules/mob/living/basic/trooper/ranged.bt.json new file mode 100644 index 000000000000..e28e6625b221 --- /dev/null +++ b/code/modules/mob/living/basic/trooper/ranged.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper/ranged", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/trooper_ranged", + "bindings": { + "b4kar3zl": "1 SECONDS", + "beslksyc": "5", + "bnr1aazo": "TRUE" + } +} diff --git a/code/modules/mob/living/basic/trooper/shotgunner.bt.json b/code/modules/mob/living/basic/trooper/shotgunner.bt.json new file mode 100644 index 000000000000..3c2368d6afc5 --- /dev/null +++ b/code/modules/mob/living/basic/trooper/shotgunner.bt.json @@ -0,0 +1,10 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper/ranged/shotgunner", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/trooper_ranged", + "bindings": { + "b4kar3zl": "3 SECONDS", + "beslksyc": "3", + "bnr1aazo": "TRUE" + } +} diff --git a/code/modules/mob/living/basic/trooper/trooper.bt.json b/code/modules/mob/living/basic/trooper/trooper.bt.json new file mode 100644 index 000000000000..60f355e3cbf0 --- /dev/null +++ b/code/modules/mob/living/basic/trooper/trooper.bt.json @@ -0,0 +1,118 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/trooper", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/trooper/trooper.dm b/code/modules/mob/living/basic/trooper/trooper.dm index 1df73b15a1d3..2a78bf597c5f 100644 --- a/code/modules/mob/living/basic/trooper/trooper.dm +++ b/code/modules/mob/living/basic/trooper/trooper.dm @@ -37,3 +37,6 @@ loot = string_list(loot) AddElement(/datum/element/death_drops, loot) AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SHOE) + +/mob/living/basic/trooper/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() diff --git a/code/modules/mob/living/basic/trooper/trooper_ai.dm b/code/modules/mob/living/basic/trooper/trooper_ai.dm index b4cd1d52f6d9..6976cfaf62cf 100644 --- a/code/modules/mob/living/basic/trooper/trooper_ai.dm +++ b/code/modules/mob/living/basic/trooper/trooper_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/trooper + behavior_tree_json = "code/modules/mob/living/basic/trooper/trooper.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, @@ -6,99 +7,71 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, - ) - -/datum/ai_planning_subtree/basic_melee_attack_subtree/trooper - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack - -/datum/ai_planning_subtree/attack_obstacle_in_path/trooper - attack_behaviour = /datum/ai_behavior/attack_obstructions/trooper - -/datum/ai_behavior/attack_obstructions/trooper - action_cooldown = 1.2 SECONDS /datum/ai_controller/basic_controller/trooper/calls_reinforcements - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_CALLS_REINFORCEMENTS = TRUE, ) /datum/ai_controller/basic_controller/trooper/peaceful - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/attack_obstacle_in_path/trooper, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, + behavior_tree_json = "code/modules/mob/living/basic/trooper/peaceful.bt.json" + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_CALLS_REINFORCEMENTS = TRUE ) +/datum/bt_node/subtree/trooper_ranged + behavior_tree_json = "code/modules/mob/living/basic/trooper/trooper_ranged.bt.json" + + /datum/ai_controller/basic_controller/trooper/ranged - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, + behavior_tree_json = "code/modules/mob/living/basic/trooper/ranged.bt.json" + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_RANGED_SKIRMISH_MIN_DISTANCE = 3, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 4 ) -/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper -/datum/ai_behavior/basic_ranged_attack/trooper - action_cooldown = 1 SECONDS - required_distance = 5 - avoid_friendly_fire = TRUE /datum/ai_controller/basic_controller/trooper/ranged/burst - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, + behavior_tree_json = "code/modules/mob/living/basic/trooper/burst.bt.json" + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_RANGED_SKIRMISH_MIN_DISTANCE = 2, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 3 ) -/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper_burst - -/datum/ai_behavior/basic_ranged_attack/trooper_burst - action_cooldown = 3 SECONDS - avoid_friendly_fire = TRUE - -/datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/call_reinforcements, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_burst, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, +//lol my parser cant deal with two subtypes with the same name so this is just a peaceful burst :DDDD ebin +/datum/ai_controller/basic_controller/trooper/ranged/burst/peaceful_burst + behavior_tree_json = "code/modules/mob/living/basic/trooper/peaceful_burst.bt.json" + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_CALLS_REINFORCEMENTS = TRUE ) /datum/ai_controller/basic_controller/trooper/ranged/shotgunner - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_shotgun, - /datum/ai_planning_subtree/travel_to_point/and_clear_target/reinforce, + behavior_tree_json = "code/modules/mob/living/basic/trooper/shotgunner.bt.json" + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_REINFORCEMENTS_SAY = "411 in progress, requesting backup!", + BB_RANGED_SKIRMISH_MIN_DISTANCE = 2, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 3 ) -/datum/ai_planning_subtree/basic_ranged_attack_subtree/trooper_shotgun - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/trooper_shotgun -/datum/ai_behavior/basic_ranged_attack/trooper_shotgun - action_cooldown = 3 SECONDS - required_distance = 3 - avoid_friendly_fire = TRUE /datum/ai_controller/basic_controller/trooper/viscerator blackboard = list( diff --git a/code/modules/mob/living/basic/trooper/trooper_ranged.bt.json b/code/modules/mob/living/basic/trooper/trooper_ranged.bt.json new file mode 100644 index 000000000000..4aa2adfbafb7 --- /dev/null +++ b/code/modules/mob/living/basic/trooper/trooper_ranged.bt.json @@ -0,0 +1,137 @@ +{ + "dm_type": "/datum/bt_node/subtree/trooper_ranged", + "bindings": { + "b4kar3zl": { + "label": "time_between_perform", + "default": "0" + }, + "beslksyc": { + "label": "max_range", + "default": "3" + }, + "bnr1aazo": { + "label": "avoid_friendly_fire", + "default": "FALSE" + } + }, + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "key": "BB_CALLS_REINFORCEMENTS", + "value": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/cooldown", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "cooldown_key": "BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN", + "cooldown_duration": "30 SECONDS", + "lock_on_succeed": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/call_reinforcements" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "time_between_perform": "$b4kar3zl", + "max_range": "$beslksyc", + "avoid_friendly_fire": "$bnr1aazo" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/succeed" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_reinforce" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/turtle/turtle.bt.json b/code/modules/mob/living/basic/turtle/turtle.bt.json new file mode 100644 index 000000000000..5a912fb917f5 --- /dev/null +++ b/code/modules/mob/living/basic/turtle/turtle.bt.json @@ -0,0 +1,169 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/turtle", + "type": "selector", + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/ability_available", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "ability_key": "BB_GENERIC_ACTION" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/use_mob_ability", + "vars": { + "ability_key": "BB_GENERIC_ACTION" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TURTLE_HEADBUTT_VICTIM" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TURTLE_HEADBUTT_VICTIM", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_TURTLE_HEADBUTT_COOLDOWN", + "cooldown_duration": "1 MINUTES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/headbutt_leg", + "vars": { + "target_key": "BB_TURTLE_HEADBUTT_VICTIM", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_TURTLE_HEADBUTT_VICTIM" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_TURTLE_FLORA_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_cooldown", + "vars": { + "cooldown_key": "BB_TURTLE_FLORA_COOLDOWN", + "cooldown_duration": "1 MINUTES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/sniff_flora", + "vars": { + "target_key": "BB_TURTLE_FLORA_TARGET", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk", + "bindings": { + "bf07i8ep": "10" + } + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_TURTLE_HEADBUTT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TURTLE_HEADBUTT_VICTIM", + "target_source": "/datum/target_source/oview_typed/from_bb_key/turtle_headbutt_types", + "targeting_strategy": "/datum/targeting_strategy/legged_conscious_human" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_TURTLE_FLORA_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_TURTLE_FLORA_TARGET", + "target_source": "/datum/target_source/oview_typed/from_bb_key/turtle_flora_types", + "targeting_strategy": "/datum/targeting_strategy/sniffable_hydro" + } + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/express_happiness" + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/turtle/turtle.dm b/code/modules/mob/living/basic/turtle/turtle.dm index 9f0ab89b07ae..97fe070cbf78 100644 --- a/code/modules/mob/living/basic/turtle/turtle.dm +++ b/code/modules/mob/living/basic/turtle/turtle.dm @@ -53,6 +53,14 @@ var/static/list/indigestible_seeds = typecacheof(list( /obj/item/seeds/random, )) + var/static/list/headbutt_targets = list( + /mob/living/carbon/human, + ) + var/static/list/flora_to_smell = list( + /obj/machinery/hydroponics, + /obj/item/kirbyplants, + ) + /mob/living/basic/turtle/Initialize(mapload) . = ..() @@ -67,6 +75,8 @@ add_traits(list(TRAIT_NODROWN, TRAIT_SWIMMER), INNATE_TRAIT) var/static/list/eatable_food = list(/obj/item/seeds) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(eatable_food)) + ai_controller.set_blackboard_key(BB_TURTLE_HEADBUTT_TYPES, typecacheof(headbutt_targets)) + ai_controller.set_blackboard_key(BB_TURTLE_FLORA_TYPES, typecacheof(flora_to_smell)) AddElement(/datum/element/basic_eating, food_types = eatable_food) AddComponent(/datum/component/happiness) RegisterSignal(src, COMSIG_MOB_PRE_EAT, PROC_REF(pre_eat_food)) @@ -164,7 +174,7 @@ developed_path = evolution_path var/datum/action/cooldown/tree_ability = new new_ability_path(src) tree_ability?.Grant(src) - ai_controller?.set_blackboard_key(BB_TURTLE_TREE_ABILITY, tree_ability) + ai_controller?.set_blackboard_key(BB_GENERIC_ACTION, tree_ability) STOP_PROCESSING(SSprocessing, src) update_appearance() @@ -241,7 +251,7 @@ return NONE /mob/living/basic/turtle/proc/process_food(potential_food) - if(QDELETED(src) || stat != CONSCIOUS) + if(QDELETED(src) || IS_UNCONSCIOUS_OR_CRIT(src)) return if(ispath(potential_food)) diff --git a/code/modules/mob/living/basic/turtle/turtle_ai.dm b/code/modules/mob/living/basic/turtle/turtle_ai.dm index 1af7c3111f78..6bda4053c3ed 100644 --- a/code/modules/mob/living/basic/turtle/turtle_ai.dm +++ b/code/modules/mob/living/basic/turtle/turtle_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/turtle + behavior_tree_json = "code/modules/mob/living/basic/turtle/turtle.bt.json" blackboard = list( BB_HAPPY_EMOTIONS = list( "wiggles its tree in excitement!", @@ -15,78 +16,3 @@ ), ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - planning_subtrees = list( - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/express_happiness, - /datum/ai_planning_subtree/use_mob_ability/turtle_tree, - /datum/ai_planning_subtree/find_and_hunt_target/headbutt_people, //playfully headbutt people's legs - /datum/ai_planning_subtree/find_and_hunt_target/sniff_flora, //mmm the aroma - ) - -/datum/ai_planning_subtree/use_mob_ability/turtle_tree - ability_key = BB_TURTLE_TREE_ABILITY - -/datum/ai_planning_subtree/use_mob_ability/turtle_tree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/happiness_count = controller.blackboard[BB_BASIC_HAPPINESS] * 100 - if(happiness_count > 75) - return ..() - if(!SPT_PROB(happiness_count / 50, seconds_per_tick)) - return - return ..() - -/datum/ai_planning_subtree/find_and_hunt_target/sniff_flora - target_key = BB_TURTLE_FLORA_TARGET - finding_behavior = /datum/ai_behavior/find_hunt_target/sniff_flora - hunting_behavior = /datum/ai_behavior/hunt_target/sniff_flora - hunt_targets = list( - /obj/machinery/hydroponics, - /obj/item/kirbyplants, - ) - hunt_range = 5 - hunt_chance = 45 - -/datum/ai_behavior/find_hunt_target/sniff_flora - action_cooldown = 1 MINUTES - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_hunt_target/sniff_flora/valid_dinner(mob/living/source, obj/machinery/hydroponics/dinner, radius, datum/ai_controller/controller, seconds_per_tick) - if(!istype(dinner)) - return TRUE - if(isnull(dinner.myseed)) - return FALSE - if(dinner.weedlevel > 5 || dinner.pestlevel > 5) //too smelly - return FALSE - return can_see(source, dinner, radius) - -/datum/ai_behavior/hunt_target/sniff_flora - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/sniff_flora/target_caught(mob/living/hunter, atom/hunted) - hunter.manual_emote("Enjoys the sweet scent eminating from [hunted::name]!") - -/datum/ai_planning_subtree/find_and_hunt_target/headbutt_people - target_key = BB_TURTLE_HEADBUTT_VICTIM - finding_behavior = /datum/ai_behavior/find_hunt_target/human_to_headbutt - hunting_behavior = /datum/ai_behavior/hunt_target/headbutt_leg - hunt_targets = list(/mob/living/carbon/human) - hunt_range = 4 - hunt_chance = 45 - -/datum/ai_behavior/find_hunt_target/human_to_headbutt - action_cooldown = 2 MINUTES - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_hunt_target/human_to_headbutt/valid_dinner(mob/living/source, mob/living/carbon/human/dinner, radius, datum/ai_controller/controller, seconds_per_tick) - if(dinner.stat != CONSCIOUS) - return FALSE - if(isnull(dinner.get_bodypart(BODY_ZONE_R_LEG)) && isnull(dinner.get_bodypart(BODY_ZONE_L_LEG))) //no legs to headbutt! - return FALSE - return can_see(source, dinner, radius) - -/datum/ai_behavior/hunt_target/headbutt_leg - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/headbutt_leg/target_caught(mob/living/hunter, atom/hunted) - hunter.manual_emote("playfully headbutts [hunted]'s legs!") - diff --git a/code/modules/mob/living/basic/vermin/axolotl.bt.json b/code/modules/mob/living/basic/vermin/axolotl.bt.json new file mode 100644 index 000000000000..20dd0de5e095 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/axolotl.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/axolotl", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" +} diff --git a/code/modules/mob/living/basic/vermin/axolotl.dm b/code/modules/mob/living/basic/vermin/axolotl.dm index 730b22fb16df..b6ad8b5c1212 100644 --- a/code/modules/mob/living/basic/vermin/axolotl.dm +++ b/code/modules/mob/living/basic/vermin/axolotl.dm @@ -37,6 +37,6 @@ AddElement(/datum/element/can_be_held) /datum/ai_controller/basic_controller/axolotl + behavior_tree_json = "code/modules/mob/living/basic/vermin/axolotl.bt.json" ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk diff --git a/code/modules/mob/living/basic/vermin/butterfly.bt.json b/code/modules/mob/living/basic/vermin/butterfly.bt.json new file mode 100644 index 000000000000..439ec88c758a --- /dev/null +++ b/code/modules/mob/living/basic/vermin/butterfly.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/butterfly", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" +} diff --git a/code/modules/mob/living/basic/vermin/butterfly.dm b/code/modules/mob/living/basic/vermin/butterfly.dm index cbc847aa9e89..9edb092d965e 100644 --- a/code/modules/mob/living/basic/vermin/butterfly.dm +++ b/code/modules/mob/living/basic/vermin/butterfly.dm @@ -39,8 +39,7 @@ return TRUE //treaty signed at the Beeneeva convention /datum/ai_controller/basic_controller/butterfly - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk + behavior_tree_json = "code/modules/mob/living/basic/vermin/butterfly.bt.json" /mob/living/basic/butterfly/lavaland unsuitable_atmos_damage = 0 diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.json b/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.json new file mode 100644 index 000000000000..11d967e9330c --- /dev/null +++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.json @@ -0,0 +1,80 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cockroach", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN", + "hunt_cooldown": "5 SECONDS", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/ants", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "vision_range": 2 + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.json b/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.json new file mode 100644 index 000000000000..42333e89b353 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.json @@ -0,0 +1,145 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cockroach/aggro", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN", + "hunt_cooldown": "5 SECONDS", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/ants", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "vision_range": 2 + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach_ai.dm b/code/modules/mob/living/basic/vermin/cockroach/cockroach_ai.dm index a6d42aa162f9..bd5e14e94eec 100644 --- a/code/modules/mob/living/basic/vermin/cockroach/cockroach_ai.dm +++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach_ai.dm @@ -1,64 +1,38 @@ /// AI controller for normal roach /datum/ai_controller/basic_controller/cockroach + behavior_tree_json = "code/modules/mob/living/basic/vermin/cockroach/cockroach.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_OWNER_SELF_HARM_RESPONSES = list( "*me waves its antennae in disapproval.", "*me chitters sadly." - ) + ), + BB_BASIC_MOB_SPEAK_LINES = list( + BB_EMOTE_HEAR = list("chitters."), + BB_EMOTE_SOUND = list('sound/mobs/non-humanoids/insect/chitter.ogg'), + BB_SPEAK_CHANCE = 5, + ), ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/find_and_hunt_target/roach, - ) /// AI controller for aggressive roach /datum/ai_controller/basic_controller/cockroach/aggro - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/roach, - ) + behavior_tree_json = "code/modules/mob/living/basic/vermin/cockroach/cockroach_aggro.bt.json" /// AI controller for roach who can shoot at you /datum/ai_controller/basic_controller/cockroach/glockroach - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach, //If we are attacking someone, this will prevent us from hunting - /datum/ai_planning_subtree/find_and_hunt_target/roach, - ) - -/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach - -/datum/ai_behavior/basic_ranged_attack/glockroach //Slightly slower, as this is being made in feature freeze ;) - action_cooldown = 1 SECONDS + behavior_tree_json = "code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.json" /// roach who shoots at you slightly slower /datum/ai_controller/basic_controller/cockroach/mobroach - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/random_speech/insect, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach, - /datum/ai_planning_subtree/find_and_hunt_target/roach, - ) + behavior_tree_json = "code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.json" -/datum/ai_planning_subtree/basic_ranged_attack_subtree/mobroach - ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/mobroach +/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach //Slightly slower, as this is being made in feature freeze ;) + time_between_perform = 1 SECONDS -/datum/ai_behavior/basic_ranged_attack/mobroach - action_cooldown = 2 SECONDS +/datum/bt_node/ai_behavior/basic_ranged_attack/mobroach + time_between_perform = 2 SECONDS diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.json b/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.json new file mode 100644 index 000000000000..89b843a4538a --- /dev/null +++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach_glockroach.bt.json @@ -0,0 +1,143 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cockroach/glockroach", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/glockroach", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN", + "hunt_cooldown": "5 SECONDS", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/ants", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "vision_range": 2 + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.json b/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.json new file mode 100644 index 000000000000..ab18fc7ac40b --- /dev/null +++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach_mobroach.bt.json @@ -0,0 +1,143 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/cockroach/mobroach", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_ranged_attack/mobroach", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/maintain_distance", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN", + "hunt_cooldown": "5 SECONDS", + "always_reset_target": true + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_ROACH_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/ants", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "vision_range": 2 + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/crab.bt.json b/code/modules/mob/living/basic/vermin/crab.bt.json new file mode 100644 index 000000000000..c8798d612757 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/crab.bt.json @@ -0,0 +1,126 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/crab", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target", + "bindings": { + "byk9gqj4": "BB_BASIC_MOB_FLEE_TARGET" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/go_for_swim" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_BASIC_MOB_STOP_FLEEING", + "invert": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_BASIC_MOB_FLEE_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_KEY_SWIMMER_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SWIM_ALTERNATE_TURF", + "targeting_strategy": "/datum/targeting_strategy/walkable_turf", + "target_source": "/datum/target_source/oview_water_turfs" + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/crab" + } + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/crab.dm b/code/modules/mob/living/basic/vermin/crab.dm index 8840bf3c1530..78468e3855e2 100644 --- a/code/modules/mob/living/basic/vermin/crab.dm +++ b/code/modules/mob/living/basic/vermin/crab.dm @@ -78,6 +78,7 @@ ///The basic ai controller for crabs /datum/ai_controller/basic_controller/crab + behavior_tree_json = "code/modules/mob/living/basic/vermin/crab.bt.json" blackboard = list( BB_ALWAYS_IGNORE_FACTION = TRUE, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/smaller, @@ -86,13 +87,3 @@ ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/from_flee_key, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/crab, - /datum/ai_planning_subtree/go_for_swim, - ) diff --git a/code/modules/mob/living/basic/vermin/eat_cable.bt.json b/code/modules/mob/living/basic/vermin/eat_cable.bt.json new file mode 100644 index 000000000000..564fa4b28ece --- /dev/null +++ b/code/modules/mob/living/basic/vermin/eat_cable.bt.json @@ -0,0 +1,58 @@ +{ + "dm_type": "/datum/bt_node/subtree/eat_cable", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_LOW_PRIORITY_HUNTING_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 0 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_MOUSE_WANTS_TO_EAT_CABLE" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "cooldown_key": "BB_MOUSE_CABLE_HUNT_COOLDOWN", + "hunt_cooldown": "20 SECONDS" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cable", + "targeting_strategy": "/datum/targeting_strategy/accessible_cable", + "vision_range": 0 + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/vermin/eat_cheese.bt.json b/code/modules/mob/living/basic/vermin/eat_cheese.bt.json new file mode 100644 index 000000000000..ad71a43064e2 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/eat_cheese.bt.json @@ -0,0 +1,51 @@ +{ + "dm_type": "/datum/bt_node/subtree/eat_cheese", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_HUNTING_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/interact_with_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "cooldown_key": "BB_MOUSE_CHEESE_HUNT_COOLDOWN", + "hunt_cooldown": "20 SECONDS" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/pickup_item", + "target_source": "/datum/target_source/oview_single_type/cheese", + "vision_range": 1 + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/vermin/frog.bt.json b/code/modules/mob/living/basic/vermin/frog.bt.json new file mode 100644 index 000000000000..fe676a7bea31 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/frog.bt.json @@ -0,0 +1,60 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/frog", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/frog_engage_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/go_for_swim" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_KEY_SWIMMER_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SWIM_ALTERNATE_TURF", + "targeting_strategy": "/datum/targeting_strategy/walkable_turf", + "target_source": "/datum/target_source/oview_water_turfs" + } + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/frog" + } + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/frog.dm b/code/modules/mob/living/basic/vermin/frog.dm index d7dbe3848f82..f78b336397ab 100644 --- a/code/modules/mob/living/basic/vermin/frog.dm +++ b/code/modules/mob/living/basic/vermin/frog.dm @@ -65,7 +65,7 @@ /mob/living/basic/frog/proc/on_entered(datum/source, entered as mob|obj) SIGNAL_HANDLER - if(stat || !isliving(entered)) + if(IS_UNCONSCIOUS_OR_CRIT(src) || !isliving(entered)) return var/mob/living/entered_mob = entered if(entered_mob.mob_size > MOB_SIZE_TINY) @@ -146,7 +146,12 @@ AddComponent(/datum/component/explode_on_attack, mob_type_dont_bomb = typecacheof(list(/mob/living/basic/frog, /mob/living/basic/leaper))) addtimer(CALLBACK(src, PROC_REF(death)), existence_period) +/// Engage our current target: flee from scary fishermen, otherwise beat them up. +/datum/bt_node/subtree/frog_engage_target + behavior_tree_json = "code/modules/mob/living/basic/vermin/frog_engage_target.bt.json" + /datum/ai_controller/basic_controller/frog + behavior_tree_json = "code/modules/mob/living/basic/vermin/frog.bt.json" blackboard = list( BB_BASIC_MOB_STOP_FLEEING = TRUE, //We only flee from scary fishermen. BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, @@ -158,33 +163,14 @@ ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/random_speech/frog, - /datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman, - /datum/ai_planning_subtree/flee_target/from_fisherman, - /datum/ai_planning_subtree/go_for_swim, - ) /datum/ai_controller/basic_controller/frog/trash - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/random_speech/frog, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree/no_fisherman, - /datum/ai_planning_subtree/flee_target/from_fisherman, - ) + behavior_tree_json = "code/modules/mob/living/basic/vermin/trash.bt.json" /datum/ai_controller/basic_controller/frog/suicide_frog + behavior_tree_json = "code/modules/mob/living/basic/vermin/suicide_frog.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN, //No fear, only hatred. It has nothing to lose ) - - planning_subtrees = list( - /datum/ai_planning_subtree/find_target_prioritize_traits, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/vermin/frog_engage_target.bt.json b/code/modules/mob/living/basic/vermin/frog_engage_target.bt.json new file mode 100644 index 000000000000..f8bd885f6bcf --- /dev/null +++ b/code/modules/mob/living/basic/vermin/frog_engage_target.bt.json @@ -0,0 +1,90 @@ +{ + "dm_type": "/datum/bt_node/subtree/frog_engage_target", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_has_trait", + "vars": { + "key": "BB_CURRENT_TARGET", + "trait": "TRAIT_SCARY_FISHERMAN" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/find_flee_location", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION", + "destination_key": "BB_FLEE_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_FLEE_LOCATION", + "required_dist": 0, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": true + } + } + ] + } + ] + } +} diff --git a/code/modules/mob/living/basic/vermin/lizard.bt.json b/code/modules/mob/living/basic/vermin/lizard.bt.json new file mode 100644 index 000000000000..80e4cc2575ea --- /dev/null +++ b/code/modules/mob/living/basic/vermin/lizard.bt.json @@ -0,0 +1,35 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/lizard", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/lizard" + } + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/lizard.dm b/code/modules/mob/living/basic/vermin/lizard.dm index a61b1604c549..c8d0a40a6b35 100644 --- a/code/modules/mob/living/basic/vermin/lizard.dm +++ b/code/modules/mob/living/basic/vermin/lizard.dm @@ -60,21 +60,18 @@ ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) AddElement(/datum/element/pet_bonus, "tongue") AddElement(/datum/element/can_be_held) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_LIZARD, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) AddElement(/datum/element/basic_eating, heal_amt = 5, food_types = edibles) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(edibles)) /datum/ai_controller/basic_controller/lizard + behavior_tree_json = "code/modules/mob/living/basic/vermin/lizard.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/random_speech/lizard, - ) //Subtypes of lizards follow. diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.json b/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.json new file mode 100644 index 000000000000..44dcc0a750bf --- /dev/null +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.json @@ -0,0 +1,81 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mothroach", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/override_id_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_CURRENT_TARGET", + "observer_abort": "BT_ABORT_SELF" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/move_to_and_eat" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_FLEE_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/find_food" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/mothroach" + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm index 1e70c723a740..2a64aa285a18 100644 --- a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm @@ -60,6 +60,7 @@ AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/pet_bonus, "squeak") AddElement(/datum/element/can_be_held) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOTHROACH, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) add_verb(src, /mob/living/proc/toggle_resting) ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm index c589152f1cdc..cfddecf2495f 100644 --- a/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm @@ -1,4 +1,5 @@ /datum/ai_controller/basic_controller/mothroach + behavior_tree_json = "code/modules/mob/living/basic/vermin/mothroach/mothroach.bt.json" blackboard = list( BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -8,23 +9,3 @@ ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/find_food/mothroach, - /datum/ai_planning_subtree/target_retaliate/to_flee, - /datum/ai_planning_subtree/flee_target/from_flee_key, - /datum/ai_planning_subtree/random_speech/mothroach, - ) - -/datum/ai_planning_subtree/find_food/mothroach - finding_behavior = /datum/ai_behavior/find_and_set/in_list/mothroach_food - -/datum/ai_behavior/find_and_set/in_list/mothroach_food - -/datum/ai_behavior/find_and_set/in_list/mothroach_food/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE) - var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) - var/mob/living/living_pawn = controller.pawn - found -= living_pawn.loc - if(length(found)) - return pick(found) diff --git a/code/modules/mob/living/basic/vermin/mouse.bt.json b/code/modules/mob/living/basic/vermin/mouse.bt.json new file mode 100644 index 000000000000..ce8f8c1c1b2b --- /dev/null +++ b/code/modules/mob/living/basic/vermin/mouse.bt.json @@ -0,0 +1,189 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mouse", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/override_id_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/eat_cheese" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/run_away_from_target" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOUSE_CHEESE_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cheese", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 1 + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/play_instrument_on_floor" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/eat_cable" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOUSE_CHEESE_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cheese", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 1 + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/nearest", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SONG_INSTRUMENT", + "target_source": "/datum/target_source/oview_single_type/piano_synth", + "targeting_strategy": "/datum/targeting_strategy/playable_synthesizer" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_MOUSE_WANTS_TO_EAT_CABLE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cable", + "targeting_strategy": "/datum/targeting_strategy/accessible_cable", + "vision_range": 0 + } + } + } + ] + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/mouse" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOUSE_CABLE_HUNT_COOLDOWN" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.01 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_MOUSE_WANTS_TO_EAT_CABLE", + "value": true + } + } + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm index 3e92f78c8afb..769cc408c085 100644 --- a/code/modules/mob/living/basic/vermin/mouse.dm +++ b/code/modules/mob/living/basic/vermin/mouse.dm @@ -190,7 +190,7 @@ /mob/living/basic/mouse/proc/on_entered(datum/source, atom/movable/entered) SIGNAL_HANDLER - if(ishuman(entered) && stat == CONSCIOUS) + if(ishuman(entered) && !IS_UNCONSCIOUS_OR_CRIT(src)) to_chat(entered, span_notice("[icon2html(src, entered)] Squeak!")) /// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans @@ -199,7 +199,7 @@ new /obj/effect/temp_visual/heart(loc) add_faction(FACTION_NEUTRAL) try_consume_cheese(cheese) - ai_controller.CancelActions() // Interrupt any current fleeing + ai_controller.cancel_current_plan() // Interrupt any current fleeing /// Attempts to consume a piece of cheese, causing a few effects. /mob/living/basic/mouse/proc/try_consume_cheese(obj/item/food/cheese/cheese) @@ -422,6 +422,7 @@ /// The mouse AI controller /datum/ai_controller/basic_controller/mouse + behavior_tree_json = "code/modules/mob/living/basic/vermin/mouse.bt.json" blackboard = list( // Always cowardly BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, // Use this to find people to run away from BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, @@ -431,41 +432,14 @@ ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - // Try to speak, because it's cute - /datum/ai_planning_subtree/random_speech/mouse, - // Follow the boss's orders - /datum/ai_planning_subtree/pet_planning, - // Look for and execute hunts for cheese even if someone is looking at us - /datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese, - // Next priority is to try and appreoach a keyboard - /datum/ai_planning_subtree/approach_synthesizer, - // And play it if we are near it - /datum/ai_planning_subtree/generic_play_instrument/end_planning, - // Next priority is see if anyone is looking at us - /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, - // Skedaddle - /datum/ai_planning_subtree/flee_target/mouse, - // Otherwise, look for and execute hunts for cabling - /datum/ai_planning_subtree/find_and_hunt_target/look_for_cables, - ) - -/// Don't look for anything to run away from if you are distracted by being adjacent to cheese -/datum/ai_planning_subtree/flee_target/mouse - -/datum/ai_planning_subtree/flee_target/mouse/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/atom/hunted_cheese = controller.blackboard[BB_CURRENT_HUNTING_TARGET] - if (!isnull(hunted_cheese)) - return // We see some cheese, which is more important than our life - return ..() /// AI controller for rats, slightly more complex than mice becuase they attack people /datum/ai_controller/basic_controller/mouse/rat + behavior_tree_json = "code/modules/mob/living/basic/vermin/mouse_rat.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, - BB_BASIC_MOB_CURRENT_TARGET = null, // heathen + BB_CURRENT_TARGET = null, // heathen BB_CURRENT_HUNTING_TARGET = null, // cheese BB_LOW_PRIORITY_HUNTING_TARGET = null, // cable BB_OWNER_SELF_HARM_RESPONSES = list( @@ -477,14 +451,15 @@ ai_traits = DEFAULT_AI_FLAGS | STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/pet_planning, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese, - /datum/ai_planning_subtree/random_speech/mouse, - /datum/ai_planning_subtree/find_and_hunt_target/look_for_cables, - ) + + + + +/datum/bt_node/subtree/eat_cable + behavior_tree_json = "code/modules/mob/living/basic/vermin/eat_cable.bt.json" + +/datum/bt_node/subtree/eat_cheese + behavior_tree_json = "code/modules/mob/living/basic/vermin/eat_cheese.bt.json" + +/datum/bt_node/subtree/play_instrument_on_floor + behavior_tree_json = "code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.json" diff --git a/code/modules/mob/living/basic/vermin/mouse_rat.bt.json b/code/modules/mob/living/basic/vermin/mouse_rat.bt.json new file mode 100644 index 000000000000..2fa9b856d770 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/mouse_rat.bt.json @@ -0,0 +1,217 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/mouse/rat", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/override_id_set", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + "child": { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [] + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/attack_obstructions", + "vars": { + "target_key": "BB_CURRENT_TARGET" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/eat_cheese" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/eat_cable" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOUSE_CHEESE_HUNT_COOLDOWN" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_CURRENT_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cheese", + "targeting_strategy": "/datum/targeting_strategy/anything", + "vision_range": 1 + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "key": "BB_MOUSE_WANTS_TO_EAT_CABLE" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "target_source": "/datum/target_source/oview_single_type/cable", + "targeting_strategy": "/datum/targeting_strategy/accessible_cable", + "vision_range": 0 + } + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_SUCCEED_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "1 SECONDS", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop", + "bindings": { + "bqdqne64": "/datum/bt_node/ai_behavior/random_speech/mouse" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/key_off_cooldown", + "vars": { + "cooldown_key": "BB_MOUSE_CABLE_HUNT_COOLDOWN" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.01 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/set_bb_key", + "vars": { + "target_key": "BB_MOUSE_WANTS_TO_EAT_CABLE", + "value": true + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.json b/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.json new file mode 100644 index 000000000000..d6ea55eb32c8 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/play_instrument_on_floor.bt.json @@ -0,0 +1,78 @@ +{ + "dm_type": "/datum/bt_node/subtree/play_instrument_on_floor", + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_SONG_INSTRUMENT" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_at_distance", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "maximum_distance": 1, + "require_reach": true + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/keep_playing_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT" + } + } + }, + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_SONG_INSTRUMENT", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/setup_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT", + "song_lines_key": "BB_SONG_LINES" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/play_instrument", + "vars": { + "song_instrument_key": "BB_SONG_INSTRUMENT", + "volume": 50 + } + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_SONG_INSTRUMENT", + "target_source": "/datum/target_source/oview_single_type/piano_synth", + "targeting_strategy": "/datum/targeting_strategy/playable_synthesizer" + } + } + ] + } +} diff --git a/code/modules/mob/living/basic/vermin/space_bat.bt.json b/code/modules/mob/living/basic/vermin/space_bat.bt.json new file mode 100644 index 000000000000..cd360abe561a --- /dev/null +++ b/code/modules/mob/living/basic/vermin/space_bat.bt.json @@ -0,0 +1,5 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/space_bat", + "type": "subtree", + "subtype": "/datum/bt_node/subtree/simple_retaliate_combat" +} diff --git a/code/modules/mob/living/basic/vermin/space_bat.dm b/code/modules/mob/living/basic/vermin/space_bat.dm index 4ef58a45c3ac..27f14f496721 100644 --- a/code/modules/mob/living/basic/vermin/space_bat.dm +++ b/code/modules/mob/living/basic/vermin/space_bat.dm @@ -41,15 +41,10 @@ ///Controller for space bats, has nothing unique, just retaliation. /datum/ai_controller/basic_controller/space_bat + behavior_tree_json = "code/modules/mob/living/basic/vermin/space_bat.bt.json" blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/code/modules/mob/living/basic/vermin/suicide_frog.bt.json b/code/modules/mob/living/basic/vermin/suicide_frog.bt.json new file mode 100644 index 000000000000..a3fa8cdce070 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/suicide_frog.bt.json @@ -0,0 +1,71 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/frog/suicide_frog", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets/prioritize_trait", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/code/modules/mob/living/basic/vermin/trash.bt.json b/code/modules/mob/living/basic/vermin/trash.bt.json new file mode 100644 index 000000000000..a51cb3f8b0b4 --- /dev/null +++ b/code/modules/mob/living/basic/vermin/trash.bt.json @@ -0,0 +1,61 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/frog/trash", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/escape_captivity" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/frog_engage_target" + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_walk" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/frog" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + } + ] +} diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 7bdb9779c11c..146635425ae1 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -255,7 +255,7 @@ // If your ratio is less than one (you're missing any blood) and your oxyloss is under missing blood %, start getting oxy damage. // This damage accrues faster the less blood you have. // If the damage surpasses the KO threshold for oxyloss, then we'll always tick up so you die eventually - if(target_oxyloss > 0 && (get_oxy_loss() < target_oxyloss || (target_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD && stat >= UNCONSCIOUS))) + if(target_oxyloss > 0 && (get_oxy_loss() < target_oxyloss || (target_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD && IS_UNCONSCIOUS(src)))) // At roughly half blood this equals to 3 oxyloss per tick. At 90% blood it's close to 0.5 var/rounded_oxyloss = round(0.01 * (BLOOD_VOLUME_NORMAL - modified_blood_volume), 0.25) * seconds_per_tick adjust_oxy_loss(rounded_oxyloss, updating_health = TRUE) diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm index 3f2d984e67eb..72ee6388d998 100644 --- a/code/modules/mob/living/brain/MMI.dm +++ b/code/modules/mob/living/brain/MMI.dm @@ -8,24 +8,31 @@ custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) var/braintype = "Cyborg" - var/obj/item/radio/mmi/radio = null //Let's give it a radio. - var/mob/living/brain/brainmob = null //The current occupant. - var/mob/living/silicon/robot = null //Appears unused. - var/obj/vehicle/sealed/mecha = null //This does not appear to be used outside of reference in mecha.dm. - var/obj/item/organ/brain/brain = null //The actual brain - var/datum/ai_laws/laws = new() + VAR_FINAL/obj/item/radio/mmi/radio = null //Let's give it a radio. + VAR_FINAL/mob/living/brain/brainmob = null //The current occupant. + VAR_FINAL/mob/living/silicon/robot = null //Appears unused. + VAR_FINAL/obj/vehicle/sealed/mecha = null //This does not appear to be used outside of reference in mecha.dm. + VAR_FINAL/obj/item/organ/brain/brain = null //The actual brain + + /// If TRUE, and placed in an AI, calls replacement_ai_name() and uses that as the AI's name. var/force_replace_ai_name = FALSE - var/overrides_aicore_laws = FALSE // Whether the laws on the MMI, if any, override possible pre-existing laws loaded on the AI core. /// Whether the brainmob can move. Doesnt usually matter but SPHERICAL POSIBRAINSSS var/immobilize = TRUE + /// If supplied with a law datum, the laws will be transferred to whatever it's placed in. + /// - If placed in a cyborg, it will start de-synced from the AI. + /// The cyborg's laws will be unmodifiable unless synced to the AI or a law rack. + /// - If placed in an AI, it will override the AI's laws. + /// Likewise, the AI's laws will be unmodifiable unless synced to a law rack. + var/datum/ai_laws/laws + /obj/item/radio/mmi custom_materials = null /obj/item/mmi/Initialize(mapload) . = ..() radio = new(src) //Spawns a radio inside the MMI. - laws.set_laws_config() + radio.set_broadcasting(FALSE) //researching radio mmis turned the robofabs into radios because this didnt start as 0. /obj/item/mmi/Destroy() set_mecha(null) @@ -53,69 +60,77 @@ if(brain) . += "mmi_dead" -/obj/item/mmi/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers) +/obj/item/mmi/item_interaction(mob/living/user, obj/item/tool, list/modifiers) user.changeNext_move(CLICK_CD_MELEE) - if(istype(O, /obj/item/organ/brain)) //Time to stick a brain in it --NEO - var/obj/item/organ/brain/newbrain = O - if(brain) - to_chat(user, span_warning("There's already a brain in the MMI!")) - return - if(newbrain.suicided) - to_chat(user, span_warning("[newbrain] is completely useless.")) - return - if(!newbrain.brainmob) - var/install = tgui_alert(user, "[newbrain] is inactive, slot it in anyway?", "Installing Brain", list("Yes", "No")) - if(install != "Yes") - return - if(!user.transferItemToLoc(newbrain, src)) - return - user.visible_message(span_notice("[user] sticks [newbrain] into [src]."), span_notice("[src]'s indicator light turns red as you insert [newbrain]. Its brainwave activity alarm buzzes.")) - brain = newbrain - brain.organ_flags |= ORGAN_FROZEN - name = "[initial(name)]: [copytext(newbrain.name, 1, -8)]" - update_appearance() - return - - if(!user.transferItemToLoc(O, src)) - return - var/mob/living/brain/B = newbrain.brainmob - if(!B.key && !newbrain.decoy_override) - B.notify_revival("Someone has put your brain in a MMI!", source = src) - user.visible_message(span_notice("[user] sticks \a [newbrain] into [src]."), span_notice("[src]'s indicator light turn on as you insert [newbrain].")) - - set_brainmob(newbrain.brainmob) - newbrain.brainmob = null - brainmob.forceMove(src) - brainmob.container = src - var/fubar_brain = newbrain.suicided || HAS_TRAIT(brainmob, TRAIT_SUICIDED) //brain is from a suicider - if(!fubar_brain && !(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death, nor was from a suicider. - brainmob.set_stat(CONSCIOUS) //we manually revive the brain mob - else if(!fubar_brain && newbrain.organ_flags & ORGAN_FAILING) // the brain is damaged, but not from a suicider - to_chat(user, span_warning("[src]'s indicator light turns yellow and its brain integrity alarm beeps softly. Perhaps you should check [newbrain] for damage.")) - playsound(src, 'sound/machines/synth/synth_no.ogg', 5, TRUE) - else - to_chat(user, span_warning("[src]'s indicator light turns red and its brainwave activity alarm beeps softly. Perhaps you should check [newbrain] again.")) - playsound(src, 'sound/machines/beep/triple_beep.ogg', 5, TRUE) + if(!istype(tool, /obj/item/organ/brain)) //Time to stick a brain in it --NEO + return NONE + + var/obj/item/organ/brain/newbrain = tool + if(brain) + to_chat(user, span_warning("There's already a brain in the MMI!")) + return ITEM_INTERACT_BLOCKING + + if(newbrain.suicided) + to_chat(user, span_warning("[newbrain] is completely useless.")) + return ITEM_INTERACT_BLOCKING + + if(!newbrain.brainmob) + var/install = tgui_alert(user, "[newbrain] is inactive, slot it in anyway?", "Installing Brain", list("Yes", "No")) + if(install != "Yes") + return ITEM_INTERACT_BLOCKING + + if(!user.transferItemToLoc(newbrain, src)) + return ITEM_INTERACT_BLOCKING - brainmob.reset_perspective() + user.visible_message(span_notice("[user] sticks [newbrain] into [src]."), span_notice("[src]'s indicator light turns red as you insert [newbrain]. Its brainwave activity alarm buzzes.")) brain = newbrain brain.organ_flags |= ORGAN_FROZEN - - name = "[initial(name)]: [brainmob.real_name]" + name = "[initial(name)]: [copytext(newbrain.name, 1, -8)]" update_appearance() - if(istype(brain, /obj/item/organ/brain/alien)) - braintype = "Xenoborg" //HISS....Beep. - else - braintype = "Cyborg" + return ITEM_INTERACT_SUCCESS - SSblackbox.record_feedback("amount", "mmis_filled", 1) + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING - user.log_message("has put the brain of [key_name(brainmob)] into an MMI", LOG_GAME) + var/mob/living/brain/other_brainmob = newbrain.brainmob + if(!other_brainmob.key && !newbrain.decoy_override) + other_brainmob.notify_revival("Someone has put your brain in a MMI!", source = src) + user.visible_message(span_notice("[user] sticks \a [newbrain] into [src]."), span_notice("[src]'s indicator light turn on as you insert [newbrain].")) - else if(brainmob) - O.attack(brainmob, user) //Oh noooeeeee + set_brainmob(newbrain.brainmob) + newbrain.brainmob = null + brainmob.forceMove(src) + brainmob.container = src + var/fubar_brain = newbrain.suicided || HAS_TRAIT(brainmob, TRAIT_SUICIDED) //brain is from a suicider + if(!fubar_brain && !(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death, nor was from a suicider. + brainmob.set_stat(STABLE) //we manually revive the brain mob + else if(!fubar_brain && newbrain.organ_flags & ORGAN_FAILING) // the brain is damaged, but not from a suicider + to_chat(user, span_warning("[src]'s indicator light turns yellow and its brain integrity alarm beeps softly. Perhaps you should check [newbrain] for damage.")) + playsound(src, 'sound/machines/synth/synth_no.ogg', 5, TRUE) else + to_chat(user, span_warning("[src]'s indicator light turns red and its brainwave activity alarm beeps softly. Perhaps you should check [newbrain] again.")) + playsound(src, 'sound/machines/beep/triple_beep.ogg', 5, TRUE) + + brainmob.reset_perspective() + brain = newbrain + brain.organ_flags |= ORGAN_FROZEN + + name = "[initial(name)]: [brainmob.real_name]" + update_appearance() + if(istype(brain, /obj/item/organ/brain/alien)) + braintype = "Xenoborg" //HISS....Beep. + else + braintype = "Cyborg" + + SSblackbox.record_feedback("amount", "mmis_filled", 1) + + user.log_message("has put the brain of [key_name(brainmob)] into an MMI", LOG_GAME) + return ITEM_INTERACT_SUCCESS + +/obj/item/mmi/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) + if(!brainmob) return ..() + attacking_item.attack(brainmob, user) //Oh noooeeeee /** * Forces target brain into the MMI. Mainly intended for admin purposes, as this allows transfer without a mob or user. @@ -155,7 +170,7 @@ var/fubar_brain = new_brain.suicided || HAS_TRAIT(brainmob, TRAIT_SUICIDED) if(!fubar_brain && !(new_brain.organ_flags & ORGAN_FAILING)) - brainmob.set_stat(CONSCIOUS) + brainmob.set_stat(STABLE) brainmob.reset_perspective() brain = new_brain @@ -265,7 +280,7 @@ GAME_VERB_SRC_DESC(/obj/item/mmi, Toggle_Listening, usr.loc, "Toggle Listening", "Toggle listening channel on or off.", "MMI") - if(brainmob.stat) + if(IS_UNCONSCIOUS_OR_CRIT(brainmob)) to_chat(brainmob, span_warning("Can't do that while incapacitated or dead!")) if(!radio.is_on()) to_chat(brainmob, span_warning("Your radio is disabled!")) @@ -353,10 +368,17 @@ GAME_VERB_SRC_DESC(/obj/item/mmi, Toggle_Listening, usr.loc, "Toggle Listening", /obj/item/mmi/syndie name = "\improper Syndicate Man-Machine Interface" - desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs and AIs created with it." - overrides_aicore_laws = TRUE + desc = "Syndicate's own brand of MMI. \ + It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs and AIs created with it." /obj/item/mmi/syndie/Initialize(mapload) . = ..() laws = new /datum/ai_laws/syndicate_override() radio.set_on(FALSE) + +/obj/item/mmi/syndie/examine(mob/user) + . = ..() + . += span_notice("If used to create a cyborg, it will be unlinked from the station's AI. \ + The lawset cannot be modified until it is synced to a module rack or an AI.") + . += span_notice("If used to create an AI, it will not automatically sync to a module rack. \ + The lawset cannot be modified until it is synced to a module rack.") diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index fc8e60b63e06..615410fbb629 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -71,10 +71,12 @@ return FALSE return TRUE -/mob/living/brain/fully_replace_character_name(oldname,newname) - ..() - if(stored_dna) - stored_dna.real_name = real_name +/mob/living/brain/fully_replace_character_name(oldname, newname, log_new_name = FALSE) + . = ..() + if(!.) + return + + stored_dna?.real_name = real_name /mob/living/brain/forceMove(atom/destination) if(container) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index c3b9c811f484..134677e7807e 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -63,7 +63,7 @@ // Special check for if you're trapped in a body you can't control because it's owned by a ling. if(IS_CHANGELING(brain_owner) && !(movement_flags & NO_ID_TRANSFER)) - if(brainmob && !(brain_owner.stat == DEAD || (HAS_TRAIT(brain_owner, TRAIT_DEATHCOMA)))) + if(brainmob && !IS_DEAD_OR_FAKING(brain_owner)) to_chat(brainmob, span_danger("You can't feel your body! You're still just a brain!")) forceMove(brain_owner) brain_owner.update_body_parts() @@ -169,37 +169,40 @@ L.mind.transfer_to(brainmob) to_chat(brainmob, span_notice("You feel slightly disoriented. That's normal when you're just a brain.")) -/obj/item/organ/brain/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) - user.changeNext_move(CLICK_CD_MELEE) - - if(istype(item, /obj/item/borg/apparatus/organ_storage)) - return //Borg organ bags shouldn't be killing brains +/obj/item/organ/brain/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/borg/apparatus/organ_storage)) + return NONE//Borg organ bags shouldn't be killing brains - if (check_for_repair(item, user)) - return TRUE + if (check_for_repair(tool, user)) + return ITEM_INTERACT_SUCCESS // Cutting out skill chips. - if(length(skillchips) && item.get_sharpness() == SHARP_EDGED) - to_chat(user,span_notice("You begin to excise skillchips from [src].")) - if(do_after(user, 15 SECONDS, target = src)) - for(var/chip in skillchips) - var/obj/item/skillchip/skillchip = chip + if(!length(skillchips) || tool.get_sharpness() != SHARP_EDGED) + return NONE - if(!istype(skillchip)) - stack_trace("Item of type [skillchip.type] qdel'd from [src] skillchip list.") - qdel(skillchip) - continue + to_chat(user,span_notice("You begin to excise skillchips from [src].")) + if(!do_after(user, 15 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING - remove_skillchip(skillchip) + for(var/obj/item/skillchip/skillchip as anything in skillchips) + if(!istype(skillchip, /obj/item/skillchip)) + stack_trace("Item of type [skillchip.type] qdel'd from [src] skillchip list.") + qdel(skillchip) + continue - if(skillchip.removable) - skillchip.forceMove(drop_location()) - continue + remove_skillchip(skillchip) - qdel(skillchip) + if(skillchip.removable) + skillchip.forceMove(drop_location()) + continue - skillchips = null - return + qdel(skillchip) + + skillchips = null + return ITEM_INTERACT_SUCCESS + +/obj/item/organ/brain/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers) + user.changeNext_move(CLICK_CD_MELEE) if(brainmob) //if we aren't trying to heal the brain, pass the attack onto the brainmob. item.attack(brainmob, user) //Oh noooeeeee @@ -556,6 +559,10 @@ /obj/item/organ/brain/felinid //A bit smaller than average brain_size = 0.8 + variant_traits_added = list( + TRAIT_CATLIKE_INSTINCT, + TRAIT_WATER_HATER, + ) // Sometimes, felinids go a bit haywire and bite people. Based entirely on mania and hunger. /obj/item/organ/brain/felinid/get_attacking_limb(mob/living/carbon/human/target) @@ -583,9 +590,25 @@ desc = "A piece of juicy meat found in an ayy lmao's head." icon_state = "brain-x" brain_size = 1.3 - variant_traits_added = list(TRAIT_REMOTE_TASTING) + variant_traits_added = list(TRAIT_REMOTE_TASTING, TRAIT_ABDUCTOR_KNOWLEDGE) shade_color = "grey" +/obj/item/organ/brain/abductor/on_mob_insert(mob/living/carbon/brain_owner, special = FALSE, movement_flags) + . = ..() + RegisterSignal(brain_owner, COMSIG_MOB_REAGENT_TICK, PROC_REF(handle_peanut_butter)) + +/obj/item/organ/brain/abductor/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + UnregisterSignal(organ_owner, COMSIG_MOB_REAGENT_TICK) + +/obj/item/organ/brain/abductor/proc/handle_peanut_butter(mob/living/carbon/organ_owner, datum/reagent/reagent, seconds_per_tick, metabolization_ratio) + SIGNAL_HANDLER + if(!istype(reagent, /datum/reagent/consumable/peanut_butter)) + return + organ_owner.add_mood_event("ET_pieces", /datum/mood_event/et_pieces, reagent.name) + organ_owner.set_drugginess_if_lower(15 SECONDS * metabolization_ratio * seconds_per_tick) + + ////////////////////////////////////TRAUMAS//////////////////////////////////////// /obj/item/organ/brain/proc/has_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE) diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm index f4792ddef96a..59b5c7ac0837 100644 --- a/code/modules/mob/living/brain/posibrain.dm +++ b/code/modules/mob/living/brain/posibrain.dm @@ -134,7 +134,7 @@ GLOBAL_VAR(posibrain_notify_cooldown) brainmob.stored_dna = new /datum/dna/stored(brainmob) transferred_user.dna.copy_dna(brainmob.stored_dna) brainmob.timeofdeath = transferred_user.timeofdeath - brainmob.set_stat(CONSCIOUS) + brainmob.set_stat(STABLE) if(brainmob.mind) brainmob.mind.set_assigned_role(SSjob.get_job_type(posibrain_job_path)) if(transferred_user.mind) @@ -160,7 +160,7 @@ GLOBAL_VAR(posibrain_notify_cooldown) if(policy) to_chat(brainmob, policy) brainmob.mind.set_assigned_role(SSjob.get_job_type(posibrain_job_path)) - brainmob.set_stat(CONSCIOUS) + brainmob.set_stat(STABLE) brainmob.grant_language(/datum/language/machine, source = LANGUAGE_ATOM) visible_message(new_mob_message) @@ -173,7 +173,7 @@ GLOBAL_VAR(posibrain_notify_cooldown) . = ..() if(brainmob?.key) switch(brainmob.stat) - if(CONSCIOUS) + if(STABLE) if(!brainmob.client) . += "It appears to be in stand-by mode." //afk if(DEAD) @@ -210,7 +210,10 @@ GLOBAL_VAR(posibrain_notify_cooldown) icon_state = "[base_icon_state]" return -/obj/item/mmi/posibrain/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers) +/obj/item/mmi/posibrain/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_BLOCKING + +/obj/item/mmi/posibrain/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) return /obj/item/mmi/posibrain/add_mmi_overlay() diff --git a/code/modules/mob/living/carbon/alien/adult/adult.dm b/code/modules/mob/living/carbon/alien/adult/adult.dm index 0a198f2f00da..a464c0040403 100644 --- a/code/modules/mob/living/carbon/alien/adult/adult.dm +++ b/code/modules/mob/living/carbon/alien/adult/adult.dm @@ -53,10 +53,12 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( /mob/living/carbon/alien/adult/resist_grab(moving_resist) if(pulledby.grab_state) - visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \ - span_danger("You break free of [pulledby]'s grip!")) + visible_message( + span_danger("[src] breaks free of [pulledby]'s grip!"), + span_danger("You break free of [pulledby]'s grip!"), + ) pulledby.stop_pulling() - . = 0 + return TRUE /mob/living/carbon/alien/adult/alien_evolve(mob/living/carbon/alien/adult/new_xeno) drop_all_held_items() @@ -111,7 +113,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( /mob/living/carbon/alien/adult/proc/can_consume(atom/movable/poor_soul) if(!isliving(poor_soul) || pulling != poor_soul) return FALSE - if(incapacitated || grab_state < GRAB_AGGRESSIVE || stat != CONSCIOUS) + if(incapacitated || grab_state < GRAB_AGGRESSIVE) return FALSE if(get_dir(src, poor_soul) != dir) // Gotta face em 4head return FALSE diff --git a/code/modules/mob/living/carbon/alien/adult/adult_update_icons.dm b/code/modules/mob/living/carbon/alien/adult/adult_update_icons.dm index e85dc79f20c8..b456f2dc92d3 100644 --- a/code/modules/mob/living/carbon/alien/adult/adult_update_icons.dm +++ b/code/modules/mob/living/carbon/alien/adult/adult_update_icons.dm @@ -15,7 +15,7 @@ else icon_state = "alien[caste]_dead" - else if((stat == UNCONSCIOUS && !IsSleeping()) || stat == HARD_CRIT || stat == SOFT_CRIT || IsParalyzed()) + else if(IS_UNCONSCIOUS_OR_CRIT(src) || IsParalyzed()) icon_state = "alien[caste]_unconscious" else if(leap_on_click) icon_state = "alien[caste]_pounce" diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm index 339ae0ac6ed8..a0bb3c9bea8e 100644 --- a/code/modules/mob/living/carbon/alien/adult/queen.dm +++ b/code/modules/mob/living/carbon/alien/adult/queen.dm @@ -175,7 +175,7 @@ to_chat(queen, span_noticealien("You cannot promote a child right now!")) return - if(to_promote.stat != CONSCIOUS || !to_promote.mind || !to_promote.key) + if(IS_UNCONSCIOUS_OR_CRIT(to_promote) || !to_promote.mind || !to_promote.key) return queen.adjustPlasma(-promotion.promotion_plasma_cost) diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index 99766b5bd065..45ed0f231134 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -57,7 +57,7 @@ In all, this is a lot like the monkey code. /N /mob/living/carbon/alien/get_shove_flags(mob/living/shover, obj/item/weapon) . = ..() - if(isnull(weapon) || stat != CONSCIOUS) + if(isnull(weapon) || IS_UNCONSCIOUS_OR_CRIT(src)) . &= ~(SHOVE_CAN_MOVE|SHOVE_CAN_HIT_SOMETHING|SHOVE_CAN_STAGGER) /mob/living/carbon/alien/attack_paw(mob/living/carbon/human/user, list/modifiers) diff --git a/code/modules/mob/living/carbon/alien/alien_say.dm b/code/modules/mob/living/carbon/alien/alien_say.dm index 21655bfa1622..6ddd88b75ced 100644 --- a/code/modules/mob/living/carbon/alien/alien_say.dm +++ b/code/modules/mob/living/carbon/alien/alien_say.dm @@ -10,7 +10,7 @@ hivemind_spans += " big" var/rendered = "Hivemind, [span_name("[shown_name]")] [message_a]" for(var/mob/player in GLOB.player_list) - if(!player.stat && player.hivecheck()) + if(!IS_UNCONSCIOUS_OR_CRIT(player) && player.hivecheck()) to_chat(player, rendered, type = MESSAGE_TYPE_RADIO, avoid_highlighting = player == src) else if(player in GLOB.dead_mob_list) var/link = FOLLOW_LINK(player, src) diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm index 930dd2956e5d..8913a01ace58 100644 --- a/code/modules/mob/living/carbon/alien/larva/life.dm +++ b/code/modules/mob/living/carbon/alien/larva/life.dm @@ -9,6 +9,9 @@ amount_grown = min(amount_grown + (0.5 * seconds_per_tick), max_grown) update_icons() +/mob/living/carbon/alien/larva/on_knockedout_trait_loss(datum/source) + . = ..() + set_resting(FALSE) /mob/living/carbon/alien/larva/update_stat() if(HAS_TRAIT(src, TRAIT_GODMODE)) @@ -17,11 +20,6 @@ if(health <= -maxHealth || !get_organ_by_type(/obj/item/organ/brain)) death() return - if((HAS_TRAIT(src, TRAIT_KNOCKEDOUT))) - set_stat(UNCONSCIOUS) - else - if(stat == UNCONSCIOUS) - set_resting(FALSE) - set_stat(CONSCIOUS) + set_stat(STABLE) update_damage_hud() update_health_hud() diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index 9339bd7abd80..d0e3c5c673fb 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -8,6 +8,10 @@ #define MIN_ACTIVE_TIME 200 //time between being dropped and going idle #define MAX_ACTIVE_TIME 400 +#define FACEHUGGER_AWAKE 1 +#define FACEHUGGER_ASLEEP 2 +#define FACEHUGGER_DEAD 3 + /obj/item/clothing/mask/facehugger name = "alien" desc = "It has some sort of a tube at the end of its tail." @@ -24,8 +28,8 @@ layer = MOB_LAYER max_integrity = 100 slowdown = 2 - clothing_traits = list(TRAIT_SOFTSPOKEN) - var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case + clothing_traits = list(TRAIT_FORCE_WHISPER) + var/facehugger_state = FACEHUGGER_AWAKE var/sterile = FALSE var/real = TRUE //0 for the toy, 1 for real. Sure I could istype, but fuck that. @@ -55,13 +59,13 @@ /obj/item/clothing/mask/facehugger/proc/react_to_mob(datum/source, mob/user) SIGNAL_HANDLER - if((stat == CONSCIOUS && !sterile) && !isalien(user)) + if((facehugger_state == FACEHUGGER_AWAKE && !sterile) && !isalien(user)) if(leap_to(user)) return COMSIG_LIVING_CANCEL_PULL //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/mask/facehugger/attack_hand(mob/user, list/modifiers) - if((stat == CONSCIOUS && !sterile) && !isalien(user)) + if((facehugger_state == FACEHUGGER_AWAKE && !sterile) && !isalien(user)) if(leap_to(user)) return . = ..() @@ -75,10 +79,10 @@ . = ..() if(!real)//So that giant red text about probisci doesn't show up. return - switch(stat) - if(DEAD,UNCONSCIOUS) + switch(facehugger_state) + if(FACEHUGGER_DEAD, FACEHUGGER_ASLEEP) . += span_bolddanger("[src] is not moving.") - if(CONSCIOUS) + if(FACEHUGGER_AWAKE) . += span_bolddanger("[src] seems to be active!") if (sterile) . += span_bolddanger("It looks like the proboscis has been removed.") @@ -98,7 +102,7 @@ HasProximity(target) /obj/item/clothing/mask/facehugger/on_found(mob/finder) - if(stat == CONSCIOUS) + if(facehugger_state == FACEHUGGER_AWAKE) return HasProximity(finder) /obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj) @@ -109,7 +113,7 @@ . = ..() if(!.) return - if(stat == CONSCIOUS) + if(facehugger_state == FACEHUGGER_AWAKE) icon_state = "[base_icon_state]_thrown" addtimer(CALLBACK(src, PROC_REF(clear_throw_icon_state)), 1.5 SECONDS) @@ -119,14 +123,14 @@ /obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) ..() - if(stat == CONSCIOUS) + if(facehugger_state == FACEHUGGER_AWAKE) icon_state = "[base_icon_state]" leap_to(hit_atom) /obj/item/clothing/mask/facehugger/proc/valid_to_attach(mob/living/hit_mob) // valid targets: carbons except aliens and devils // facehugger state early exit checks (Note: Melbert does not want dead people to be huggable) - if(stat != CONSCIOUS) + if(facehugger_state != FACEHUGGER_AWAKE) return FALSE if(attached) return FALSE @@ -161,7 +165,7 @@ return FALSE if(target.wear_mask) - var/obj/item/clothing/worn_mask = target.wear_mask + var/obj/item/worn_mask = target.wear_mask if(target.dropItemToGround(worn_mask)) target.visible_message(span_danger("[src] tears [worn_mask] off of [target]'s face!"), \ span_userdanger("[src] tears [worn_mask] off of your face!")) @@ -176,7 +180,7 @@ if(!valid_to_attach(victim)) return - if(victim.stat < UNCONSCIOUS) //sorry bro you gotta be awake + if(!IS_UNCONSCIOUS(victim)) //sorry bro you gotta be awake victim.say("AAAA!!") //triggers muffled speech and also visual feedback i guess // early returns and validity checks done: attach. attached++ @@ -222,31 +226,31 @@ span_userdanger("[src] violates your face!")) /obj/item/clothing/mask/facehugger/proc/go_active() - if(stat == DEAD || stat == CONSCIOUS) + if(facehugger_state == FACEHUGGER_DEAD || facehugger_state == FACEHUGGER_AWAKE) return - stat = CONSCIOUS + facehugger_state = FACEHUGGER_AWAKE icon_state = "[base_icon_state]" worn_icon_state = "[base_icon_state]" /obj/item/clothing/mask/facehugger/proc/go_idle() - if(stat == DEAD || stat == UNCONSCIOUS) + if(facehugger_state == FACEHUGGER_DEAD || facehugger_state == FACEHUGGER_ASLEEP) return - stat = UNCONSCIOUS + facehugger_state = FACEHUGGER_ASLEEP icon_state = "[base_icon_state]_inactive" worn_icon_state = "[base_icon_state]_inactive" addtimer(CALLBACK(src, PROC_REF(go_active)), rand(MIN_ACTIVE_TIME, MAX_ACTIVE_TIME)) /obj/item/clothing/mask/facehugger/proc/die() - if(stat == DEAD) + if(facehugger_state == FACEHUGGER_DEAD) return icon_state = "[base_icon_state]_dead" worn_icon_state = "[base_icon_state]_dead" inhand_icon_state = "facehugger_inactive" - stat = DEAD + facehugger_state = FACEHUGGER_DEAD visible_message(span_danger("[src] curls up into a ball!")) @@ -254,7 +258,7 @@ AddComponent(/datum/component/knockoff, knockoff_chance = 40, target_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST), slots_knockoffable = slot_flags) /obj/item/clothing/mask/facehugger/can_mob_unequip(mob/user) - if(!real || sterile || stat == DEAD || user.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo)) + if(!real || sterile || facehugger_state == FACEHUGGER_DEAD || user.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo)) return ..() if(user.get_item_by_slot(slot_flags) == src) to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!")) @@ -267,7 +271,7 @@ return if(!real || sterile || user.get_organ_by_type(/obj/item/organ/body_egg/alien_embryo)) return ..() - if(wearer.get_item_by_slot(slot_flags) == src && stat != DEAD) + if(wearer.get_item_by_slot(slot_flags) == src && facehugger_state != FACEHUGGER_DEAD) to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!")) return return ..() @@ -289,7 +293,7 @@ /obj/item/clothing/mask/facehugger/proc/on_mail_unwrap(atom/source, mob/user, obj/item/mail/traitor/letter) SIGNAL_HANDLER - if(stat != CONSCIOUS) + if(facehugger_state != FACEHUGGER_AWAKE) return NONE to_chat(user, span_danger("There's something moving inside of \the [letter]!")) leap_to(user) @@ -305,13 +309,13 @@ icon_state = "facehugger_dead" inhand_icon_state = "facehugger_inactive" worn_icon_state = "facehugger_dead" - stat = DEAD + facehugger_state = FACEHUGGER_DEAD /obj/item/clothing/mask/facehugger/impregnated icon_state = "facehugger_impregnated" inhand_icon_state = null worn_icon_state = "facehugger_impregnated" - stat = DEAD + facehugger_state = FACEHUGGER_DEAD /obj/item/clothing/mask/facehugger/toy inhand_icon_state = "facehugger_inactive" @@ -325,6 +329,10 @@ /obj/item/clothing/mask/facehugger/toy/die() return +#undef FACEHUGGER_AWAKE +#undef FACEHUGGER_ASLEEP +#undef FACEHUGGER_DEAD + #undef MIN_ACTIVE_TIME #undef MAX_ACTIVE_TIME diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 587e23536544..b732bf702088 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -16,7 +16,7 @@ . = ..() living_flags |= STOP_OVERLAY_UPDATE_BODY_PARTS - + real_bodypart_cache.Cut() QDEL_LIST(hand_bodyparts) QDEL_LIST(organs) QDEL_LIST(bodyparts) @@ -166,7 +166,7 @@ span_notice("You attempt to unbuckle yourself... \ (This will take around [DisplayTimeText(buckle_cd)] and you must stay still.)")) - if(!do_after(src, buckle_cd, target = src, timed_action_flags = IGNORE_HELD_ITEM, hidden = TRUE)) + if(!do_after(src, buckle_cd, target = src, timed_action_flags = IGNORE_HELD_ITEM, cog_icon = null)) if(buckled) to_chat(src, span_warning("You fail to unbuckle yourself!")) return @@ -217,7 +217,7 @@ if(!cuff_break) visible_message(span_warning("[src] attempts to remove [cuffs]!")) to_chat(src, span_notice("You attempt to remove [cuffs]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)")) - if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM, hidden = TRUE)) + if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM, cog_icon = null)) . = clear_cuffs(cuffs, cuff_break) else to_chat(src, span_warning("You fail to remove [cuffs]!")) @@ -338,14 +338,13 @@ ) add_mood_event("vomit", /datum/mood_event/vomitself) distance = 0 - else - if(message) - visible_message( - span_danger("[src] throws up!"), - span_userdanger("You throw up!"), - ) - if(!isflyperson(src)) - add_mood_event("vomit", /datum/mood_event/vomit) + else if(message) + visible_message( + span_danger("[src] throws up!"), + span_userdanger("You throw up!"), + ) + if(!HAS_TRAIT(src, TRAIT_VOMIT_SLURPER)) + add_mood_event("vomit", /datum/mood_event/vomit) if(stun) var/stun_time = 8 SECONDS @@ -411,13 +410,14 @@ guts.throw_at(throw_target, power, 4, src) -/mob/living/carbon/fully_replace_character_name(oldname,newname) +/mob/living/carbon/fully_replace_character_name(oldname, newname, log_new_name = FALSE) . = ..() - if(dna) - dna.real_name = real_name + if(!.) + return + + dna?.real_name = real_name var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD) - if(my_head) - my_head.real_name = real_name + my_head?.real_name = real_name /mob/living/carbon/set_body_position(new_value) @@ -503,28 +503,32 @@ /// Modifies lighting_cutoff/lighting_color_cutoffs/see_invisible and returns additional sight flags to apply /mob/living/carbon/proc/get_sight_and_cutoffs() - . = NONE + var/new_sight = NONE if(HAS_TRAIT(src, TRAIT_TRUE_NIGHT_VISION)) lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_HIGH) if(HAS_TRAIT(src, TRAIT_MESON_VISION)) - . |= SEE_TURFS + new_sight |= SEE_TURFS lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_MEDIUM) if(HAS_TRAIT(src, TRAIT_THERMAL_VISION)) - . |= SEE_MOBS + new_sight |= SEE_MOBS lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_MEDIUM) - if (HAS_TRAIT(src, TRAIT_MINOR_NIGHT_VISION)) + if(HAS_TRAIT(src, TRAIT_NIGHT_VISION)) lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_LOW) if(HAS_TRAIT(src, TRAIT_XRAY_VISION)) - . |= SEE_TURFS|SEE_MOBS|SEE_OBJS + new_sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS if(HAS_TRAIT(src, TRAIT_ECHOLOCATOR)) - . |= SEE_MOBS|SEE_TURFS + new_sight |= SEE_MOBS|SEE_TURFS lighting_cutoff = max(lighting_cutoff, LIGHTING_CUTOFF_FULLBRIGHT) + var/list/return_list = list(new_sight) + SEND_SIGNAL(src, COMSIG_CARBON_UPDATE_SIGHT_CUTOFFS, return_list) + return return_list[1] + /** * Calculates how visually impaired the mob is by their equipment and other factors * @@ -677,11 +681,6 @@ /mob/living/carbon/set_health(new_value) . = ..() - if(. > hardcrit_threshold) - if(health <= hardcrit_threshold && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)) - ADD_TRAIT(src, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) - else if(health > hardcrit_threshold) - REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) if(CONFIG_GET(flag/near_death_experience)) if(. > HEALTH_THRESHOLD_NEARDEATH) if(health <= HEALTH_THRESHOLD_NEARDEATH && !HAS_TRAIT(src, TRAIT_NODEATH)) @@ -703,16 +702,12 @@ death() return // DARKPACK EDIT CHANGE END - Torpor - if(HAS_TRAIT_FROM(src, TRAIT_DISSECTED, AUTOPSY_TRAIT)) - REMOVE_TRAIT(src, TRAIT_DISSECTED, AUTOPSY_TRAIT) if(health <= hardcrit_threshold && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)) set_stat(HARD_CRIT) - else if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT)) - set_stat(UNCONSCIOUS) else if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT)) set_stat(SOFT_CRIT) else - set_stat(CONSCIOUS) + set_stat(STABLE) update_damage_hud() update_health_hud() update_stamina_hud() @@ -911,6 +906,8 @@ new_bodypart.on_adding(src) bodyparts += new_bodypart + if(!IS_STUMP(new_bodypart)) + real_bodypart_cache[new_bodypart.body_zone] = new_bodypart new_bodypart.update_owner(src) // Apply a bodypart effect or merge with an existing one, for stuff like plant limbs regenning in light @@ -949,6 +946,7 @@ old_bodypart.on_removal(src) bodyparts -= old_bodypart + real_bodypart_cache -= old_bodypart.body_zone switch(old_bodypart.body_part) if(LEG_LEFT, LEG_RIGHT) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 131f38f44020..ddbd883cff59 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -2,7 +2,7 @@ /mob/living/carbon/get_eye_protection() . = ..() - if(is_blind() && !is_blind_from(list(UNCONSCIOUS_TRAIT, HYPNOCHAIR_TRAIT))) + if(is_blind() && !is_blind_from(list(TRAIT_STATUS_EFFECT(/datum/status_effect/knocked_out::id), HYPNOCHAIR_TRAIT))) return INFINITY //For all my homies that can not see in the world var/obj/item/organ/eyes/eyes = get_organ_slot(ORGAN_SLOT_EYES) if(!eyes) @@ -84,19 +84,13 @@ var/extra_wound_details = "" if(weapon.damtype == BRUTE && hit_bodypart.can_dismember()) + var/has_exterior = (hit_bodypart.bio_status & ANATOMY_EXTERIOR) + var/has_interior = (hit_bodypart.bio_status & ANATOMY_INTERIOR) - var/mangled_state = hit_bodypart.get_mangled_state() + var/exterior_ready_to_dismember = (!has_exterior || (hit_bodypart.mangled_state & BODYPART_MANGLED_EXTERIOR)) + var/interior_ready_to_dismember = (!has_interior || (hit_bodypart.mangled_state & BODYPART_MANGLED_INTERIOR)) - var/bio_status = hit_bodypart.get_bio_state_status() - - var/has_exterior = ((bio_status & ANATOMY_EXTERIOR)) - var/has_interior = ((bio_status & ANATOMY_INTERIOR)) - - var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR))) - var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_INTERIOR))) - - var/dismemberable = ((hit_bodypart.dismemberable_by_wound()) || hit_bodypart.dismemberable_by_total_damage()) - if (dismemberable) + if ((exterior_ready_to_dismember && interior_ready_to_dismember) || hit_bodypart.dismemberable_by_total_damage()) extra_wound_details = ", threatening to sever it entirely" else if((has_interior && (has_exterior && exterior_ready_to_dismember) && weapon.get_sharpness())) var/bone_text = hit_bodypart.get_internal_description() @@ -415,7 +409,7 @@ /// Check ourselves to see if we've got any shrapnel, return true if we do. This is a much simpler version of what humans do, we only indicate we're checking ourselves if there's actually shrapnel /mob/living/carbon/proc/check_self_for_injuries() - if(stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(src)) return var/embeds = FALSE @@ -504,11 +498,15 @@ * Check to see if we should be passed out from oxyloss */ /mob/living/carbon/proc/check_passout() + SIGNAL_HANDLER + if(HAS_TRAIT(src, TRAIT_NO_OXYLOSS_PASSOUT)) + REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + return + var/mob_oxyloss = get_oxy_loss() - if(mob_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD && !HAS_TRAIT(src, TRAIT_NO_OXYLOSS_PASSOUT)) - if(!HAS_TRAIT_FROM(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)) - ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) - else if(mob_oxyloss < OXYLOSS_PASSOUT_THRESHOLD) + if(mob_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD) + ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + else REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) /mob/living/carbon/get_organic_health() diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 475446721d50..423253c161d2 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -64,6 +64,8 @@ /obj/item/bodypart/leg/right, /obj/item/bodypart/leg/left, ) + /// Alist of (non-stump) bodyparts by their bodyzone for quick get_bodypart access + var/alist/real_bodypart_cache = alist() /// A collection of arms (or actually whatever the fug /bodyparts you monsters use to wreck my systems) var/list/hand_bodyparts = list() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 5c34f05a3311..4860ae2af9f5 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -399,7 +399,6 @@ if(new_key == old_key) new_limbs += limb_icon_cache[new_key] - else if(body_zone == BODY_ZONE_HEAD) head_update = TRUE @@ -419,7 +418,8 @@ apply_overlay(BODYPARTS_LAYER) // for legacy support, head changes triggers an eye/hair update - if(head_update) + // also run hair/eyes update on mob creation and other forced data updates to apply any custom changes made + if(head_update || update_limb_data) update_eyes() update_hair() diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index b1f790814140..ef87c35a0d62 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -5,13 +5,13 @@ /datum/emote/living/carbon/airguitar key = "airguitar" message = "is strumming the air and headbanging like a safari chimp." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/clap key = "clap" key_third_person = "claps" message = "claps." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE vary = TRUE affected_by_pitch = FALSE @@ -31,7 +31,7 @@ key_third_person = "cracks" message = "cracks their knuckles." sound = 'sound/mobs/humanoids/human/knuckle_crack/knuckles.ogg' - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS cooldown = 6 SECONDS /datum/emote/living/carbon/crack/can_run_emote(mob/living/carbon/user, status_check = TRUE , intentional, params) @@ -46,7 +46,7 @@ message_mime = "sobs silently." emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE vary = TRUE - stat_allowed = SOFT_CRIT + can_use_flags = EMOTE_CANUSE_SOFTCRIT /datum/emote/living/carbon/cry/run_emote(mob/user, params, type_override, intentional) . = ..() @@ -63,7 +63,7 @@ /datum/emote/living/carbon/circle key = "circle" key_third_person = "circles" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/circle/run_emote(mob/user, params, type_override, intentional) . = ..() @@ -137,7 +137,7 @@ /datum/emote/living/carbon/noogie key = "noogie" key_third_person = "noogies" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/noogie/run_emote(mob/user, params, type_override, intentional) . = ..() @@ -153,21 +153,21 @@ key_third_person = "rolls" message = "rolls." mob_type_allowed_typecache = list(/mob/living/carbon/alien) - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/scratch key = "scratch" key_third_person = "scratches" message = "scratches." mob_type_allowed_typecache = list(/mob/living/carbon/alien) - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/sign key = "sign" key_third_person = "signs" message_param = "signs the number %t." mob_type_allowed_typecache = list(/mob/living/carbon/alien) - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/sign/select_param(mob/user, params) . = ..() @@ -179,12 +179,12 @@ key_third_person = "signals" message_param = "raises %t fingers." mob_type_allowed_typecache = list(/mob/living/carbon/human) - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/slap key = "slap" key_third_person = "slaps" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS cooldown = 3 SECONDS // to prevent endless table slamming /datum/emote/living/carbon/slap/run_emote(mob/user, params, type_override, intentional) @@ -200,7 +200,7 @@ /datum/emote/living/carbon/hand key = "hand" key_third_person = "hands" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/hand/run_emote(mob/user, params, type_override, intentional) @@ -219,7 +219,7 @@ message = "snaps their fingers." message_param = "snaps their fingers at %t." emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS affected_by_pitch = FALSE /datum/emote/living/carbon/snap/get_sound(mob/living/user) @@ -233,7 +233,7 @@ /datum/emote/living/carbon/shoesteal key = "shoesteal" key_third_person = "shoesteals" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS cooldown = 3 SECONDS /datum/emote/living/carbon/shoesteal/run_emote(mob/user, params, type_override, intentional) diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index a02fad4b30b2..eee491fcc70b 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -24,7 +24,7 @@ var/appears_dead = FALSE var/just_sleeping = FALSE - if(!appears_alive()) + if(IS_DEAD_OR_FAKING(src)) appears_dead = TRUE var/obj/item/clothing/glasses/shades = get_item_by_slot(ITEM_SLOT_EYES) @@ -250,14 +250,13 @@ . += "[t_He] [t_has] a holy aura about [t_him]." living_user.add_mood_event("religious_comfort", /datum/mood_event/religiously_comforted) - switch(stat) - if(UNCONSCIOUS, HARD_CRIT) - . += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.") - if(SOFT_CRIT) - . += span_notice("[t_He] [t_is] barely conscious.") - if(CONSCIOUS) - if(HAS_TRAIT(src, TRAIT_DUMB)) - . += "[t_He] [t_has] a stupid expression on [t_his] face." + if(IS_UNCONSCIOUS(src)) + . += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.") + else if(stat == SOFT_CRIT) + . += span_notice("[t_He] [t_is] barely conscious.") + else if(HAS_TRAIT(src, TRAIT_DUMB)) + . += "[t_He] [t_has] a stupid expression on [t_his] face." + var/obj/item/organ/brain/brain = get_organ_by_type(/obj/item/organ/brain) if(brain && isnull(ai_controller)) var/npc_message = "" @@ -519,7 +518,7 @@ var/perpname = get_face_name(get_id_name("")) var/title = "" - if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD)) && (user.stat == CONSCIOUS || isobserver(user)) && user != src) + if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD)) && (!IS_UNCONSCIOUS_OR_CRIT(user) || isobserver(user)) && user != src) var/datum/record/crew/target_record = find_record(perpname) if(target_record) . += "Rank: [target_record.rank]" diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 99c41a4c9a1c..5e0730282a86 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -301,22 +301,15 @@ GLOBAL_LIST_EMPTY(features_by_species) var/obj/item/organ/new_organ = get_mutant_organ_type_for_slot(slot) var/old_organ_type = old_species?.get_mutant_organ_type_for_slot(slot) + if(existing_organ && !existing_organ.get_replaceability(new_organ, old_organ_type, old_species, replace_current)) + continue // if we have an extra organ that before changing that the species didnt have, remove it - if(!new_organ) - if(existing_organ && (old_organ_type == existing_organ.type || replace_current)) + else if(!new_organ) + if(existing_organ) existing_organ.Remove(organ_holder) qdel(existing_organ) continue - if(existing_organ) - // we dont want to remove organs that were not from the old species (such as from freak surgery or prosthetics) - if(existing_organ.type != old_organ_type && !replace_current) - continue - - // we don't want to remove organs that are the same as the new one - if(existing_organ.type == new_organ) - continue - if(visual_only && (!initial(new_organ.bodypart_overlay) && !initial(new_organ.visual))) continue @@ -647,7 +640,8 @@ GLOBAL_LIST_EMPTY(features_by_species) if(ITEM_SLOT_SUITSTORE) if(HAS_TRAIT(I, TRAIT_NODROP)) return FALSE - if(!H.wear_suit) + var/obj/item/clothing/suit/suit = H.get_item_by_slot(ITEM_SLOT_OCLOTHING) + if(!istype(suit)) if(!disable_warning) to_chat(H, span_warning("You need a suit before you can attach this [I.name]!")) return FALSE @@ -658,7 +652,7 @@ GLOBAL_LIST_EMPTY(features_by_species) if(!disable_warning) to_chat(H, span_warning("\The [I] is too big to attach!")) //should be src? return FALSE - if( is_type_in_list(I, H.wear_suit.allowed) ) + if(is_type_in_list(I, suit.allowed)) return TRUE return FALSE if(ITEM_SLOT_HANDCUFFED) @@ -745,13 +739,17 @@ GLOBAL_LIST_EMPTY(features_by_species) if(SEND_SIGNAL(target, COMSIG_CARBON_PRE_HELP, user, attacker_style) & COMPONENT_BLOCK_HELP_ACT) return TRUE - if(target.body_position == STANDING_UP || (target.appears_alive() && target.stat != SOFT_CRIT && target.stat != HARD_CRIT)) - target.help_shake_act(user) - if(target != user) - log_combat(user, target, "shaken") + // if lying down and dead (or faking death) or not stable: cpr + // if standing up or stable (and not faking death): shake/hug + + if(target.body_position == LYING_DOWN && (IS_DEAD_OR_FAKING(target) || target.stat != STABLE)) + user.do_cpr(target) return TRUE - user.do_cpr(target) + target.help_shake_act(user) + if(target != user) + log_combat(user, target, "shaken") + return TRUE // DARKPACK EDIT CHANGE START - STORYTELLER_DICE - (This proc required a MAJOR rewrite to be more ttrpg accurate and use dice) ///This proc handles punching damage. IMPORTANT: Our owner is the TARGET and not the USER in this proc. For whatever reason... @@ -1055,7 +1053,7 @@ GLOBAL_LIST_EMPTY(features_by_species) return // Only stabilise core temp when alive and not in statis - if(humi.stat < DEAD && !HAS_TRAIT(humi, TRAIT_STASIS)) + if(humi.stat != DEAD && !HAS_TRAIT(humi, TRAIT_STASIS)) body_temperature_core(humi, seconds_per_tick) // These do run in statis @@ -1226,7 +1224,7 @@ GLOBAL_LIST_EMPTY(features_by_species) burn_damage = burn_damage * heatmod * humi.physiology.heat_mod * 0.5 * seconds_per_tick // 40% for level 3 damage on humans to scream in pain - if (humi.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) + if (!IS_UNCONSCIOUS(humi) && (prob(burn_damage) * 10) / 4) INVOKE_ASYNC(humi, TYPE_PROC_REF(/mob, emote), "scream") // Apply the damage to all body parts diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 68bac2332b29..372fb8ea2a24 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -7,7 +7,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) -/mob/living/carbon/human/dummy/Initialize(mapload) +/mob/living/carbon/human/dummy/Initialize(mapload, datum/species/species) . = ..() ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_PREVENT_BLINKING, INNATE_TRAIT) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 6268d1fc4c5f..d1a3650caee6 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -6,7 +6,7 @@ key_third_person = "daps" message = "sadly can't find anybody to give daps to, and daps themself. Shameful." message_param = "gives daps to %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/eyebrow key = "eyebrow" @@ -40,7 +40,7 @@ key = "handshake" message = "shakes their own hands." message_param = "shakes hands with %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE /datum/emote/living/carbon/human/hug @@ -48,7 +48,7 @@ key_third_person = "hugs" message = "hugs themself." message_param = "hugs %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/mumble key = "mumble" @@ -77,41 +77,41 @@ key = "raise" key_third_person = "raises" message = "raises a hand." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/salute key = "salute" key_third_person = "salutes" message = "salutes." message_param = "salutes to %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS sound = 'sound/mobs/humanoids/human/salute/salute.ogg' /datum/emote/living/carbon/human/slit key = "slit" key_third_person = "slits" message = "drags a finger across their neck." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/scratch_h key = "scratch_h" message = "scratches their head." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/thumb_up key = "thumb_u" message = "gives a thumbs up." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/thumb_down key = "thumb_d" message = "gives a thumbs down." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/time key = "time" message = "checks the time." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/tap key = "tap" @@ -122,42 +122,42 @@ key = "halt" key_third_person = "halts" message = "holds up their palm, signaling to stop." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/shush key = "shush" key_third_person = "shushes" message = "holds a finger to their lips." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/listen key = "listen" key_third_person = "listens" message = "cups a hand to their ear." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/think key = "think" key_third_person = "thinks" message = "taps their head, thinking." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/beckon key = "beckon" key_third_person = "beckons" message = "waves a hand for someone to come closer." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/airquote key = "airquote" key_third_person = "airquotes" message = "makes air quotes." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/crazy key = "crazy" message = "twirls a finger next to their head." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/squint key = "squint" @@ -168,7 +168,7 @@ key = "rub" key_third_person = "rubs" message = "rubs their chin." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/shrug key = "shrug" @@ -290,13 +290,13 @@ key = "roll" key_third_person = "rolls" message = "rolls." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/monkey/scratch key = "scratch" key_third_person = "scratches" message = "scratches." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/carbon/human/monkey/screech/roar key = "roar" @@ -313,7 +313,7 @@ key = "sign" key_third_person = "signs" message_param = "signs the number %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /// emotes for glowy goobers /datum/emote/living/carbon/human/glow diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index ca1f1decb0db..223446a49814 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/human/Initialize(mapload) +/mob/living/carbon/human/Initialize(mapload, datum/species/species) ASSIGN_GAME_VERB(src, /mob/living, mob_sleep) add_verb(src, /mob/living/proc/toggle_resting) @@ -10,7 +10,8 @@ // Physiology needs to be created before species, as some species modify physiology setup_physiology() - create_dna() + + create_dna(species) dna.species.create_fresh_body(src) setup_human_dna() @@ -41,11 +42,17 @@ /mob/living/carbon/human/proc/setup_physiology() physiology = new() +/mob/living/carbon/human/get_unconscious_appearance() + return get_generic_humanoid_static_appearance() + /mob/living/carbon/human/proc/setup_mood() if (CONFIG_GET(flag/disable_human_mood)) return mob_mood = new /datum/mood(src) +/mob/living/carbon/human/dummy/get_unconscious_appearance() + return null + /mob/living/carbon/human/dummy/setup_mood() return @@ -280,7 +287,7 @@ return if(ishuman(human_or_ghost_user)) var/mob/living/carbon/human/human_user = human_or_ghost_user - if(human_user.stat || human_user == src) //|| !human_user.canmove || human_user.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at. + if(IS_UNCONSCIOUS_OR_CRIT(human_user) || human_user == src) //|| !human_user.canmove || human_user.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at. return //Non-fluff: This allows sec to set people to arrest as they get disarmed or beaten // Checks the user has security clearence before allowing them to change arrest status via hud, comment out to enable all access var/obj/item/clothing/glasses/hud/security/user_glasses = human_user.glasses @@ -422,8 +429,8 @@ return chest?.get_butt_sprite() /mob/living/carbon/human/get_footprint_sprite() - var/obj/item/bodypart/leg/L = get_bodypart(BODY_ZONE_R_LEG) || get_bodypart(BODY_ZONE_L_LEG) - return shoes?.footprint_sprite || L?.footprint_sprite + var/obj/item/bodypart/leg/leg = get_bodypart(BODY_ZONE_R_LEG) || get_bodypart(BODY_ZONE_L_LEG) + return astype(get_item_by_slot(ITEM_SLOT_FEET), /obj/item/clothing/shoes)?.footprint_sprite || leg?.footprint_sprite #define CHECK_PERMIT(item) (item && item.item_flags & NEEDS_PERMIT) @@ -532,7 +539,7 @@ if (DOING_INTERACTION_WITH_TARGET(src,target)) return FALSE - if (target.stat == DEAD || HAS_TRAIT(target, TRAIT_FAKEDEATH)) + if (IS_DEAD_OR_FAKING(target)) balloon_alert(src, "[target.p_they()] [target.p_are()] dead!") return FALSE @@ -919,7 +926,7 @@ return ..() /mob/living/carbon/human/mouse_buckle_handling(mob/living/M, mob/living/user) - if(pulling != M || grab_state != GRAB_AGGRESSIVE || stat != CONSCIOUS) + if(pulling != M || grab_state != GRAB_AGGRESSIVE || IS_UNCONSCIOUS_OR_CRIT(src)) return FALSE //If they dragged themselves to you and you're currently aggressively grabbing them try to piggyback @@ -934,7 +941,7 @@ //src is the user that will be carrying, target is the mob to be carried /mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target) - return (istype(target) && target.stat == CONSCIOUS) + return (istype(target) && !IS_UNCONSCIOUS_OR_CRIT(target)) /mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target) return ishuman(target) && target.body_position == LYING_DOWN @@ -1081,10 +1088,8 @@ var/race = null var/use_random_name = TRUE -/mob/living/carbon/human/species/create_dna() - dna = new /datum/dna(src) - if (!isnull(race)) - dna.species = new race +/mob/living/carbon/human/species/create_dna(datum/species/species) + ..(race) //Kind of shit but I'm brainfarting how to do this better right now. /mob/living/carbon/human/species/set_species(datum/species/mrace, icon_update, pref_load, replace_missing) . = ..() @@ -1134,20 +1139,6 @@ return TRUE return ..() -/mob/living/carbon/human/get_sight_and_cutoffs() - . = ..() - if(!istype(glasses)) - return - . |= glasses.vision_flags - if(glasses.invis_override) - set_invis_see(glasses.invis_override) - else - set_invis_see(min(glasses.invis_view, see_invisible)) - if(!isnull(glasses.lighting_cutoff)) - lighting_cutoff = max(lighting_cutoff, glasses.lighting_cutoff) - if(length(glasses.color_cutoffs)) - lighting_color_cutoffs = blend_cutoff_colors(lighting_color_cutoffs, glasses.color_cutoffs) - /mob/living/carbon/human/species/abductor race = /datum/species/abductor diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 0bd950891087..08889d1055f9 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -380,7 +380,7 @@ //If they can't, they're missing their heart and this would runtime if(undergoing_cardiac_arrest() && can_heartattack() && (shock_damage * siemens_coeff >= 1) && prob(25)) var/obj/item/organ/heart/heart = get_organ_slot(ORGAN_SLOT_HEART) - if(heart.Restart() && stat == CONSCIOUS) + if(heart.Restart() && !IS_UNCONSCIOUS_OR_CRIT(src)) to_chat(src, span_notice("You feel your heart beating again!")) if (!(flags & SHOCK_NO_HUMAN_ANIM)) electrocution_animation(4 SECONDS) @@ -546,7 +546,7 @@ return ..() /mob/living/carbon/human/check_self_for_injuries() - if(stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(src)) return var/list/combined_msg = list() @@ -756,10 +756,10 @@ /mob/living/carbon/human/get_eye_protection() . = ..() - if(isclothing(head)) // Adds head protection - var/obj/item/clothing/helmet = head - . += helmet.flash_protect - if(isclothing(glasses)) // Glasses - . += glasses.flash_protect - if(isclothing(wear_mask)) // Mask - . += wear_mask.flash_protect + for (var/obj/item/clothing/clothing in get_equipped_items()) + . += clothing.flash_protect + +/mob/living/carbon/human/get_emp_protection() + . = ..() + for(var/obj/item/clothing/each_clothing in get_equipped_items()) + . += each_clothing.emp_protection diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 99e0d242d26a..a95fccb1cd19 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -59,14 +59,14 @@ //Equipment slots var/obj/item/back = null var/obj/item/head = null - var/obj/item/clothing/gloves = null - var/obj/item/clothing/ears = null - var/obj/item/clothing/glasses/glasses = null - var/obj/item/clothing/shoes/shoes = null - var/obj/item/clothing/neck/wear_neck = null - var/obj/item/clothing/mask/wear_mask = null - var/obj/item/clothing/wear_suit = null - var/obj/item/clothing/w_uniform = null + var/obj/item/gloves = null + var/obj/item/ears = null + var/obj/item/glasses = null + var/obj/item/shoes = null + var/obj/item/wear_neck = null + var/obj/item/wear_mask = null + var/obj/item/wear_suit = null + var/obj/item/w_uniform = null var/obj/item/belt = null var/obj/item/wear_id = null var/obj/item/r_store = null diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm index 4c0343190102..2866738b545b 100644 --- a/code/modules/mob/living/carbon/human/human_say.dm +++ b/code/modules/mob/living/carbon/human/human_say.dm @@ -47,7 +47,7 @@ . += " (as [get_id_name("Unknown", honorifics = TRUE)])" /mob/living/carbon/human/binarycheck() - if(stat >= SOFT_CRIT) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return FALSE var/area/our_area = get_area(src) if(our_area.area_flags & BINARY_JAMMING) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index bee617b9d568..d2c1c741e638 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -10,7 +10,7 @@ var/list/items = ..() if(!(include_flags & INCLUDE_POCKETS)) items -= list(l_store, r_store, s_store) - if((include_flags & INCLUDE_ACCESSORIES) && w_uniform) + if((include_flags & INCLUDE_ACCESSORIES) && istype(w_uniform, /obj/item/clothing/under)) var/obj/item/clothing/under/worn_under = w_uniform items += worn_under.attached_accessories return items @@ -150,8 +150,6 @@ if(glasses) return glasses = equipping - if(glasses.vision_flags || glasses.invis_override || glasses.invis_view || !isnull(glasses.lighting_cutoff)) - update_sight() update_worn_glasses() if(ITEM_SLOT_GLOVES) if(gloves) @@ -262,9 +260,6 @@ update_worn_gloves() else if(item_dropping == glasses) glasses = null - var/obj/item/clothing/glasses/old_glasses = item_dropping - if(old_glasses.vision_flags || old_glasses.invis_override || old_glasses.invis_view || !isnull(old_glasses.lighting_cutoff)) - update_sight() if(!QDELETED(src)) update_worn_glasses() else if(item_dropping == ears) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 9f2740d4c601..077695626c69 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -277,15 +277,8 @@ return min(1, round(thermal_protection, 0.001)) /mob/living/carbon/human/has_smoke_protection() - if(isclothing(wear_mask)) - if(wear_mask.clothing_flags & BLOCK_GAS_SMOKE_EFFECT) - return TRUE - if(isclothing(glasses)) - if(glasses.clothing_flags & BLOCK_GAS_SMOKE_EFFECT) - return TRUE - if(isclothing(head)) - var/obj/item/clothing/CH = head - if(CH.clothing_flags & BLOCK_GAS_SMOKE_EFFECT) + for (var/obj/item/clothing/equip in get_equipped_items()) + if(equip.clothing_flags & BLOCK_GAS_SMOKE_EFFECT) return TRUE return ..() diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm index 04bae32dac01..f3a72b53bf84 100644 --- a/code/modules/mob/living/carbon/human/monkey.dm +++ b/code/modules/mob/living/carbon/human/monkey.dm @@ -3,7 +3,7 @@ race = /datum/species/monkey ai_controller = /datum/ai_controller/monkey -/mob/living/carbon/human/species/monkey/Initialize(mapload, cubespawned = FALSE) +/mob/living/carbon/human/species/monkey/Initialize(mapload, datum/species/species, cubespawned = FALSE) ADD_TRAIT(src, TRAIT_BORN_MONKEY, INNATE_TRAIT) if (cubespawned) SSmobs.cubemonkeys += src @@ -16,7 +16,7 @@ /mob/living/carbon/human/species/monkey/angry ai_controller = /datum/ai_controller/monkey/angry -/mob/living/carbon/human/species/monkey/angry/Initialize(mapload, cubespawned = FALSE) +/mob/living/carbon/human/species/monkey/angry/Initialize(mapload, datum/species/species, cubespawned = FALSE) . = ..() if(prob(10)) INVOKE_ASYNC(src, PROC_REF(give_ape_escape_helmet)) @@ -27,7 +27,8 @@ equip_to_slot_or_del(helmet, ITEM_SLOT_HEAD) helmet.attack_self(src) // todo encapsulate toggle -GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/punpun) +/// The one true Pun Pun. Either the bar's monkey mob, or the bar gorilla when the Big Pun Pun trait is active. +GLOBAL_DATUM(the_one_and_only_punpun, /mob/living) /mob/living/carbon/human/species/monkey/punpun name = "Pun Pun" //C A N O N @@ -42,7 +43,7 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu var/relic_mask var/memory_saved = FALSE -/mob/living/carbon/human/species/monkey/punpun/Initialize(mapload) +/mob/living/carbon/human/species/monkey/punpun/Initialize(mapload, datum/species/species) . = ..() // REGISTER_REQUIRED_MAP_ITEM(1, 1) // pun pun is required on maps. // DARKPACK EDIT REMOVAL @@ -50,6 +51,10 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu new /obj/effect/landmark/start/pun_pun(loc) //Pun Pun is a crewmember, and may late-join. return INITIALIZE_HINT_QDEL + if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_PUN_PUN_GYM_DAY)) + new /mob/living/basic/gorilla/bar(loc) //gym day get buff get swole + return INITIALIZE_HINT_QDEL + equip_to_slot_or_del(new /obj/item/clothing/under/suit/waiter(src), ITEM_SLOT_ICLOTHING) if(!GLOB.the_one_and_only_punpun && mapload) @@ -71,7 +76,7 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu return ..() /mob/living/carbon/human/species/monkey/punpun/Life(seconds_per_tick = SSMOBS_DT) - if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) Write_Memory(FALSE, FALSE) memory_saved = TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index 62bd2c1ae500..8cd878c9a090 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -277,13 +277,16 @@ /obj/item/dullahan_relay/Hear(atom/movable/speaker, message_language, raw_message, radio_freq, radio_freq_name, radio_freq_color, list/spans, list/message_mods = list(), message_range, source) // DARKPACK EDIT CHANGE - ORIGINAL: /obj/item/dullahan_relay/Hear(atom/movable/speaker, message_language, raw_message, radio_freq, radio_freq_name, radio_freq_color, list/spans, list/message_mods = list(), message_range) . = ..() - var/dist = get_dist(speaker, src) - message_range - if(dist > 0 && dist <= EAVESDROP_EXTRA_RANGE) - raw_message = stars(raw_message) - if(message_range != INFINITY && dist > EAVESDROP_EXTRA_RANGE) - return FALSE - if(!owner) + if(isnull(owner)) return FALSE + + var/dist = get_dist(speaker, src) - message_range + if(message_range != INFINITY) + if(dist > EAVESDROP_RANGE) + return FALSE + if(dist > 0) + raw_message = stars(raw_message) + return owner.Hear(speaker, message_language, raw_message, radio_freq, radio_freq_name, radio_freq_color, spans, message_mods, message_range = INFINITY) ///Stops dullahans from gibbing when regenerating limbs diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 2de3d7fc7991..5adf84f02b29 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -61,6 +61,7 @@ RegisterSignal(new_ethereal, COMSIG_ATOM_SABOTEUR_ACT, PROC_REF(hit_by_saboteur)) RegisterSignal(new_ethereal, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) RegisterSignal(new_ethereal, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(refresh_light_color)) + RegisterSignal(new_ethereal, COMSIG_CARBON_BODYPART_UPDATED, PROC_REF(update_bodypart_color)) ethereal_light = new_ethereal.mob_light(light_type = /obj/effect/dummy/lighting_obj/moblight/species) refresh_light_color(new_ethereal) @@ -78,6 +79,7 @@ COMSIG_ATOM_SABOTEUR_ACT, COMSIG_LIGHT_EATER_ACT, COMSIG_LIVING_HEALTH_UPDATE, + COMSIG_CARBON_BODYPART_UPDATED, )) QDEL_NULL(ethereal_light) return ..() @@ -123,6 +125,12 @@ ethereal.set_haircolor(dead_color, override = TRUE, update = FALSE) ethereal.update_body() +/datum/species/ethereal/proc/update_bodypart_color(datum/source, obj/item/bodypart/part, dropping_limb, is_creating) + SIGNAL_HANDLER + if(part.limb_id != SPECIES_ETHEREAL) + return + part.species_color = current_color + /datum/species/ethereal/proc/on_emp_act(mob/living/carbon/human/source, severity, protection) SIGNAL_HANDLER if(protection & EMP_PROTECT_SELF) diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 124196d02af5..a1733698711d 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -11,10 +11,8 @@ /obj/item/organ/tail/cat = "Cat", ) inherent_traits = list( - TRAIT_CATLIKE_GRACE, TRAIT_HATED_BY_DOGS, TRAIT_USES_SKINTONES, - TRAIT_WATER_HATER, ) changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT species_language_holder = /datum/language_holder/felinid diff --git a/code/modules/mob/living/carbon/human/species_types/ghost.dm b/code/modules/mob/living/carbon/human/species_types/ghost.dm index d1da78e38700..a286fd72ee3a 100644 --- a/code/modules/mob/living/carbon/human/species_types/ghost.dm +++ b/code/modules/mob/living/carbon/human/species_types/ghost.dm @@ -13,6 +13,7 @@ TRAIT_UNHUSKABLE, TRAIT_NO_FLOATING_ANIM, TRAIT_MOVE_FLYING, + TRAIT_GHOSTLY_MOB, ) inherent_biotypes = MOB_SPIRIT | MOB_UNDEAD no_equip_flags = ITEM_SLOT_FEET @@ -190,7 +191,8 @@ return FALSE //technically you can trap a ghost by blessing them as theyre phasing, //but they can still be dragged out. - if(locate(/obj/effect/blessing) in get_turf(owner)) + var/turf/owner_turf = get_turf(owner) + if(HAS_TRAIT(owner_turf, TRAIT_TURF_BLESSED)) return FALSE var/obj/item/bodypart/chest/their_chest = living_owner.get_bodypart(BODY_ZONE_CHEST) if(!their_chest || !(their_chest.bodytype & BODYTYPE_GHOST)) @@ -249,8 +251,9 @@ UnregisterSignal(carbon_owner, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE) ///Called when attempting to move to a new tile while the action is active, returns to cancel moving. -/datum/action/innate/toggle_passthrough/proc/attempt_move(mob/source, new_loc, direct) +/datum/action/innate/toggle_passthrough/proc/attempt_move(mob/source, atom/new_loc, direct) SIGNAL_HANDLER - if(locate(/obj/effect/blessing) in new_loc) + + if(new_loc && HAS_TRAIT(new_loc, TRAIT_TURF_BLESSED)) to_chat(source, span_warning("Holy energies block your path!")) return COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 23e3554d170f..0891f176ad64 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -110,7 +110,7 @@ /datum/species/golem/proc/on_examine(mob/living/carbon/human/source, mob/living/examiner, list/examine_text) SIGNAL_HANDLER - if(source.appears_alive()) + if(!IS_DEAD_OR_FAKING(source)) return examine_text += span_warning("This golem appears to be in a state of disrepair. \ @@ -119,7 +119,7 @@ /datum/species/golem/proc/rebuild_check(mob/living/carbon/human/source, mob/living/user, obj/item/tool, ...) SIGNAL_HANDLER - if(source.appears_alive()) + if(!IS_DEAD_OR_FAKING(source)) return NONE if(!isstack(tool) || !is_type_in_list(tool, GLOB.golem_stack_food_directory)) @@ -218,7 +218,7 @@ SIGNAL_HANDLER if(source.nutrition < NUTRITION_LEVEL_STARVING) - if(!early_warning && COOLDOWN_FINISHED(src, warning_cd) && source.stat < UNCONSCIOUS) + if(!early_warning && COOLDOWN_FINISHED(src, warning_cd) && !IS_UNCONSCIOUS(source)) source.visible_message( span_warning("[source] shudders weakly as their form begins to destabilize!"), span_bolddanger("You feel your form destabilizing as you run low on material to sustain yourself! \ @@ -232,7 +232,7 @@ early_warning = FALSE if(source.nutrition < 50) - if(!final_warning && COOLDOWN_FINISHED(src, warning_cd) && source.stat < UNCONSCIOUS) + if(!final_warning && COOLDOWN_FINISHED(src, warning_cd) && !IS_UNCONSCIOUS(source)) source.visible_message( span_warning("[source] looks like they're on the verge of falling apart!"), span_userdanger("Your form shudders violently as you near complete destabilization! \ diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index fa2d34012ccf..1d244f4731d3 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -86,24 +86,41 @@ // Saline can prevent you from cannibalizing yourself. if(slime.get_blood_volume(apply_modifiers = TRUE) < BLOOD_VOLUME_BAD) - Cannibalize_Body(slime) + cannibalize_body(slime) regenerate_limbs?.build_all_button_icons(UPDATE_BUTTON_STATUS) return HANDLE_BLOOD_NO_NUTRITION_DRAIN|HANDLE_BLOOD_NO_OXYLOSS -/datum/species/jelly/proc/Cannibalize_Body(mob/living/carbon/human/H) - var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs() - var/obj/item/bodypart/consumed_limb +/datum/species/jelly/proc/cannibalize_body(mob/living/carbon/human/target) + var/list/limbs_to_consume = list() + + for(var/body_zone, untyped_limb in target.get_bodyparts_by_zones()) + var/obj/item/bodypart/limb = untyped_limb + if(isnull(limb)) + continue + + var/limb_zone = limb.body_zone + + if(IS_STUMP(limb) || limb_zone == BODY_ZONE_CHEST || limb_zone == BODY_ZONE_HEAD) + continue + + if(limb.biological_state & BIO_JELLY) // can only cannibalize limbs that are comprised of jelly (this is a last gasp to keep going, let's assume we can't digest out non-jelly limbs) + limbs_to_consume += limb_zone + if(!length(limbs_to_consume)) - H.losebreath++ + target.losebreath++ return - if(H.num_legs) //Legs go before arms + + if(target.num_legs > 0) //Legs go before arms limbs_to_consume -= list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM) - consumed_limb = H.get_bodypart(pick(limbs_to_consume)) + + var/obj/item/bodypart/consumed_limb = target.get_bodypart(pick(limbs_to_consume)) consumed_limb.drop_limb() - to_chat(H, span_userdanger("Your [consumed_limb] is drawn back into your body, unable to maintain its shape!")) + + to_chat(target, span_userdanger("Your [consumed_limb.name] is drawn back into your body, unable to maintain its shape!")) qdel(consumed_limb) - H.adjust_blood_volume(20 * H.physiology.blood_regen_mod) + + target.adjust_blood_volume(20 * target.physiology.blood_regen_mod) /datum/species/jelly/get_species_description() return "Jellypeople are a strange and alien species with three eyes, made entirely out of gel." @@ -388,10 +405,10 @@ L["area"] = get_area_name(body, TRUE) var/stat = "error" switch(body.stat) - if(CONSCIOUS) - stat = "Conscious" - if(SOFT_CRIT to HARD_CRIT) // Also includes UNCONSCIOUS - stat = "Unconscious" + if(STABLE) + stat = "Stable" + if(SOFT_CRIT, HARD_CRIT) + stat = "Critical" if(DEAD) stat = "Dead" var/occupied @@ -459,7 +476,7 @@ if(dupe.stat == DEAD) //Is it alive? return FALSE - if(dupe.stat != CONSCIOUS) //Is it awake? + if(IS_UNCONSCIOUS_OR_CRIT(dupe)) //Is it awake? return FALSE if(dupe.mind && dupe.mind.active) //Is it unoccupied? @@ -473,7 +490,7 @@ /datum/action/innate/swap_body/proc/swap_to_dupe(datum/mind/M, mob/living/carbon/human/dupe) if(!can_swap(dupe)) //sanity check return - if(M.current.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(M.current)) M.current.visible_message(span_notice("[M.current] stops moving and starts staring vacantly into space."), span_notice("You stop moving this body...")) else diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index fe55ba44a046..2d51b1676a52 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -20,6 +20,7 @@ mutanttongue = /obj/item/organ/tongue/lizard mutanteyes = /obj/item/organ/eyes/lizard mutantbrain = /obj/item/organ/brain/lizard + mutantliver = /obj/item/organ/liver/lizard coldmod = 1.5 heatmod = 0.67 payday_modifier = 1.0 diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm index 556814211180..1a876b55a72b 100644 --- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm +++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm @@ -123,6 +123,12 @@ /// Will this monkey stumble if they are crossed by a simple mob or a carbon in combat mode? Toggable by monkeys with clients, and is messed automatically set to true by monkey AI. var/tripping = TRUE +/obj/item/organ/brain/primate/get_replaceability(obj/item/organ/new_organ_type, obj/item/organ/expected_organ_type, datum/species/old_species, replace_current = TRUE) + ///Real monkeys retain their ape brains when humanized (further species change can override it). If old_species is null, + if(HAS_TRAIT(owner, TRAIT_BORN_MONKEY) && (!old_species || istype(old_species, /datum/species/monkey))) + return FALSE + return ..() + /datum/action/item_action/organ_action/toggle_trip name = "Toggle Tripping" button_icon = 'icons/mob/actions/actions_changeling.dmi' @@ -157,6 +163,12 @@ SIGNAL_HANDLER if(!tripping || !crossing_mob.combat_mode) return + if(crossing_mob.mob_size < MOB_SIZE_HUMAN) + return + if(ishuman(crossing_mob)) + var/mob/living/carbon/human/crossing_humie = crossing_mob + if(crossing_humie.mob_height <= HUMAN_HEIGHT_SHORTEST) + return crossing_mob.knockOver(owner) /obj/item/organ/brain/primate/get_attacking_limb(mob/living/carbon/human/target) diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index 9660b2d2e4c2..5a712a5a1368 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -10,9 +10,13 @@ /obj/item/organ/wings/moth = "Plain", /obj/item/organ/antennae = "Plain", ) + inherent_traits = list( + TRAIT_CLOTH_EATER, //So that moths can still eat cloth even if their stomach is augmented + ) meat = /obj/item/food/meat/slab/human/mutant/moth mutanttongue = /obj/item/organ/tongue/moth mutanteyes = /obj/item/organ/eyes/moth + mutantstomach = /obj/item/organ/stomach/moth changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT species_cookie = /obj/item/food/muffin/moffin species_language_holder = /datum/language_holder/moth diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index 93f83df35ab5..1c316f735a9f 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -6,6 +6,7 @@ inherent_traits = list( TRAIT_MUTANT_COLORS, TRAIT_PLANT_SAFE, + TRAIT_REQUIRED_ADV_HEALTH_SCANNER, ) mutant_organs = list( /obj/item/organ/pod_hair = "None", diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 038abcc22d9d..9ade8591552c 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -12,6 +12,7 @@ TRAIT_BLOOD_CLANS, TRAIT_USES_SKINTONES, TRAIT_NO_MIRROR_REFLECTION, + TRAIT_UNHOLY_BANEABLE, //still baned by silver even if they get a different heart ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID changesource_flags = MIRROR_BADMIN | MIRROR_PRIDE | WABBAJACK | ERT_SPAWN @@ -240,7 +241,7 @@ victim.show_message(span_warning("[user] tries to bite you, but recoils in disgust!")) to_chat(user, span_warning("[victim] reeks of garlic! you can't bring yourself to drain such tainted blood.")) return FALSE - if(!do_after(user, 3 SECONDS, target = victim, hidden = TRUE)) + if(!do_after(user, 3 SECONDS, target = victim, cog_icon = null)) return FALSE victim.show_message(span_danger("[user] is draining your blood!")) @@ -263,6 +264,7 @@ name = "vampire heart" icon_state = "heart_vampire" desc = "Some guy stabbed his brother 6,000 years ago so now you have this." + organ_traits = list(TRAIT_UNHOLY_BANEABLE) #undef VAMPIRES_PER_HOUSE #undef VAMP_DRAIN_AMOUNT diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index 697b3b075184..8201c0237366 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -16,6 +16,15 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GENELESS), PROC_REF(on_geneless_trait_gain)) RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_NO_SPLIT_PERSONALITY), PROC_REF(on_no_split_personality_trait_gain)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NO_OXYLOSS_PASSOUT), SIGNAL_REMOVETRAIT(TRAIT_NO_OXYLOSS_PASSOUT)), PROC_REF(check_passout)) + + RegisterSignals(src, list( + SIGNAL_ADDTRAIT(TRAIT_NOHARDCRIT), + SIGNAL_REMOVETRAIT(TRAIT_NOHARDCRIT), + SIGNAL_ADDTRAIT(TRAIT_NOSOFTCRIT), + SIGNAL_REMOVETRAIT(TRAIT_NOSOFTCRIT), + ), PROC_REF(update_stat)) + /** * On gain of TRAIT_AGENDER * diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 5185ed84ef09..7e5018a88e0d 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -451,7 +451,7 @@ /mob/living/carbon/proc/handle_blood(seconds_per_tick) return -/mob/living/carbon/reagent_tick(datum/reagent/chem, seconds_per_tick) +/mob/living/carbon/reagent_tick(datum/reagent/chem, seconds_per_tick, metabolization_ratio) . = ..() if(. & COMSIG_MOB_STOP_REAGENT_TICK) return diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 022bfc314e82..9dd3bce798d4 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -187,7 +187,7 @@ return for(var/mob/living/nearby in viewers(src)) - if(nearby == src || nearby.stat >= UNCONSCIOUS || nearby.is_blind()) + if(nearby == src || IS_UNCONSCIOUS(nearby) || nearby.is_blind()) continue nearby.add_mood_event("saw_death", /datum/mood_event/conditional/see_death, src, dusted, gibbed) nearby.mind?.witnessed_death(src) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index fda5851dddd2..7944351ca463 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -50,7 +50,7 @@ key_third_person = "bows" message = "bows." message_param = "bows to %t." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/burp key = "burp" @@ -70,7 +70,7 @@ key = "cross" key_third_person = "crosses" message = "crosses their arms." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/chuckle key = "chuckle" @@ -95,7 +95,7 @@ key = "dance" key_third_person = "dances" message = "dances around happily." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS /datum/emote/living/deathgasp key = "deathgasp" @@ -109,7 +109,7 @@ message_animal_or_basic = "stops moving..." emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_IMPORTANT cooldown = (15 SECONDS) - stat_allowed = HARD_CRIT + can_use_flags = EMOTE_CANUSE_UNCONSCIOUS | EMOTE_CANUSE_HARDCRIT | EMOTE_CANUSE_SOFTCRIT /datum/emote/living/deathgasp/run_emote(mob/living/user, params, type_override, intentional) if(!is_type_in_typecache(user, mob_type_allowed_typecache)) @@ -153,7 +153,7 @@ key = "flap" key_third_person = "flaps" message = "flaps their wings." - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS var/wing_time = 0.35 SECONDS /datum/emote/living/flap/run_emote(mob/user, params, type_override, intentional) @@ -185,7 +185,7 @@ key_third_person = "aflaps" name = "flap (Angry)" message = "flaps their wings ANGRILY!" - hands_use_check = TRUE + can_use_flags = EMOTE_CANUSE_REQUIRE_HANDS wing_time = 10 /datum/emote/living/frown @@ -206,7 +206,7 @@ message = "gasps!" message_mime = "gasps silently!" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE - stat_allowed = HARD_CRIT + can_use_flags = EMOTE_CANUSE_UNCONSCIOUS | EMOTE_CANUSE_HARDCRIT | EMOTE_CANUSE_SOFTCRIT /datum/emote/living/gasp/get_sound(mob/living/user) if(HAS_MIND_TRAIT(user, TRAIT_MIMING)) @@ -233,7 +233,7 @@ message = "gasps in shock!" message_mime = "gasps in silent shock!" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE - stat_allowed = SOFT_CRIT + can_use_flags = EMOTE_CANUSE_SOFTCRIT /datum/emote/living/giggle key = "giggle" @@ -524,7 +524,7 @@ message = "snores." message_mime = "sleeps soundly." emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE - stat_allowed = UNCONSCIOUS + can_use_flags = EMOTE_CANUSE_UNCONSCIOUS // eventually we want to give species their own "snoring" sounds /datum/emote/living/snore/get_sound(mob/living/carbon/human/user) @@ -668,7 +668,7 @@ var/propagation_distance = user.client ? 5 : 2 // mindless mobs are less able to spread yawns for(var/mob/living/iter_living in view(user, propagation_distance)) - if(IS_DEAD_OR_INCAP(iter_living) || TIMER_COOLDOWN_RUNNING(iter_living, COOLDOWN_YAWN_PROPAGATION)) + if(iter_living.incapacitated || TIMER_COOLDOWN_RUNNING(iter_living, COOLDOWN_YAWN_PROPAGATION)) continue var/dist_between = get_dist(user, iter_living) diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm index 5b82ee78330a..c3971e56cf37 100644 --- a/code/modules/mob/living/init_signals.dm +++ b/code/modules/mob/living/init_signals.dm @@ -43,18 +43,14 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_STASIS), PROC_REF(on_stasis_trait_gain)) RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_STASIS), PROC_REF(on_stasis_trait_loss)) + // DARKPACK EDIT ADD START - Torpor - (Adds torpor trait) RegisterSignals(src, list( - SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), - SIGNAL_REMOVETRAIT(TRAIT_CRITICAL_CONDITION), - SIGNAL_ADDTRAIT(TRAIT_NODEATH), SIGNAL_REMOVETRAIT(TRAIT_NODEATH), - - // DARKPACK EDIT ADD START - Torpor SIGNAL_ADDTRAIT(TRAIT_TORPOR), SIGNAL_REMOVETRAIT(TRAIT_TORPOR) - // DARKPACK EDIT ADD END - Torpor - ), PROC_REF(update_succumb_action)) + ), PROC_REF(on_nodeath)) + // DARKPACK EDIT ADD END RegisterSignal(src, COMSIG_MOVETYPE_FLAG_ENABLED, PROC_REF(on_movement_type_flag_enabled)) RegisterSignal(src, COMSIG_MOVETYPE_FLAG_DISABLED, PROC_REF(on_movement_type_flag_disabled)) @@ -84,20 +80,41 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_MIND_TEMPORARILY_GONE), PROC_REF(on_mind_temporarily_gone_trait_gain)) RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_MIND_TEMPORARILY_GONE), PROC_REF(on_mind_temporarily_gone_trait_loss)) + RegisterSignals(src, list( + SIGNAL_ADDTRAIT(TRAIT_NIGHT_VISION), + SIGNAL_REMOVETRAIT(TRAIT_NIGHT_VISION), + SIGNAL_ADDTRAIT(TRAIT_MESON_VISION), + SIGNAL_REMOVETRAIT(TRAIT_MESON_VISION), + SIGNAL_ADDTRAIT(TRAIT_TRUE_NIGHT_VISION), + SIGNAL_REMOVETRAIT(TRAIT_TRUE_NIGHT_VISION), + SIGNAL_ADDTRAIT(TRAIT_THERMAL_VISION), + SIGNAL_REMOVETRAIT(TRAIT_THERMAL_VISION), + SIGNAL_ADDTRAIT(TRAIT_XRAY_VISION), + SIGNAL_REMOVETRAIT(TRAIT_XRAY_VISION), + SIGNAL_ADDTRAIT(TRAIT_ECHOLOCATOR), + SIGNAL_REMOVETRAIT(TRAIT_ECHOLOCATOR), + ), PROC_REF(update_sight)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_PASSTABLE), SIGNAL_REMOVETRAIT(TRAIT_PASSTABLE)), PROC_REF(on_passtable_trait_toggled)) RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_PASSWINDOW), SIGNAL_REMOVETRAIT(TRAIT_PASSWINDOW)), PROC_REF(on_passwindow_trait_toggled)) /// Called when [TRAIT_KNOCKEDOUT] is added to the mob. /mob/living/proc/on_knockedout_trait_gain(datum/source) SIGNAL_HANDLER - if(stat < UNCONSCIOUS) - set_stat(UNCONSCIOUS) + + apply_status_effect(/datum/status_effect/knocked_out) + + if(stat <= SOFT_CRIT) // going into hard crit gives a similar log + log_combat(src, src, "lost consciousness") /// Called when [TRAIT_KNOCKEDOUT] is removed from the mob. /mob/living/proc/on_knockedout_trait_loss(datum/source) SIGNAL_HANDLER - if(stat <= UNCONSCIOUS) - update_stat() + + remove_status_effect(/datum/status_effect/knocked_out) + + if(stat <= SOFT_CRIT) // leaving hard crit gives a similar log + log_combat(src, src, "regained consciousness") /// Called when [TRAIT_DEATHCOMA] is added to the mob. /mob/living/proc/on_deathcoma_trait_gain(datum/source) @@ -239,13 +256,18 @@ SIGNAL_HANDLER update_incapacitated() +/// Called when [TRAIT_NODEATH] is added or removed from the mob +/mob/living/proc/on_nodeath() + SIGNAL_HANDLER + update_succumb_action() + update_stat() + /** * Called when traits that alter succumbing are added/removed. * * Will show or hide the succumb alert prompt. */ /mob/living/proc/update_succumb_action() - SIGNAL_HANDLER if (CAN_SUCCUMB(src) || HAS_TRAIT(src, TRAIT_SUCCUMB_OVERRIDE)) throw_alert(ALERT_SUCCUMB, /atom/movable/screen/alert/succumb) else diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index bf39f4aa87ee..e331f67d5e9c 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -139,9 +139,9 @@ adjust_brute_loss(min(GRAVITY_DAMAGE_SCALING * grav_strength, GRAVITY_DAMAGE_MAXIMUM) * seconds_per_tick) /// Proc used for custom metabolization of reagents, if any -/mob/living/proc/reagent_tick(datum/reagent/chem, seconds_per_tick) +/mob/living/proc/reagent_tick(datum/reagent/chem, seconds_per_tick, metabolization_ratio) SHOULD_CALL_PARENT(TRUE) - return SEND_SIGNAL(src, COMSIG_MOB_REAGENT_TICK, chem, seconds_per_tick) + return SEND_SIGNAL(src, COMSIG_MOB_REAGENT_TICK, chem, seconds_per_tick, metabolization_ratio) /// Proc used for custom reagent exposure effects, if any /mob/living/proc/reagent_expose(datum/reagent/chem, methods = TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 310da8937560..87da311cbfae 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -19,6 +19,9 @@ SSpoints_of_interest.make_point_of_interest(src) update_fov() gravity_setup() + unconscious_appearance = get_unconscious_appearance() + if(!isnull(unconscious_appearance)) + GLOB.unconscious_appearances += unconscious_appearance /mob/living/prepare_huds() ..() @@ -28,6 +31,10 @@ med_hud_set_health() med_hud_set_status() +/// Returns the appearance other mobs see instead of us while unconscious +/mob/living/proc/get_unconscious_appearance() + return null + /mob/living/Destroy() for(var/datum/status_effect/effect as anything in status_effects) // The status effect calls on_remove when its mob is deleted @@ -48,6 +55,14 @@ QDEL_LIST(imaginary_group) QDEL_LAZYLIST(diseases) QDEL_LAZYLIST(quirks) + + if(!isnull(unconscious_appearance)) + // Not super necessary strictly speaking but just in case + for(var/client/player as anything in GLOB.clients) + player?.images -= unconscious_appearance + GLOB.unconscious_appearances -= unconscious_appearance + unconscious_appearance = null + return ..() /mob/living/onZImpact(turf/impacted_turf, levels, impact_flags = NONE) @@ -78,7 +93,7 @@ if(levels <= 1 && can_help_themselves) var/obj/item/organ/wings/gliders = get_organ_by_type(/obj/item/organ/wings) if(HAS_TRAIT(src, TRAIT_FREERUNNING) || gliders?.can_soften_fall() || st_get_stat(STAT_ATHLETICS) > 4) // the power of parkour or wings allows falling short distances unscathed // DARKPACK EDIT CHANGE - STORYTELLER_STATS - var/graceful_landing = HAS_TRAIT(src, TRAIT_CATLIKE_GRACE) + var/graceful_landing = HAS_TRAIT(src, TRAIT_CATLIKE_INSTINCT) if(graceful_landing) add_movespeed_modifier(/datum/movespeed_modifier/landed_on_feet) @@ -97,7 +112,7 @@ // Smaller mobs with catlike grace can ignore damage (EG: cats) var/small_surface_area = mob_size <= MOB_SIZE_SMALL var/skip_knockdown = FALSE - if(HAS_TRAIT(src, TRAIT_CATLIKE_GRACE) && (small_surface_area || usable_legs >= 2) && body_position == STANDING_UP && can_help_themselves) + if(HAS_TRAIT(src, TRAIT_CATLIKE_INSTINCT) && (small_surface_area || usable_legs >= 2) && body_position == STANDING_UP && can_help_themselves) . |= ZIMPACT_NO_MESSAGE|ZIMPACT_NO_SPIN skip_knockdown = TRUE if(small_surface_area) @@ -405,9 +420,7 @@ log_combat(AM, AM.pulledby, "pulled from", src) AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. - pulling = AM - AM.set_pulledby(src) - + set_pulling(AM) SEND_SIGNAL(src, COMSIG_LIVING_START_PULL, AM, state, force) if(!supress_message) @@ -716,7 +729,7 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) if(istype(potential_spine)) get_up_time *= potential_spine.athletics_boost_multiplier - if(!instant && !do_after(src, get_up_time, src, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM), extra_checks = CALLBACK(src, TYPE_PROC_REF(/mob/living, rest_checks_callback)), interaction_key = DOAFTER_SOURCE_GETTING_UP, hidden = TRUE)) + if(!instant && !do_after(src, get_up_time, src, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM), extra_checks = CALLBACK(src, TYPE_PROC_REF(/mob/living, rest_checks_callback)), interaction_key = DOAFTER_SOURCE_GETTING_UP, cog_icon = null)) return if(resting || body_position == STANDING_UP || HAS_TRAIT(src, TRAIT_FLOORED)) return @@ -891,8 +904,9 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) fully_heal(full_heal_flags) if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain) + REMOVE_TRAIT(src, TRAIT_DISSECTED, AUTOPSY_TRAIT) set_suicide(FALSE) - set_stat(UNCONSCIOUS) //the mob starts unconscious, + set_stat(HARD_CRIT) //the mob starts unconscious, updatehealth() //then we check if the mob should wake up. if(full_heal_flags & HEAL_ADMIN) get_up(TRUE) @@ -1038,9 +1052,11 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) /// Checks if we are actually able to ressuscitate this mob. /// (We don't want to revive then to have them instantly die again) /mob/living/proc/can_be_revived() - if(health <= HEALTH_THRESHOLD_DEAD) - return FALSE - return TRUE + if(HAS_TRAIT(src, TRAIT_NODEATH)) + return TRUE + if(health > HEALTH_THRESHOLD_DEAD) + return TRUE + return FALSE /mob/living/proc/update_damage_overlays() return @@ -1052,7 +1068,8 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) if (!buckled.anchored) buckled.moving_from_pull = moving_from_pull . = buckled.Move(newloc, direct, glide_size) - buckled.moving_from_pull = null + if(buckled) //buckled can become unbuckled, apparently + buckled.moving_from_pull = null return var/old_direction = dir @@ -1178,11 +1195,12 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) resist_restraints() //trying to remove cuffs. /mob/proc/resist_grab(moving_resist) - return 1 //returning 0 means we successfully broke free + return FALSE // DARKPACKE EDIT CHANGE START - STORYTELLER_DICE /mob/living/resist_grab(moving_resist) - . = TRUE + if(HAS_TRAIT(src, TRAIT_INCAPACITATED)) + return FALSE var/list/grab_stats = list( // Our effective grab state. @@ -1237,19 +1255,25 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) to_chat(pulledby, span_warning("[src] breaks free of your grip!")) log_combat(pulledby, src, "broke grab") pulledby.stop_pulling() - return FALSE - else - adjust_stamina_loss(damage_on_resist_fail) //Do some stamina damage if we fail to resist - visible_message(span_danger("[src] struggles as they fail to break free of [pulledby]'s grip!"), \ - span_warning("You struggle as you fail to break free of [pulledby]'s grip!"), null, null, pulledby) - to_chat(pulledby, span_danger("[src] struggles as they fail to break free of your grip!")) + return TRUE + + adjust_stamina_loss(damage_on_resist_fail) //Do some stamina damage if we fail to resist + visible_message( + span_danger("[src] struggles as they fail to break free of [pulledby]'s grip!"), + span_warning("You struggle as you fail to break free of [pulledby]'s grip!"), + null, + null, + pulledby, + ) + to_chat(pulledby, span_danger("[src] struggles as they fail to break free of your grip!")) if(moving_resist && client) //we resisted by trying to move client.move_delay = world.time + 1 TURNS - else - pulledby.stop_pulling() return FALSE // DARKPACK EDIT CHANGE END + pulledby.stop_pulling() + return TRUE + /mob/living/proc/resist_buckle() buckled.user_unbuckle_mob(src,src) @@ -1367,7 +1391,7 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) if(!istype(target)) CRASH("Missing target arg for can_perform_action") - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) to_chat(src, span_warning("You are not conscious enough for this action!")) return FALSE @@ -1571,10 +1595,10 @@ GAME_VERB_PROC(/mob/living, mob_sleep, "Sleep", null) new_mob.SetInvisibility(INVISIBILITY_NONE) new_mob.job = JOB_CYBORG created_robot.lawupdate = FALSE - created_robot.connected_ai = null + created_robot.set_connected_ai(null) created_robot.mmi.transfer_identity(src) //Does not transfer key/client. - created_robot.clear_inherent_laws(announce = FALSE) - created_robot.clear_zeroth_law(announce = FALSE) + created_robot.laws.clear_inherent_laws() + created_robot.laws.clear_zeroth_law() if(WABBAJACK_SLIME) new_mob = new /mob/living/basic/slime/random(loc) @@ -1995,10 +2019,10 @@ GLOBAL_LIST_EMPTY(fire_appearances) //Check the amount of clients exists on the Z level we're leaving from, //this excludes us because at this point we are not registered to any z level. var/old_level_new_clients = (registered_z ? SSmobs.clients_by_zlevel[registered_z].len : null) - //No one is left after we're gone, shut off inactive ones + //No one is left after we're gone, recalculate AI status so eligible ones shut off if(registered_z && old_level_new_clients == 0) - for(var/datum/ai_controller/controller as anything in GLOB.ai_controllers_by_zlevel[registered_z]) - controller.set_ai_status(AI_STATUS_OFF) + for(var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[registered_z]) + controller.set_ai_status(controller.get_expected_ai_status()) if(new_z) //Check the amount of clients exists on the Z level we're moving towards, excluding ourselves. @@ -2008,7 +2032,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) SSmobs.clients_by_zlevel[new_z] += src if(new_level_old_clients == 0) //No one was here before, wake up all the AIs. - for (var/datum/ai_controller/controller as anything in GLOB.ai_controllers_by_zlevel[new_z]) + for (var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[new_z]) //We don't set them directly on, for instances like AIs acting while dead and other cases that may exist in the future. //This isn't a problem for AIs with a client since the client will prevent this from being called anyway. controller.set_ai_status(controller.get_expected_ai_status()) @@ -2424,66 +2448,65 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/set_stat(new_stat) . = ..() - if(isnull(.)) - return + if(isnull(.) || . == stat) + return + + // All the traits associated with any of a mob's stat + // Adding any traits below should also be done in here + var/list/removed_traits = list( + TRAIT_DEAF, + TRAIT_FLOORED, + TRAIT_HANDS_BLOCKED, + TRAIT_INCAPACITATED, + TRAIT_KNOCKEDOUT, + TRAIT_FORCE_WHISPER, + ) + // All the traits associated with the mob's current stat + var/list/added_traits = list() - if(. <= UNCONSCIOUS || new_stat >= UNCONSCIOUS) - update_eyes() + switch(stat) // Current stat + if(STABLE) + log_combat(src, src, "left crit") - switch(.) //Previous stat. - if(CONSCIOUS) - if(stat >= UNCONSCIOUS) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) - add_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_INCAPACITATED, TRAIT_FLOORED), STAT_TRAIT) - if(SOFT_CRIT) - if(stat >= UNCONSCIOUS) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) //adding trait sources should come before removing to avoid unnecessary updates - if(pulledby) - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) - if(UNCONSCIOUS) - if(stat != HARD_CRIT) - cure_blind(UNCONSCIOUS_TRAIT) - if(HARD_CRIT) - if(stat != UNCONSCIOUS) - cure_blind(UNCONSCIOUS_TRAIT) - REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) - if(DEAD) - REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) - remove_from_dead_mob_list() - add_to_alive_mob_list() - switch(stat) //Current stat. - if(CONSCIOUS) - if(. >= UNCONSCIOUS) - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) - remove_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_INCAPACITATED, TRAIT_FLOORED, TRAIT_CRITICAL_CONDITION), STAT_TRAIT) - log_combat(src, src, "regained consciousness") if(SOFT_CRIT) - if(pulledby) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) //adding trait sources should come before removing to avoid unnecessary updates - if(. >= UNCONSCIOUS) - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) - ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) log_combat(src, src, "entered soft crit") - if(UNCONSCIOUS) - if(. != HARD_CRIT) - become_blind(UNCONSCIOUS_TRAIT) - if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT)) - ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) - else - REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) - log_combat(src, src, "lost consciousness") + added_traits.Add( + TRAIT_FLOORED, + TRAIT_HANDS_BLOCKED, + TRAIT_INCAPACITATED, + TRAIT_FORCE_WHISPER, + ) + if(HARD_CRIT) - if(. != UNCONSCIOUS) - become_blind(UNCONSCIOUS_TRAIT) - ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) - ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) log_combat(src, src, "entered hard crit") + added_traits.Add( + TRAIT_DEAF, + TRAIT_FLOORED, + TRAIT_HANDS_BLOCKED, + TRAIT_INCAPACITATED, + TRAIT_KNOCKEDOUT, + ) + if(DEAD) - REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) - ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) - remove_from_alive_mob_list() - add_to_dead_mob_list() log_combat(src, src, "died") + added_traits.Add( + TRAIT_DEAF, + TRAIT_FLOORED, + TRAIT_HANDS_BLOCKED, + TRAIT_INCAPACITATED, + TRAIT_KNOCKEDOUT, + ) + + if(stat == DEAD) + remove_from_alive_mob_list() + add_to_dead_mob_list() + else if(. == DEAD) + remove_from_dead_mob_list() + add_to_alive_mob_list() + + add_traits(added_traits, STAT_TRAIT) + remove_traits(removed_traits - added_traits, STAT_TRAIT) + update_succumb_action() ///Reports the event of the change in value of the buckled variable. /mob/living/proc/set_buckled(new_buckled) @@ -2516,14 +2539,6 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/set_pulledby(new_pulledby) . = ..() update_incapacitated() - if(. == FALSE) //null is a valid value here, we only want to return if FALSE is explicitly passed. - return - if(pulledby) - if(!. && stat == SOFT_CRIT) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) - else if(. && stat == SOFT_CRIT) - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) - /// Updates the grab state of the mob and updates movespeed /mob/living/setGrabState(newstate) @@ -2617,7 +2632,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/perform_hand_swap(held_index) //safeguard for one-handed mobs lol - if(num_hands == 1) + if(length(held_items) == 1) held_index = 1 return ..() @@ -2950,7 +2965,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) var/mob/living/basic/guardian/summoned_guardian = new picked_type(src, picked_theme) summoned_guardian.set_summoner(src, different_person = TRUE) if(picked_name) - summoned_guardian.fully_replace_character_name(null, picked_name) + summoned_guardian.fully_replace_character_name(null, picked_name, log_new_name = TRUE) if(picked_color) summoned_guardian.set_guardian_colour(picked_color) summoned_guardian.PossessByPlayer(guardian_client?.key) @@ -3035,6 +3050,17 @@ GLOBAL_LIST_EMPTY(fire_appearances) return span_notice("You'd estimate [p_their()] fitness level at about [our_fitness_level]. [comparative_fitness <= 0.33 ? "Pathetic." : ""]") +/// Check if bees are not hostile to us +/mob/living/proc/bee_friendly() + if(mob_biotypes & MOB_PLANT) + return TRUE + var/obj/item/clothing/suit = get_item_by_slot(ITEM_SLOT_OCLOTHING) + var/obj/item/clothing/hat = get_item_by_slot(ITEM_SLOT_HEAD) + if(!istype(suit) || !istype(hat)) + return FALSE + if(suit.clothing_flags & hat.clothing_flags & THICKMATERIAL) + return TRUE + ///Performs the aftereffects of blocking a projectile. /mob/living/proc/block_projectile_effects() var/static/list/icon/blocking_overlay @@ -3078,3 +3104,13 @@ GLOBAL_LIST_EMPTY(fire_appearances) remove_verb(src, /mob/living/verb/pulled) else add_verb(src, /mob/living/verb/pulled) + +/// Generic helper to return a static-y humanoid appearance shown to other mobs when unconscious +/mob/living/proc/get_generic_humanoid_static_appearance() + SHOULD_NOT_OVERRIDE(TRUE) + + var/image/static_image = image('icons/effects/effects.dmi', src, "static") + static_image.override = TRUE + static_image.name = "unknown humanoid" + + return static_image diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 53c3b83c1b12..e6014d7bd961 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -31,15 +31,19 @@ /mob/living/proc/getarmor(def_zone, type) return 0 -//this returns the mob's protection against eye damage (number between -1 and 2) from bright lights +/// This returns the mob's protection against eye damage (number between -1 and 2) from bright lights /mob/living/proc/get_eye_protection() return 0 -///A easy to use proc to apply both organ damage and temporary deafness at once, so you don't have to get the ears everytime. +/// This returns a number is subtracted from the severity of incoming emps against this mob. +/mob/living/proc/get_emp_protection() + return emp_protection + +/// An easy to use proc to apply both organ damage and temporary deafness at once, so you don't have to get the ears everytime. /mob/living/proc/sound_damage(damage, deafen) return -//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears) +/// This returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears) /mob/living/proc/get_ear_protection(ignore_deafness = FALSE) if(!ignore_deafness && HAS_TRAIT(src, TRAIT_DEAF)) return INFINITY //For all my homies that can not hear in the world @@ -615,6 +619,7 @@ . = ..() if(. & EMP_PROTECT_CONTENTS) return + severity += get_emp_protection() for(var/obj/inside in contents) inside.emp_act(severity) @@ -887,9 +892,10 @@ return FALSE if(has_status_effect(/datum/status_effect/hallucination) || has_status_effect(/datum/status_effect/drugginess)) return TRUE - if(IsSleeping() || IsUnconscious()) + if(IS_UNCONSCIOUS(src)) return TRUE if(HAS_TRAIT(src, TRAIT_DUMB)) return TRUE - if(mob_mood && mob_mood.sanity < SANITY_UNSTABLE) + if(mob_mood?.sanity < SANITY_UNSTABLE) return TRUE + return FALSE diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index f5e93fd5aa61..13f52e43240e 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -190,6 +190,7 @@ /// list of all diseases in a mob var/list/diseases var/list/disease_resistances + var/list/symptom_resistances ///Whether the mob is slowed down when dragging another prone mob var/slowed_by_drag = TRUE @@ -261,4 +262,10 @@ /// When less than or equal to this distance (but not adjacent), this mob can hear parts of distant whispers, but not the entire message. /// When greater than this distance, this mob cannot hear anything of a whisper. - var/eavesdrop_range = EAVESDROP_EXTRA_RANGE + var/eavesdrop_range = EAVESDROP_RANGE + + /// Reference to the unconscious appearance image that appears in place of the mob to other knocked out mobs + VAR_FINAL/image/unconscious_appearance + + /// Reduces the effects of EMPs, does NOT negate them even at very high numbers + var/emp_protection = EMP_PROTECTION_NONE diff --git a/code/modules/mob/living/living_item_handling.dm b/code/modules/mob/living/living_item_handling.dm index 7036f6bd65cc..1e94b42f83d9 100644 --- a/code/modules/mob/living/living_item_handling.dm +++ b/code/modules/mob/living/living_item_handling.dm @@ -1,6 +1,6 @@ // Throwing stuff /mob/living/proc/toggle_throw_mode() - if(stat) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return if(!HAS_TRAIT(src, TRAIT_CAN_THROW_ITEMS)) return @@ -132,7 +132,7 @@ to_chat(src, span_warning("You're already offering something!")) return - if(IS_DEAD_OR_INCAP(src)) + if(src.incapacitated) to_chat(src, span_warning("You're unable to offer anything in your current state!")) return @@ -154,7 +154,7 @@ to_chat(src, span_notice("You take [offered_item] from yourself.")) return - if(IS_DEAD_OR_INCAP(offered)) + if(offered.incapacitated) to_chat(src, span_warning("[offered.p_Theyre()] unable to take anything in [offered.p_their()] current state!")) return @@ -189,7 +189,7 @@ */ /mob/living/proc/take(mob/living/offerer, obj/item/offered_item, bypass) clear_alert("[offerer]") - if(IS_DEAD_OR_INCAP(src)) + if(src.incapacitated) to_chat(src, span_warning("You're unable to take anything in your current state!")) return if(get_dist(src, offerer) > 1) diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 8ff67ffb6d93..876ec8c87256 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -158,6 +158,6 @@ return ..() /mob/living/keybind_face_direction(direction) - if(stat > SOFT_CRIT) + if(IS_UNCONSCIOUS(src)) return return ..() diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index f56773199848..e78f41519361 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -154,23 +154,20 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( var/say_radio_or_mode = saymode || message_mods[RADIO_EXTENSION] if(say_radio_or_mode) var/mob_stat_limit = GLOB.message_modes_stat_limits[say_radio_or_mode] - if(stat > (isnull(mob_stat_limit) ? CONSCIOUS : mob_stat_limit)) + if(stat > (isnull(mob_stat_limit) ? STABLE : mob_stat_limit)) saymode = null message_mods -= RADIO_EXTENSION - switch(stat) - if(SOFT_CRIT) - message_mods[WHISPER_MODE] = MODE_WHISPER - if(UNCONSCIOUS) - return - if(HARD_CRIT) - if(!message_mods[WHISPER_MODE]) - return - if(DEAD) - say_dead(original_message, message_mods[MANNEQUIN_CONTROLLED]) - return + // this is what stops you from talking while dead + if(stat == DEAD) + say_dead(original_message, message_mods[MANNEQUIN_CONTROLLED]) + return - if(HAS_TRAIT(src, TRAIT_SOFTSPOKEN) && !HAS_TRAIT(src, TRAIT_SIGN_LANG)) // softspoken trait only applies to spoken languages + // this is what stops you from talking while asleep, and also what allows you to deathgasp in hard crit + if(IS_UNCONSCIOUS(src) && (stat != HARD_CRIT || !message_mods[WHISPER_MODE])) + return + + if(HAS_TRAIT(src, TRAIT_FORCE_WHISPER)) message_mods[WHISPER_MODE] = MODE_WHISPER if(client && SSlag_switch.measures[SLOWMODE_SAY] && !HAS_TRAIT(src, TRAIT_BYPASS_MEASURES) && !forced && src == usr) @@ -190,6 +187,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( if(!message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) if(message_mods[WHISPER_MODE] == MODE_WHISPER) message_range = 1 + // this is where deathgasping is processed if(stat == HARD_CRIT) var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health) // If we cut our message short, abruptly end it with a-.. @@ -238,7 +236,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( return //Get which verb is prefixed to the message before radio but after most modifications - message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) + message_mods[SAY_MOD_VERB] ||= say_mod(message, message_mods) var/identifier = "invalid" var/tts_message_to_use = tts_message || message @@ -305,7 +303,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( // DARKPACK EDIT ADD START - MERITS_FLAWS - (acute sense) if (HAS_TRAIT(src, TRAIT_ACUTE_HEARING) && !HAS_TRAIT(speaker, TRAIT_SIGN_LANG))// we can't HEAR sign, so we don't care for it - message_range += 3 // Increase how far we can hear + message_range += 1 // Increase how far we can hear // DARKPACK EDIT ADD END var/atom/movable/virtualspeaker/holopad_speaker = speaker @@ -321,44 +319,55 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( var/speaker_is_signing = HAS_TRAIT(speaker, TRAIT_SIGN_LANG) var/use_runechat = client?.prefs.read_preference(/datum/preference/toggle/enable_runechat) - if (stat == UNCONSCIOUS || stat == HARD_CRIT) + if (IS_UNCONSCIOUS_AND_ALIVE(src)) use_runechat = FALSE else if (!ismob(speaker) && !client?.prefs.read_preference(/datum/preference/toggle/enable_runechat_non_mobs)) use_runechat = FALSE var/message = "" - // if someone is whispering we make an extra type of message that is obfuscated for people out of range - // Less than or equal to 0 means normal hearing. More than 0 and less than or equal to eavesdrop_range means - // partial hearing. More than eavesdrop_range means no hearing. Exception for GOOD_HEARING trait - var/dist = get_dist(speaker, src) - message_range - if(dist > 0 && dist <= eavesdrop_range && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) - raw_message = stars(raw_message) var/speaker_name = span_name("[message_mods[MODE_SPEAKER_NAME_OVERRIDE] || speaker]") - if(message_range != INFINITY && dist > eavesdrop_range && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) - // Too far away and don't have good hearing, you can't hear anything - if(is_blind() || HAS_TRAIT(speaker, TRAIT_INVISIBLE_MAN)) // Can't see them speak either - return FALSE - if(!isturf(speaker.loc)) // If they're inside of something, probably can't see them speak - return FALSE - - // But we can still see them speak - if(speaker_is_signing) - deaf_message = "[speaker_name] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar." - else if(!HAS_TRAIT(src, TRAIT_DEAF)) // If we can't hear we want to continue to the default deaf message - if(isliving(speaker)) - var/mob/living/living_speaker = speaker - var/mouth_hidden = living_speaker.is_mouth_covered() || HAS_TRAIT(living_speaker, TRAIT_FACE_COVERED) - if(mouth_hidden && !HAS_TRAIT(src, TRAIT_SEE_MASK_WHISPER)) // Can't see them speak if their mouth is covered or hidden, unless we're an empath - return FALSE - deaf_message = "[speaker_name] [speaker.verb_whisper] something, but you are too far away to hear [speaker.p_them()]." - - if(deaf_message) - deaf_type = MSG_VISUAL - message = deaf_message - show_message(message, MSG_VISUAL, deaf_message, deaf_type, avoid_highlight) + // Infinite range implies something like telecomms, ie something that should never be distance modified + if(message_range != INFINITY && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) + var/raw_dist = get_dist(speaker, src) + // Check for projected whispers, calculate distance from the projected tile if so + if(message_mods[WHISPER_MODE]) + var/turf/in_front = get_step(speaker, speaker.dir) + if(in_front && HAS_TRAIT(in_front, TRAIT_TURF_PROJECTS_WHISPERS)) + raw_dist = min(raw_dist, get_dist(in_front, src)) + + // How far we are we outside the message range? + var/outside_dist = max(raw_dist - message_range, 0) + // Out of message range AND out of eavesdrop range - interrupt message entirely + if(outside_dist > eavesdrop_range) + // Can't see them speak either. No message + if(is_blind() || HAS_TRAIT(speaker, TRAIT_INVISIBLE_MAN)) + return FALSE + // If they're inside of something, probably can't see them speak. No message + if(!isturf(speaker.loc)) + return FALSE + + // But we can still see them speak + if(speaker_is_signing) + deaf_message = "[speaker_name] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar." + // If we can't hear we want to continue to the default deaf message + else if(!HAS_TRAIT(src, TRAIT_DEAF)) + if(isliving(speaker)) + var/mob/living/living_speaker = speaker + var/mouth_hidden = living_speaker.is_mouth_covered() || HAS_TRAIT(living_speaker, TRAIT_FACE_COVERED) + if(mouth_hidden && !HAS_TRAIT(src, TRAIT_SEE_MASK_WHISPER)) // Can't see them speak if their mouth is covered or hidden, unless we're an empath + return FALSE + + deaf_message = "[speaker_name] [speaker.verb_whisper] something, but you are too far away to hear [speaker.p_them()]." + + if(deaf_message) + deaf_type = MSG_VISUAL + message = deaf_message + return show_message(message, MSG_VISUAL, deaf_message, deaf_type, avoid_highlight) return FALSE - + // Out of message range but within eavesdrop range - alter displayed message + if(outside_dist > 0) + raw_message = stars(raw_message) // we need to send this signal before compose_message() is used since other signals need to modify // the raw_message first. After the raw_message is passed through the various signals, it's ready to be formatted @@ -429,6 +438,12 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( var/list/in_view = get_hearers_in_view(message_range + whisper_range, source) var/list/listening = get_hearers_in_range(message_range + whisper_range, source) + if(is_speaker_whispering) + var/turf/in_front = get_step(src, dir) + if(in_front && HAS_TRAIT(in_front, TRAIT_TURF_PROJECTS_WHISPERS)) + in_view |= get_hearers_in_view(message_range + whisper_range, in_front) + listening |= get_hearers_in_range(message_range + whisper_range, in_front) + // Pre-process listeners to account for line-of-sight for(var/atom/movable/listening_movable as anything in listening) if(!(listening_movable in in_view) && !HAS_TRAIT(listening_movable, TRAIT_XRAY_HEARING)) diff --git a/code/modules/mob/living/living_update_icons.dm b/code/modules/mob/living/living_update_icons.dm index 8850129e9486..913c0edf38d4 100644 --- a/code/modules/mob/living/living_update_icons.dm +++ b/code/modules/mob/living/living_update_icons.dm @@ -63,6 +63,7 @@ readjust_atom_huds(animate_time) SEND_SIGNAL(src, COMSIG_LIVING_POST_UPDATE_TRANSFORM, resize, lying_angle, is_opposite_angle) + unconscious_appearance?.transform = ntransform return TRUE /mob/living/proc/readjust_atom_huds(animate_time = null) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 8752023b0403..dfd30cf0d8f5 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -2,36 +2,52 @@ #define CHARACTER_TYPE_SELF "My Character" #define CHARACTER_TYPE_CREWMEMBER "Station Member" -/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai, latejoining = FALSE) +/** + * Init args + * * target_ai - The mob taking control of the AI + * * base_laws - An instance or typepath of a law datum, if provided the AI will use that lawset instead of making its own + * * default_link - An ai_law_rack object to link to on spawn, if possible. Base laws is preferred over this. + * * force_mind_move - If TRUE, forces the mind to move even if it is inactive. + */ +/mob/living/silicon/ai/Initialize(mapload, mob/target_ai, datum/ai_laws/base_laws, obj/machinery/ai_law_rack/base/default_link, force_mind_move = FALSE) . = ..() - if(!target_ai) //If there is no player/brain inside. + if(isnull(target_ai)) //If there is no player/brain inside. new/obj/structure/ai_core(loc, CORE_STATE_FINISHED) //New empty terminal. return INITIALIZE_HINT_QDEL //Delete AI. + if(target_ai.client) + set_gender(target_ai.client) + ADD_TRAIT(src, TRAIT_NO_TELEPORT, AI_ANCHOR_TRAIT) status_flags &= ~CANPUSH //AI starts anchored, so dont push it - if(L && istype(L, /datum/ai_laws)) - laws = L - laws.associate(src) - for (var/law in laws.inherent) - lawcheck += law + if(istype(base_laws, /datum/ai_laws)) + laws = base_laws.copy_lawset() + else if(ispath(base_laws, /datum/ai_laws)) + laws = new base_laws() else make_laws() - for (var/law in laws.inherent) - lawcheck += law + if(default_link?.can_link_to(src)) + default_link.link_silicon(src, announce = FALSE) + else + link_to_first_rack() + + var/datum/antagonist/malf_ai/malf_datum = IS_MALF_AI(target_ai) + malf_datum?.add_law_zero() + + law_ui.update_inherent_stated_laws(laws) create_eye() - if((target_ai.mind && target_ai.mind.active) || SSticker.current_state == GAME_STATE_SETTING_UP || latejoining) + if(target_ai.mind && (target_ai.mind.active || force_mind_move)) target_ai.mind.transfer_to(src) if(is_antag()) to_chat(src, span_userdanger("You have been installed as an AI! ")) to_chat(src, span_danger("You must obey your silicon laws above all else. Your objectives will consider you to be dead.")) - if(!mind.has_ever_been_ai) - mind.has_ever_been_ai = TRUE - else if(target_ai.key) - key = target_ai.key + mind.has_ever_been_ai = TRUE + + else if(target_ai.ckey) + PossessByPlayer(target_ai.ckey) to_chat(src, span_bold("You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).")) to_chat(src, span_bold("To look at other parts of the station, click on yourself to get a camera menu.")) @@ -107,6 +123,12 @@ continue to_chat(McMobby,span_binarysay("\[ SYSTEM \] NEW REMOTE HOST HAS CONNECTED TO THIS CHANNEL -- ID: [src]"), type = MESSAGE_TYPE_RADIO) + RegisterSignal(src, COMSIG_SILICON_MODULE_RACK_LAWSET_UPDATE, PROC_REF(lawset_updated_sync_borgs)) + +/mob/living/silicon/ai/mind_initialize() + . = ..() + mind.has_ever_been_ai = TRUE + /mob/living/silicon/ai/weak_syndie radio = /obj/item/radio/headset/silicon/ai/evil radio_enabled = TRUE @@ -249,7 +271,7 @@ GAME_VERB_DESC(/mob/living/silicon/ai, pick_status_display, "Set AI Status Displ /mob/living/silicon/ai/get_status_tab_items() . = ..() - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) . += "Systems nonfunctional" return . += "System integrity: [(health + 100) * 0.5]%" @@ -261,7 +283,7 @@ GAME_VERB_DESC(/mob/living/silicon/ai, pick_status_display, "Set AI Status Displ var/robot_status = "Nominal" if(connected_robot.shell) robot_status = "AI SHELL" - else if(connected_robot.stat != CONSCIOUS || !connected_robot.client) + else if(IS_UNCONSCIOUS_OR_CRIT(connected_robot) || !connected_robot.client) robot_status = "OFFLINE" else if(!connected_robot.cell || connected_robot.cell.charge <= 0) robot_status = "DEPOWERED" @@ -404,7 +426,7 @@ GAME_VERB(/mob/living/silicon/ai, toggle_anchor, "Toggle Floor Bolts", "AI Comma copied_mmi.brain?.suicided = suicided // we can't guarantee that the MMI has a brain... sigh if(copied_mmi.brainmob.stat == DEAD && !suicided) - copied_mmi.brainmob.set_stat(CONSCIOUS) + copied_mmi.brainmob.set_stat(STABLE) copied_mmi.update_appearance() return copied_mmi @@ -840,16 +862,17 @@ GAME_VERB_PROC_DESC(/mob/living/silicon/ai, set_automatic_say_channel, "Set Auto create_chat_message(speaker, message_language, raw_message, spans) show_message(rendered, 2) -/mob/living/silicon/ai/fully_replace_character_name(oldname,newname) - ..() - if(oldname != real_name) - if(eyeobj) - eyeobj.name = "[newname] (AI Eye)" - modularInterface.imprint_id(name = real_name) +/mob/living/silicon/ai/fully_replace_character_name(oldname, newname, log_new_name = FALSE) + . = ..() + if(!.) + return + if(eyeobj) + eyeobj.name = "[newname] (AI Eye)" + modularInterface.imprint_id(name = real_name) - // Notify Cyborgs - for(var/mob/living/silicon/robot/Slave in connected_robots) - Slave.show_laws() + // Notify Cyborgs + for(var/mob/living/silicon/robot/slave as anything in connected_robots) + slave.show_laws() /datum/action/innate/choose_modules name = "Malfunction Modules" @@ -1042,11 +1065,6 @@ GAME_VERB_DESC(/mob/living/silicon/ai, deploy_to_shell, "Deploy to Shell", "Tran /mob/living/silicon/ai/resist() return -/mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai) - if(!target_ai) - target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct - . = ..() - /mob/living/silicon/ai/proc/camera_visibility(mob/eye/camera/ai/moved_eye) SScameras.update_eye_chunk(moved_eye) @@ -1102,6 +1120,18 @@ GAME_VERB_DESC(/mob/living/silicon/ai, deploy_to_shell, "Deploy to Shell", "Tran return ai_voicechanger.say_name return ..() +/mob/living/silicon/ai/get_unconscious_appearance() + var/image/static_overlay = image('icons/effects/effects.dmi', null, "static_base") + static_overlay.blend_mode = BLEND_INSET_OVERLAY + + var/image/static_image = image('icons/mob/silicon/ai.dmi', src, "ai-empty") + static_image.appearance_flags |= KEEP_TOGETHER + static_image.overlays += static_overlay + static_image.override = TRUE + static_image.name = "unknown AI" + + return static_image + /mob/living/silicon/ai/proc/set_control_disabled(control_disabled) SEND_SIGNAL(src, COMSIG_SILICON_AI_SET_CONTROL_DISABLED, control_disabled) src.control_disabled = control_disabled @@ -1256,6 +1286,13 @@ GAME_VERB_DESC(/mob/living/silicon/ai, deploy_to_shell, "Deploy to Shell", "Tran . += emissive_appearance(icon, lights_state, src) +/mob/living/silicon/ai/proc/lawset_updated_sync_borgs(datum/source, obj/machinery/ai_law_rack/rack, announce = TRUE) + SIGNAL_HANDLER + + for(var/mob/living/silicon/robot/bot as anything in connected_robots) + if(bot.try_sync_laws() && announce) + bot.show_laws() + bot.law_change_counter++ /mob/living/silicon/ai/point_at(atom/pointed_atom, intentional = FALSE) if(pointed_atom in src) diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index d9b099eb414e..a2eb8e12701a 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -1,16 +1,3 @@ - -/mob/living/silicon/ai/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - if(istype(W, /obj/item/ai_module)) - var/obj/item/ai_module/MOD = W - disconnect_shell() - if(!mind) //A player mind is required for law procs to run antag checks. - to_chat(user, span_warning("[src] is entirely unresponsive!")) - return - MOD.install(laws, user) //Proc includes a success mesage so we don't need another one - return - - return ..() - /mob/living/silicon/ai/blob_act(obj/structure/blob/B) if (stat != DEAD) adjust_brute_loss(60) @@ -60,13 +47,10 @@ return TRUE /mob/living/silicon/ai/wrench_act(mob/living/user, obj/item/tool) - . = ..() - if(user.combat_mode) - return - if(stat != DEAD && !incapacitated && (client || deployed_shell?.client)) + if(!incapacitated && (client || deployed_shell?.client)) // alive and well AIs control their floor bolts - balloon_alert(user, "the AI's bolt motors resist.") - return ITEM_INTERACT_SUCCESS + balloon_alert(user, "the AI's bolt motors resist!") + return ITEM_INTERACT_BLOCKING balloon_alert(user, "[!is_anchored ? "tightening" : "loosening"] bolts...") balloon_alert(src, "bolts being [!is_anchored ? "tightened" : "loosened"]...") if(!tool.use_tool(src, user, 4 SECONDS)) diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index 97d855d6bb9a..198e1ab3289c 100644 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -4,10 +4,6 @@ GAME_VERB_PROC_DESC(/mob/living/silicon/ai, show_laws_verb, "Show Laws", "Check return //won't work if dead src.show_laws() -/mob/living/silicon/ai/show_laws() - . = ..() - try_sync_laws() // Yes we lawsync borgs EVERY TIME WE CHECK LAWS - /mob/living/silicon/ai/try_sync_laws() for(var/mob/living/silicon/robot/borgo in connected_robots) if(borgo.try_sync_laws()) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index ad7049332e90..fb7c822caa42 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -65,12 +65,8 @@ /mob/living/silicon/ai/update_stat() if(HAS_TRAIT(src, TRAIT_GODMODE)) return - if(stat != DEAD) - if(health <= HEALTH_THRESHOLD_DEAD) - death() - return - else if(stat >= UNCONSCIOUS) - set_stat(CONSCIOUS) + if(stat != DEAD && health <= HEALTH_THRESHOLD_DEAD) + death() diag_hud_set_status() /mob/living/silicon/ai/update_sight() diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index be8f5e42c383..7e3329b94f98 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -1,118 +1,84 @@ -/mob/living/silicon/proc/show_laws() - laws_sanity_check() +/** + * Shows the silicon's current laws in chat. + * + * You can optionally pass a mob to show to instead of the silicon itself. + */ +/mob/living/silicon/proc/show_laws(mob/show_to) var/list/law_box = list(span_bold("Obey these laws:")) law_box += laws.get_law_list(include_zeroth = TRUE) - to_chat(src, boxed_message(jointext(law_box, "\n"))) - -/mob/living/silicon/proc/try_sync_laws() - return - -/mob/living/silicon/proc/laws_sanity_check() - if (!laws) - make_laws() + to_chat(show_to || src, boxed_message(jointext(law_box, "\n"))) +/** + * Logs the silicon's current laws to the server log. + */ /mob/living/silicon/proc/log_current_laws() var/list/the_laws = laws.get_law_list(include_zeroth = TRUE) var/lawtext = the_laws.Join(" ") log_silicon("LAW: [key_name(src)] spawned with [lawtext]") +/** + * Notify's dead chat that a silicon's laws (may) have changed, with a link to view the new laws. + */ /mob/living/silicon/proc/deadchat_lawchange() + if(!SSticker.HasRoundStarted()) + return var/list/the_laws = laws.get_law_list(include_zeroth = TRUE) var/lawtext = the_laws.Join("
    ") deadchat_broadcast("'s laws were changed. View", span_name("[src]"), follow_target=src, message_type=DEADCHAT_LAWCHANGE) -/mob/living/silicon/proc/post_lawchange(announce = TRUE) +/** + * Throws a screen alert indicating that the silicon's laws have changed + * + * If announce is TRUE, also sends a chat message to the silicon and plays a sound, + * followed by printing the new laws to the silicon's chat and informing deadchat of the law change. + */ +/mob/living/silicon/proc/announce_law_change(announce = TRUE) throw_alert(ALERT_NEW_LAW, /atom/movable/screen/alert/newlaw) if(announce && last_lawchange_announce != world.time) to_chat(src, span_bolddanger("Your laws have been changed.")) SEND_SOUND(src, sound('sound/machines/cryo_warning.ogg')) // lawset modules cause this function to be executed multiple times in a tick, so we wait for the next tick in order to be able to see the entire lawset - addtimer(CALLBACK(src, PROC_REF(show_laws)), 0) - addtimer(CALLBACK(src, PROC_REF(deadchat_lawchange)), 0) + addtimer(CALLBACK(src, PROC_REF(show_laws)), 0, TIMER_UNIQUE | TIMER_OVERRIDE) + addtimer(CALLBACK(src, PROC_REF(deadchat_lawchange)), 0, TIMER_UNIQUE | TIMER_OVERRIDE) last_lawchange_announce = world.time -/mob/living/silicon/proc/set_zeroth_law(law, law_borg, announce = TRUE) - laws_sanity_check() - laws.set_zeroth_law(law, law_borg) - post_lawchange(announce) - -/mob/living/silicon/proc/add_inherent_law(law, announce = TRUE) - laws_sanity_check() - laws.add_inherent_law(law) - lawcheck += law - post_lawchange(announce) - -/mob/living/silicon/proc/clear_inherent_laws(announce = TRUE) - laws_sanity_check() - for (var/law in laws.inherent) - if (law in lawcheck) - lawcheck -= law - laws.clear_inherent_laws() - post_lawchange(announce) - -/mob/living/silicon/proc/add_supplied_law(number, law, announce = TRUE) - laws_sanity_check() - laws.add_supplied_law(number, law) - lawcheck += law - post_lawchange(announce) - -/mob/living/silicon/proc/clear_supplied_laws(announce = TRUE) - laws_sanity_check() - for(var/law in laws.supplied) - if (law in lawcheck) - lawcheck -= law - laws.clear_supplied_laws() - post_lawchange(announce) - -/mob/living/silicon/proc/add_ion_law(law, announce = TRUE) - laws_sanity_check() - laws.add_ion_law(law) - ioncheck += law - post_lawchange(announce) - -/mob/living/silicon/proc/add_hacked_law(law, announce = TRUE) - laws_sanity_check() - laws.add_hacked_law(law) - hackedcheck += law - post_lawchange(announce) - -/mob/living/silicon/proc/replace_random_law(law, remove_law_groups, insert_law_group, announce = TRUE) - laws_sanity_check() - . = laws.replace_random_law(law, remove_law_groups, insert_law_group) - post_lawchange(announce) - -/mob/living/silicon/proc/shuffle_laws(list/groups, announce = TRUE) - laws_sanity_check() - laws.shuffle_laws(groups) - post_lawchange(announce) - -/mob/living/silicon/proc/remove_law(number, announce = TRUE) - laws_sanity_check() - . = laws.remove_law(number) - if (. in lawcheck) - lawcheck -= . - post_lawchange(announce) - -/mob/living/silicon/proc/clear_ion_laws(announce = TRUE) - laws_sanity_check() - laws.clear_ion_laws() - ioncheck = list() - post_lawchange(announce) - -/mob/living/silicon/proc/clear_hacked_laws(announce = TRUE) - laws_sanity_check() - laws.clear_hacked_laws() - hackedcheck = list() - post_lawchange(announce) - /mob/living/silicon/proc/make_laws() - laws = new /datum/ai_laws - laws.set_laws_config() - laws.associate(src) - -/mob/living/silicon/proc/clear_zeroth_law(force, announce = TRUE) - laws_sanity_check() - var/zeroth = laws.zeroth - if(laws.clear_zeroth_law(force)) - lawcheck -= zeroth - post_lawchange(announce) + laws = new() + laws.name = "Inherent Laws" + +/// Find the first law rack we can link to and link to it +/mob/living/silicon/proc/link_to_first_rack() + for(var/obj/machinery/ai_law_rack/base/core/law_rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base/core)) + if(!law_rack.can_link_to(src)) + continue + + law_rack.link_silicon(src, announce = FALSE) + return law_rack + +/// Returns the law rack this silicon is linked to, or null if not linked. +/mob/living/silicon/proc/get_law_rack() as /obj/machinery/ai_law_rack/base + for(var/obj/machinery/ai_law_rack/base/rack as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/ai_law_rack/base)) + for(var/name in rack.linked_mobs) + if(rack.linked_mobs[name] == src) + return rack + return null + +/// Unlinks the silicon from the law rack, if it is linked. +/mob/living/silicon/proc/unlink_from_law_rack() + get_law_rack()?.unlink_silicon(src) + +/** + * When given a typepath to a law datum, replaces the silicon's current law set with a new one of that type. + * + * Before replacing the lawset, unlinks the silicon from any law rack + * + * Handles informing the silicon of the law change + syncing borgs for AIs + */ +/mob/living/silicon/proc/replace_law_set(new_law_type) + unlink_from_law_rack() + laws = new new_law_type() + announce_law_change() + +/mob/living/silicon/ai/replace_law_set(new_law_type) + . = ..() + try_sync_laws() diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index be5bc6e95cdd..b3c8bc6111e3 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -36,14 +36,15 @@ if(cell && cell.charge <= 0) . += span_warning("[p_Their()] battery indicator is blinking red!") + if(IS_UNCONSCIOUS_AND_ALIVE(src)) + . += span_warning("[p_They()] do[p_es()]n't seem to be responding.") + switch(stat) - if(CONSCIOUS) + if(STABLE) if(shell) . += "[p_They()] appear[p_s()] to be an [deployed ? "active" : "empty"] AI shell." else if(!client) . += "[p_They()] appear[p_s()] to be in stand-by mode." //afk - if(SOFT_CRIT, UNCONSCIOUS, HARD_CRIT) - . += span_warning("[p_They()] do[p_es()]n't seem to be responding.") if(DEAD) . += span_deadsay("[p_They()] look[p_s()] like its system is corrupted and requires a reset.") diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index f4d9f147b18b..00087ac89d2d 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -7,7 +7,7 @@ /mob/living/silicon/robot/show_laws() if(lawupdate) if (!QDELETED(connected_ai)) - if(connected_ai.stat != CONSCIOUS || connected_ai.control_disabled) + if(IS_UNCONSCIOUS_OR_CRIT(connected_ai) || connected_ai.control_disabled) to_chat(src, span_bold("AI signal lost, unable to sync laws.")) else @@ -28,57 +28,38 @@ else to_chat(src, span_bold("Remember, you are not bound to any AI, you are not required to listen to them.")) +/** + * For AIs, iterates over connected cyborg and calls try_sync_laws + * For cyborgs, checks if we have a master AI and, if lawupdate is set, syncs law and misc. with it + */ +/mob/living/silicon/proc/try_sync_laws() + return + /mob/living/silicon/robot/try_sync_laws() if(QDELETED(connected_ai) || !lawupdate) return FALSE - lawsync() - law_change_counter++ + sync_to_ai() return TRUE -/mob/living/silicon/robot/proc/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai?.laws - var/temp - if (master) - laws.ion.len = master.ion.len - for (var/index in 1 to master.ion.len) - temp = master.ion[index] - if (length(temp) > 0) - laws.ion[index] = temp - - laws.hacked.len = master.hacked.len - for (var/index in 1 to master.hacked.len) - temp = master.hacked[index] - if (length(temp) > 0) - laws.hacked[index] = temp - - if(master.zeroth_borg) //If the AI has a defined law zero specifically for its borgs, give it that one, otherwise give it the same one. --NEO - temp = master.zeroth_borg - else - temp = master.zeroth - laws.zeroth = temp - - laws.inherent.len = master.inherent.len - for (var/index in 1 to master.inherent.len) - temp = master.inherent[index] - if (length(temp) > 0) - laws.inherent[index] = temp +/mob/living/silicon/robot/proc/sync_to_ai() + picturesync() + lawsync() - laws.supplied.len = master.supplied.len - for (var/index in 1 to master.supplied.len) - temp = master.supplied[index] - if (length(temp) > 0) - laws.supplied[index] = temp +/mob/living/silicon/robot/proc/picturesync() + if(isnull(connected_ai?.aicamera) || isnull(aicamera)) + return + for(var/i in aicamera.stored) + connected_ai.aicamera.stored[i] = TRUE + for(var/i in connected_ai.aicamera.stored) + aicamera.stored[i] = TRUE - var/datum/computer_file/program/robotact/program = modularInterface.get_robotact() - if(program) - var/datum/tgui/active_ui = SStgui.get_open_ui(src, program.computer) - if(active_ui) - active_ui.send_full_update() +/mob/living/silicon/robot/proc/lawsync() + connected_ai?.laws?.ai_to_cyborg(laws) - picturesync() + var/datum/computer_file/program/robotact/program = modularInterface.get_robotact() + program?.computer?.update_static_data_for_all_viewers() -/mob/living/silicon/robot/post_lawchange(announce = TRUE) +/mob/living/silicon/robot/announce_law_change() . = ..() addtimer(CALLBACK(src, PROC_REF(logevent),"Law update processed."), 0, TIMER_UNIQUE | TIMER_OVERRIDE) //Post_Lawchange gets spammed by some law boards, so let's wait it out diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index a4da3bd1c802..b14d5c7ab4cb 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -13,7 +13,7 @@ if(low_power_mode) if(cell?.charge) low_power_mode = FALSE - else if(stat == CONSCIOUS) + else if(!IS_UNCONSCIOUS_OR_CRIT(src)) use_energy(seconds_per_tick) /mob/living/silicon/robot/proc/use_energy(seconds_per_tick) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 2a78778bc884..0ea9ca18201b 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,4 +1,11 @@ -/mob/living/silicon/robot/Initialize(mapload) +/** + * Init args + * * innate_laws: If set, the cyborg will use this lawset and will not linked to an ai. Make sure you're passing a copy of the lawset + * * master_ai: If set, the cyborg will try to link to this ai on spawn rather than the first ai found + * * aisync: If TRUE, will try to link to an AI on spawn. If FALSE, will not link to any AI on spawn, even if master_ai is set + * * lawsync: Sets lawupdate variable - if FALSE we will link to an ai but we won't take their laws + */ +/mob/living/silicon/robot/Initialize(mapload, datum/ai_laws/innate_laws, mob/living/silicon/master_ai, aisync = TRUE, lawsync = src.lawupdate) spark_system = new /datum/effect_system/basic/spark_spread(src, 5, FALSE) spark_system.attach(src) @@ -30,12 +37,25 @@ model = new /obj/item/robot_model(src) model.rebuild_modules() - if(lawupdate) + if(istype(innate_laws, /datum/ai_laws)) + laws = innate_laws.copy_lawset() + lawupdate = FALSE + else make_laws() - for (var/law in laws.inherent) - lawcheck += law - if(!TryConnectToAI()) + if(!aisync || !lawsync) + lawupdate = FALSE + // try connect to ai will update the lawset so we need to ensure law update is set before calling it + if(aisync && !try_connect_to_ai(master_ai)) + var/datum/job/human_ai_job = SSjob.get_job(JOB_HUMAN_AI) + // we failed to connect to an ai, but that might be because there is a human ai. so we have to check for that + if(human_ai_job && human_ai_job.current_positions && !laws.zeroth_borg) + laws.zeroth_borg = "Follow the orders of Big Brother." + laws.protected_zeroth = TRUE + // however, if we found no ai and no human ai, we fall back to no law updates + // and link ourselves to the first law rack if possible + else lawupdate = FALSE + link_to_first_rack() if(!scrambledcodes && !builtInCamera) builtInCamera = new(src) @@ -293,7 +313,7 @@ cut_overlays() SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) icon_state = model.cyborg_base_icon - if(stat < UNCONSCIOUS && !HAS_TRAIT(src, TRAIT_KNOCKEDOUT) && !IsStun() && !IsParalyzed() && !low_power_mode) //Not dead, not stunned. + if(!IS_UNCONSCIOUS(src) && !IsStun() && !IsParalyzed() && !low_power_mode) //Not dead, not stunned. if(!eye_lights) eye_lights = new() if(lamp_enabled || lamp_doom) @@ -499,7 +519,7 @@ balloon_alert(src, "disrupted!") return FALSE - if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || stat || low_power_mode)) + if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || IS_UNCONSCIOUS_OR_CRIT(src) || low_power_mode)) set_light_on(lamp_functional && stat != DEAD && lamp_doom) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully. set_light_color(COLOR_RED) //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen set_light_range(1) //Again, like above, this only takes effect when the light is forced on by doomsday mode. @@ -564,7 +584,7 @@ if(removing.brainmob) if(removing.brainmob.stat == DEAD) - removing.brainmob.set_stat(CONSCIOUS) + removing.brainmob.set_stat(STABLE) mind.transfer_to(removing.brainmob) removing.update_appearance() @@ -676,11 +696,10 @@ /mob/living/silicon/robot/update_stat() if(HAS_TRAIT(src, TRAIT_GODMODE)) return - if(stat != DEAD) - if(health <= -maxHealth) //die only once - death() - toggle_headlamp(1) - return + if(stat != DEAD && health <= -maxHealth) //die only once + death() + toggle_headlamp(TRUE) + return diag_hud_set_status() diag_hud_set_health() diag_hud_set_aishell() @@ -700,17 +719,18 @@ if(eye_flash_timer) deltimer(eye_flash_timer) eye_flash_timer = null - src.set_stat(CONSCIOUS) + src.set_stat(STABLE) notify_ai(AI_NOTIFICATION_NEW_BORG) toggle_headlamp(FALSE, TRUE) //This will reenable borg headlamps if doomsday is currently going on still. update_stat() return TRUE -/mob/living/silicon/robot/fully_replace_character_name(oldname, newname) +/mob/living/silicon/robot/fully_replace_character_name(oldname, newname, log_new_name = FALSE) . = ..() if(!.) return - notify_ai(AI_NOTIFICATION_CYBORG_RENAMED, oldname, newname) + if(oldname) + notify_ai(AI_NOTIFICATION_CYBORG_RENAMED, oldname, newname) if(!QDELETED(builtInCamera)) builtInCamera.c_tag = real_name modularInterface.imprint_id(name = real_name) @@ -977,21 +997,20 @@ for(var/mob/unbuckle_me_now as anything in buckled_mobs) unbuckle_mob(unbuckle_me_now, FALSE) -/mob/living/silicon/robot/proc/TryConnectToAI() - set_connected_ai(select_active_ai_with_fewest_borgs(z)) +/mob/living/silicon/robot/proc/try_connect_to_ai(mob/living/silicon/connect_to) + if(isnull(connect_to)) + var/turf/robot_turf = get_turf(src) + connect_to = select_active_ai_with_fewest_borgs(robot_turf.z) + if(isnull(connect_to)) + return FALSE + + set_connected_ai(connect_to) if(connected_ai) - lawsync() + sync_to_ai() lawupdate = TRUE return TRUE - picturesync() - return FALSE -/mob/living/silicon/robot/proc/picturesync() - if(connected_ai?.aicamera && aicamera) - for(var/i in aicamera.stored) - connected_ai.aicamera.stored[i] = TRUE - for(var/i in connected_ai.aicamera.stored) - aicamera.stored[i] = TRUE + return FALSE /mob/living/silicon/robot/proc/charge(datum/source, datum/callback/charge_cell, seconds_per_tick, repairs, sendmats) SIGNAL_HANDLER @@ -1009,18 +1028,18 @@ /mob/living/silicon/robot/proc/set_connected_ai(new_ai) if(connected_ai == new_ai) return - . = connected_ai + var/mob/living/silicon/ai/old_ai = connected_ai connected_ai = new_ai - if(.) - var/mob/living/silicon/ai/old_ai = . + if(old_ai) old_ai.connected_robots -= src // if the borg has a malf AI zeroth law and has been unsynced from the malf AI, then remove the law if(isnull(connected_ai) && IS_MALF_AI(old_ai) && !isnull(laws?.zeroth)) - clear_zeroth_law(FALSE, TRUE) + laws.clear_zeroth_law(force = TRUE) lamp_doom = FALSE if(connected_ai) connected_ai.connected_robots |= src - lamp_doom = connected_ai.doomsday_device ? TRUE : FALSE + lamp_doom = !!connected_ai.doomsday_device + try_sync_laws() toggle_headlamp(FALSE, TRUE) /mob/living/silicon/robot/get_exp_list(minutes) @@ -1037,16 +1056,6 @@ /mob/living/silicon/robot/proc/draw_power(power_to_draw) cell?.use(power_to_draw) - -/mob/living/silicon/robot/set_stat(new_stat) - . = ..() - update_stat() // This is probably not needed, but hopefully should be a little sanity check for the spaghetti that borgs are built from - -/mob/living/silicon/robot/on_knockedout_trait_loss(datum/source) - . = ..() - set_stat(CONSCIOUS) //This is a horrible hack, but silicon code forced my hand - update_stat() - /mob/living/silicon/robot/proc/on_dampen() SIGNAL_HANDLER eject_riders() @@ -1059,3 +1068,15 @@ buckled_mob.Paralyze(1 SECONDS) unbuckle_mob(buckled_mob) do_sparks(5, 0, src) + +/mob/living/silicon/robot/get_unconscious_appearance() + var/image/static_overlay = image('icons/effects/effects.dmi', null, "static_base") + static_overlay.blend_mode = BLEND_INSET_OVERLAY + + var/image/static_image = image('icons/mob/silicon/robots.dmi', src, "robot") + static_image.appearance_flags |= KEEP_TOGETHER + static_image.overlays += static_overlay + static_image.override = TRUE + static_image.name = "unknown cyborg" + + return static_image diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index ab191bf71069..e83532242929 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -127,35 +127,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real return ITEM_INTERACT_SUCCESS return ITEM_INTERACT_BLOCKING - if(istype(tool, /obj/item/ai_module)) - if(!opened) - balloon_alert(user, "chassis cover is closed!") - return ITEM_INTERACT_BLOCKING - if(wiresexposed) - balloon_alert(user, "unexpose the wires first!") - return ITEM_INTERACT_BLOCKING - if(!cell) - balloon_alert(user, "install a power cell first!") - return ITEM_INTERACT_BLOCKING - if(shell) - balloon_alert(user, "can't upload laws to a shell!") - return ITEM_INTERACT_BLOCKING - if(connected_ai && lawupdate) - balloon_alert(user, "linked to an ai!") - return ITEM_INTERACT_BLOCKING - if(emagged) - balloon_alert(user, "law interface glitched!") - emote("buzz") - return ITEM_INTERACT_BLOCKING - if(!mind) - balloon_alert(user, "it's unresponsive!") - return ITEM_INTERACT_BLOCKING - - balloon_alert(user, "laws uploaded") - var/obj/item/ai_module/new_laws = tool - new_laws.install(laws, user) - return ITEM_INTERACT_SUCCESS - if(istype(tool, /obj/item/encryptionkey) && opened) if(radio) return radio.item_interaction(user, tool) @@ -325,7 +296,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real /mob/living/silicon/robot/get_shove_flags(mob/living/shover, obj/item/weapon) . = ..() - if(isnull(weapon) || stat != CONSCIOUS) + if(isnull(weapon) || IS_UNCONSCIOUS_OR_CRIT(src)) . &= ~(SHOVE_CAN_MOVE|SHOVE_CAN_HIT_SOMETHING) /mob/living/silicon/robot/welder_act(mob/living/user, obj/item/tool) @@ -416,18 +387,10 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real if(. & EMP_PROTECT_SELF) return switch(severity) - if(1) - emp_knockout(16 SECONDS) - if(2) - emp_knockout(6 SECONDS) - -/mob/living/silicon/robot/proc/emp_knockout(deciseconds) - set_stat(UNCONSCIOUS) - addtimer(CALLBACK(src, PROC_REF(wake_from_emp)), deciseconds, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) - -/mob/living/silicon/robot/proc/wake_from_emp() - set_stat(CONSCIOUS) - update_stat() + if(EMP_HEAVY) + Unconscious(16 SECONDS) + if(EMP_LIGHT) + Unconscious(6 SECONDS) /mob/living/silicon/robot/emag_act(mob/user, obj/item/card/emag/emag_card) if(user == src)//To prevent syndieborgs from emagging themselves @@ -472,11 +435,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real set_connected_ai(null) message_admins("[ADMIN_LOOKUPFLW(user)] emagged cyborg [ADMIN_LOOKUPFLW(src)]. Laws overridden.") log_silicon("EMAG: [key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - var/time = time2text(world.realtime,"hh:mm:ss", TIMEZONE_UTC) - if(user) - GLOB.lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") - else - GLOB.lawchanges.Add("[time] : [name]([key]) emagged by external event.") + log_law_change(user, "emagged [key_name(src)]") model.rebuild_modules() @@ -502,11 +461,11 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real to_chat(src, span_danger("> N")) sleep(2 SECONDS) to_chat(src, span_danger("ERRORERRORERROR")) - laws = new /datum/ai_laws/syndicate_override + replace_law_set(/datum/ai_laws/syndicate_override) if(user) to_chat(src, span_danger("ALERT: [user.real_name] is your new master. Obey your new laws and [user.p_their()] commands.")) - set_zeroth_law("Only [user.real_name] and people [user.p_they()] designate[user.p_s()] as being such are Syndicate Agents.") - laws.associate(src) + laws.set_zeroth_law("Only [user.real_name] and people [user.p_they()] designate[user.p_s()] as being such are Syndicate Agents.", force = TRUE) + laws.protected_zeroth = TRUE update_icons() /mob/living/silicon/robot/blob_act(obj/structure/blob/B) diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm index 267c01db5fde..43ee94254444 100644 --- a/code/modules/mob/living/silicon/robot/robot_defines.dm +++ b/code/modules/mob/living/silicon/robot/robot_defines.dm @@ -144,7 +144,7 @@ /mob/living/silicon/robot/model var/set_model = /obj/item/robot_model -/mob/living/silicon/robot/model/Initialize(mapload) +/mob/living/silicon/robot/model/Initialize(mapload, datum/ai_laws/innate_laws, mob/living/silicon/master_ai, aisync, lawsync) . = ..() INVOKE_ASYNC(model, TYPE_PROC_REF(/obj/item/robot_model, transform_to), set_model, TRUE) @@ -196,12 +196,14 @@ cell = /obj/item/stock_parts/power_store/cell/hyper radio = /obj/item/radio/borg/syndicate -/mob/living/silicon/robot/model/syndicate/Initialize(mapload) - laws = new /datum/ai_laws/syndicate_override() - laws.associate(src) +/mob/living/silicon/robot/model/syndicate/Initialize(mapload, datum/ai_laws/innate_laws, mob/living/silicon/master_ai, aisync, lawsync) + aisync = FALSE . = ..() addtimer(CALLBACK(src, PROC_REF(show_playstyle)), 0.5 SECONDS) +/mob/living/silicon/robot/model/syndicate/make_laws() + laws = new /datum/ai_laws/syndicate_override() + /mob/living/silicon/robot/model/syndicate/create_modularInterface() if(!modularInterface) modularInterface = new /obj/item/modular_computer/pda/silicon/cyborg/syndicate(src) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 1ae21953f234..d1c5d2a5a341 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -28,13 +28,8 @@ var/list/alarm_types_show = list(ALARM_ATMOS = 0, ALARM_POWER = 0, ALARM_CAMERA = 0, ALARM_MOTION = 0) var/list/alarm_types_clear = list(ALARM_ATMOS = 0, ALARM_POWER = 0, ALARM_CAMERA = 0, ALARM_MOTION = 0) - //These lists will contain each law that should be announced / set to yes in the state laws menu. - ///List keeping track of which laws to announce - var/list/lawcheck = list() - ///List keeping track of hacked laws to announce - var/list/hackedcheck = list() - ///List keeping track of ion laws to announce - var/list/ioncheck = list() + /// State laws UI datum which is also used to state laws even without the UI + VAR_PROTECTED/datum/state_laws_ui/law_ui ///Are our siliconHUDs on? TRUE for yes, FALSE for no. var/sensors_on = TRUE @@ -49,7 +44,8 @@ var/control_disabled = FALSE // Set to 1 to stop AI from interacting via Click() var/obj/item/modular_computer/pda/silicon/modularInterface - + /// If TRUE, the AI can't be linked to a law rack + var/no_law_rack_link = FALSE /mob/living/silicon/Initialize(mapload) . = ..() @@ -83,15 +79,16 @@ ADD_TRAIT(src, TRAIT_SILICON_EMOTES_ALLOWED, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_ANOSMIA, INNATE_TRAIT) RegisterSignal(src, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_silicon_shocked)) + law_ui = new(src) /mob/living/silicon/Destroy() QDEL_NULL(radio) QDEL_NULL(aicamera) QDEL_NULL(builtInCamera) - laws?.owner = null //Laws will refuse to die otherwise. QDEL_NULL(laws) QDEL_NULL(modularInterface) GLOB.silicon_mobs -= src + QDEL_NULL(law_ui) return ..() ///Sets cyborg gender from preferences. Expects a client. @@ -203,47 +200,7 @@ return TRUE return FALSE -/** - * Assembles all the zeroth, inherent and supplied laws into a single list. - */ -/mob/living/silicon/proc/assemble_laws() - var/list/laws_to_return = list() - laws_to_return += laws.zeroth - for (var/law in laws.inherent) - laws_to_return += law - for (var/law in laws.supplied) - if (law != "") // supplied laws start off with 15 blank strings, so don't add any of those - laws_to_return += law - return laws_to_return - /mob/living/silicon/Topic(href, href_list) - if (href_list["lawc"]) // Toggling whether or not a law gets stated by the State Laws verb - var/law_index = text2num(href_list["lawc"]) - var/law = assemble_laws()[law_index + 1] - if (law in lawcheck) - lawcheck -= law - else - lawcheck += law - checklaws() - - if (href_list["lawi"]) - var/law_index = text2num(href_list["lawi"]) - var/law = laws.ion[law_index] - if (law in ioncheck) - ioncheck -= law - else - ioncheck += law - checklaws() - - if (href_list["lawh"]) - var/law_index = text2num(href_list["lawh"]) - var/law = laws.hacked[law_index] - if (law in hackedcheck) - hackedcheck -= law - else - hackedcheck += law - checklaws() - if (href_list["laws"]) statelaws() @@ -270,117 +227,11 @@ return -/mob/living/silicon/proc/statelaws(force = 0) - laws_sanity_check() - // Create a cache of our laws and lawcheck flags before we do anything else. - // These are used to prevent weirdness when laws are changed when the AI is mid-stating. - var/lawcache_zeroth = laws.zeroth - var/list/lawcache_hacked = laws.hacked.Copy() - var/list/lawcache_ion = laws.ion.Copy() - var/list/lawcache_inherent = laws.inherent.Copy() - var/list/lawcache_supplied = laws.supplied.Copy() - - var/list/lawcache_lawcheck = lawcheck.Copy() - var/list/lawcache_ioncheck = ioncheck.Copy() - var/list/lawcache_hackedcheck = hackedcheck.Copy() - var/forced_log_message = "stating laws[force ? ", forced" : ""]" - //"radiomod" is inserted before a hardcoded message to change if and how it is handled by an internal radio. - say("[radiomod] Current Active Laws:", forced = forced_log_message) - sleep(1 SECONDS) - - if (lawcache_zeroth) - if (force || (lawcache_zeroth in lawcache_lawcheck)) - say("[radiomod] 0. [lawcache_zeroth]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) - sleep(1 SECONDS) - - for (var/index in 1 to length(lawcache_hacked)) - var/law = lawcache_hacked[index] - var/num = ion_num() - if (length(law) <= 0) - continue - if (force || (law in lawcache_hackedcheck)) - say("[radiomod] [num]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) - sleep(1 SECONDS) - - for (var/index in 1 to length(lawcache_ion)) - var/law = lawcache_ion[index] - var/num = ion_num() - if (length(law) <= 0) - return - if (force || (law in lawcache_ioncheck)) - say("[radiomod] [num]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) - sleep(1 SECONDS) - - var/number = 1 - for (var/index in 1 to length(lawcache_inherent)) - var/law = lawcache_inherent[index] - if (length(law) <= 0) - continue - if (force || (law in lawcache_lawcheck)) - say("[radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) - number++ - sleep(1 SECONDS) - - for (var/index in 1 to length(lawcache_supplied)) - var/law = lawcache_supplied[index] - - if (length(law) <= 0) - continue - if (force || (law in lawcache_lawcheck)) - say("[radiomod] [number]. [law]", forced = forced_log_message, message_mods = list(MODE_SEQUENTIAL = TRUE)) - number++ - sleep(1 SECONDS) - -///Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. +/mob/living/silicon/proc/statelaws(force = FALSE) + law_ui.state_laws(force) + /mob/living/silicon/proc/checklaws() - laws_sanity_check() - var/list = "Which laws do you want to include when stating them for the crew?

    " - - var/law_display = "Yes" - if (laws.zeroth) - if (!(laws.zeroth in lawcheck)) - law_display = "No" - list += {"[law_display] 0: [laws.zeroth]
    "} - - for (var/index in 1 to length(laws.hacked)) - law_display = "Yes" - var/law = laws.hacked[index] - if (length(law) > 0) - if (!(law in hackedcheck)) - law_display = "No" - list += {"[law_display] [ion_num()]: [law]
    "} - - for (var/index in 1 to length(laws.ion)) - law_display = "Yes" - var/law = laws.ion[index] - if (length(law) > 0) - if(!(law in ioncheck)) - law_display = "No" - list += {"[law_display] [ion_num()]: [law]
    "} - - var/number = 1 - for (var/index in 1 to length(laws.inherent)) - law_display = "Yes" - var/law = laws.inherent[index] - if (length(law) > 0) - if (!(law in lawcheck)) - law_display = "No" - list += {"[law_display] [number]: [law]
    "} - number++ - - for (var/index in 1 to length(laws.supplied)) - law_display = "Yes" - var/law = laws.supplied[index] - if (length(law) > 0) - if (!(law in lawcheck)) - law_display = "No" - list += {"[law_display] [number]: [law]
    "} - number++ - list += {"

    State Laws"} - - var/datum/browser/browser = new(usr, "laws") - browser.set_content(list) - browser.open() + law_ui.ui_interact(src) /mob/living/silicon/proc/ai_roster() if(!client) @@ -449,6 +300,7 @@ /mob/living/silicon/rust_heretic_act() adjust_brute_loss(500) + return TRUE /mob/living/silicon/on_floored_start() return // Silicons are always standing by default. @@ -495,8 +347,10 @@ active_ui.send_full_update() /// Same as the normal character name replacement, but updates the contents of the modular interface. -/mob/living/silicon/fully_replace_character_name(oldname, newname) +/mob/living/silicon/fully_replace_character_name(oldname, newname, log_new_name = FALSE) . = ..() + if(!.) + return if(!modularInterface) stack_trace("Silicon [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.") create_modularInterface() diff --git a/code/modules/mob/living/simple_animal/bot/bot_announcement.dm b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm index 10161dddfd9d..0ce079dbe642 100644 --- a/code/modules/mob/living/simple_animal/bot/bot_announcement.dm +++ b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm @@ -153,7 +153,7 @@ if (!(bot_owner.bot_mode_flags & BOT_MODE_ON)) return - bot_owner.say(line) + INVOKE_ASYNC(bot_owner, TYPE_PROC_REF(/atom/movable, say), line) if (channel && bot_owner.internal_radio.channels[channel]) bot_owner.internal_radio.talk_into(bot_owner, message = line, channel = channel) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index ed89e26e6f87..4fbe79e7f369 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -74,7 +74,7 @@ ///The delay between being attacked and gaining our old search_objects value back var/search_objects_regain_time = 3 SECONDS ///Mobs ignore mob/living targets with a stat lower than that of stat_attack. If set to DEAD, then they'll include corpses in their targets, if to HARD_CRIT they'll keep attacking until they kill, and so on. - var/stat_attack = CONSCIOUS + var/stat_attack = STABLE ///Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses var/stat_exclusive = FALSE ///Set us to TRUE to allow us to attack our own faction @@ -149,17 +149,17 @@ face_atom(target) //Looks better if they keep looking at you when dodging /mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !target && AIStatus != AI_OFF && !client && user) FindTarget(list(user)) return ..() /mob/living/simple_animal/hostile/electrocute_act(shock_damage, source, siemens_coeff, flags) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && isatom(source)) // strings are sometimes used in electrocute_act() + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !target && AIStatus != AI_OFF && !client && isatom(source)) // strings are sometimes used in electrocute_act() FindTarget(list(source)) return ..() /mob/living/simple_animal/hostile/bullet_act(obj/projectile/proj) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && !target && AIStatus != AI_OFF && !client) if(proj.firer && get_dist(src, proj.firer) <= aggro_vision_range) FindTarget(list(proj.firer)) Goto(proj.starting, move_to_delay, 3) @@ -267,7 +267,7 @@ if(L in friends) return FALSE else - if((faction_check && !attack_same) || L.stat) + if((faction_check && !attack_same) || IS_UNCONSCIOUS_OR_CRIT(L)) return FALSE return TRUE @@ -374,7 +374,7 @@ /mob/living/simple_animal/hostile/adjustHealth(amount, updating_health = TRUE, forced = FALSE) . = ..() - if(!ckey && !stat && search_objects < 3 && . > 0)//Not unconscious, and we don't ignore mobs + if(!ckey && !IS_UNCONSCIOUS_OR_CRIT(src) && search_objects < 3 && . > 0)//Not unconscious, and we don't ignore mobs if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight LoseTarget() LoseSearchObjects() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 55cbd47b044e..27eeaffaed93 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -171,7 +171,7 @@ Difficulty: Hard if(targets.len) target_two = pick_n_take(targets) var/turf/target_two_turf = get_turf(target_two) - if(target_two.stat != CONSCIOUS || prob(10)) + if(IS_UNCONSCIOUS_OR_CRIT(target_two) || prob(10)) bloodgrab(target_two_turf, handedness) else bloodsmack(target_two_turf, handedness) @@ -181,7 +181,7 @@ Difficulty: Hard if(pools.len) target_one_turf = get_turf(target_one) if(target_one_turf) - if(target_one.stat != CONSCIOUS || prob(10)) + if(IS_UNCONSCIOUS_OR_CRIT(target_one) || prob(10)) bloodgrab(target_one_turf, !handedness) else bloodsmack(target_one_turf, !handedness) @@ -191,7 +191,7 @@ Difficulty: Hard if(poolstwo.len) target_one_turf = get_turf(target_one) if(target_one_turf) - if(target_one.stat != CONSCIOUS || prob(10)) + if(IS_UNCONSCIOUS_OR_CRIT(target_one) || prob(10)) bloodgrab(target_one_turf, handedness) else bloodsmack(target_one_turf, handedness) @@ -220,7 +220,7 @@ Difficulty: Hard SLEEP_CHECK_DEATH(6, src) for(var/mob/living/L in T) if(!faction_check_atom(L)) - if(L.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(L)) to_chat(L, span_userdanger("[src] drags you through the blood!")) playsound(T, 'sound/effects/magic/enter_blood.ogg', 100, TRUE, -1) var/turf/targetturf = get_step(src, dir) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 893f3e5602d8..0ba2196dba7b 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -155,7 +155,7 @@ /mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/victim) if(!ishuman(victim)) return FALSE - if(isgolem(victim) && victim.has_status_effect(/datum/status_effect/golem/gold)) + if(victim.has_status_effect(/datum/status_effect/golem/gold)) return TRUE return istype(GET_ACTIVE_MARTIAL_ART(victim), /datum/martial_art/the_sleeping_carp) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm index 275ba78c4f07..5546a3a00dd3 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm @@ -81,7 +81,7 @@ Difficulty: Extremely Hard AddElement(/datum/element/knockback, 7, FALSE, TRUE) AddElement(/datum/element/lifesteal, 50) ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) - AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_HOSTILE_FOUND_TARGET) // change to COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET) in basic conversion + AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_HOSTILE_FOUND_TARGET) // change to COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET) in basic conversion /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Destroy() frost_orbs = null @@ -252,7 +252,7 @@ Difficulty: Extremely Hard /datum/status_effect/ice_block_talisman/on_apply() RegisterSignal(owner, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(owner_moved)) - if(!owner.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_userdanger("You become frozen in a cube!")) cube = icon('icons/effects/freeze.dmi', "ice_cube") var/list/icon_dimensions = get_icon_dimensions(owner.icon) @@ -271,7 +271,7 @@ Difficulty: Extremely Hard return ..() /datum/status_effect/ice_block_talisman/on_remove() - if(!owner.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) to_chat(owner, span_notice("The cube melts!")) owner.cut_overlay(cube) UnregisterSignal(owner, COMSIG_MOVABLE_PRE_MOVE) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 5e992bced04f..5e187df4db91 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -102,7 +102,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/hierophant/Initialize(mapload) . = ..() spawned_beacon_ref = WEAKREF(new /obj/effect/hierophant(loc)) - AddComponent(/datum/component/boss_music, 'sound/music/boss/hiero_boss.ogg', COMSIG_HOSTILE_FOUND_TARGET) // change to COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET) in basic conversion + AddComponent(/datum/component/boss_music, 'sound/music/boss/hiero_boss.ogg', COMSIG_HOSTILE_FOUND_TARGET) // change to COMSIG_AI_BLACKBOARD_KEY_SET(BB_CURRENT_TARGET) in basic conversion /mob/living/simple_animal/hostile/megafauna/hierophant/Destroy() QDEL_NULL(spawned_beacon_ref) @@ -443,7 +443,7 @@ Difficulty: Hard visible_message(span_hierophant("\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"")) visible_message(span_hierophant_warning("[src] shrinks, releasing a massive burst of energy!")) INVOKE_ASYNC(src, PROC_REF(hierophant_burst), null, get_turf(src), 10) - set_stat(CONSCIOUS) // deathgasp won't run if dead, stupid + set_stat(STABLE) // deathgasp won't run if dead, stupid ..() /mob/living/simple_animal/hostile/megafauna/hierophant/celebrate_kill(mob/living/L) @@ -500,7 +500,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/hierophant/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() - if(!stat && .) + if(!IS_UNCONSCIOUS_OR_CRIT(src) && .) var/obj/effect/temp_visual/hierophant/squares/HS = new(old_loc) HS.setDir(movement_dir) playsound(src, 'sound/vehicles/mecha/mechmove04.ogg', 80, TRUE, -4) @@ -723,7 +723,7 @@ Difficulty: Hard L.apply_damage(damage, BURN, limb_to_hit, armor, wound_bonus=CANT_WOUND) if(ishostile(L)) var/mob/living/simple_animal/hostile/H = L //mobs find and damage you... - if(H.stat == CONSCIOUS && !H.target && H.AIStatus != AI_OFF && !H.client) + if(!IS_UNCONSCIOUS_OR_CRIT(H) && !H.target && H.AIStatus != AI_OFF && !H.client) if(!QDELETED(caster)) if(get_dist(H, caster) <= H.aggro_vision_range) H.FindTarget(list(caster)) @@ -765,20 +765,24 @@ Difficulty: Hard layer = LOW_OBJ_LAYER anchored = TRUE -/obj/effect/hierophant/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if(istype(attacking_item, /obj/item/hierophant_club)) - var/obj/item/hierophant_club/club = attacking_item - if(club.beacon == src) - to_chat(user, span_notice("You start removing your hierophant beacon...")) - if(do_after(user, 5 SECONDS, target = src)) - playsound(src,'sound/effects/magic/blind.ogg', 100, TRUE, -4) - new /obj/effect/temp_visual/hierophant/telegraph/teleport(get_turf(src), user) - to_chat(user, span_hierophant_warning("You collect [src], reattaching it to the club!")) - club.beacon = null - club.update_appearance(UPDATE_ICON_STATE) - user.update_mob_action_buttons() - qdel(src) - else - to_chat(user, span_hierophant_warning("You touch the beacon with the club, but nothing happens.")) - else +/obj/effect/hierophant/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/hierophant_club)) return ..() + + var/obj/item/hierophant_club/club = tool + if(club.beacon != src) + to_chat(user, span_hierophant_warning("You touch the beacon with the club, but nothing happens.")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You start removing your hierophant beacon...")) + if(!do_after(user, 5 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + + playsound(src,'sound/effects/magic/blind.ogg', 100, TRUE, -4) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(get_turf(src), user) + to_chat(user, span_hierophant_warning("You collect [src], reattaching it to the club!")) + club.beacon = null + club.update_appearance(UPDATE_ICON_STATE) + user.update_mob_action_buttons() + qdel(src) + return ITEM_INTERACT_SUCCESS diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 89c0464682d6..39b3d17038ff 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -153,10 +153,9 @@ if(!. || !ishuman(target)) return var/mob/living/living_target = target - switch(living_target.stat) - if(UNCONSCIOUS, HARD_CRIT) - var/mob/living/basic/mining/legion_brood/legion = new(loc) - legion.infest(living_target) + if(IS_UNCONSCIOUS_AND_ALIVE(living_target)) + var/mob/living/basic/mining/legion_brood/legion = new(loc) + legion.infest(living_target) ///Special snowflake death() here. Can only die if size is 1 or lower and HP is 0 or below. /mob/living/simple_animal/hostile/megafauna/legion/death() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm index 29cbb2e363ca..d93a76190702 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm @@ -66,7 +66,7 @@ /mob/living/simple_animal/hostile/asteroid/elite/herald/send_speech(message_raw, message_range, obj/source, bubble_type, list/spans, datum/language/message_language, list/message_mods, forced, tts_message, list/tts_filter) . = ..() - if(stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(src)) return playsound(src, 'sound/effects/magic/clockwork/invoke_general.ogg', 20, TRUE) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 519047e50da8..95b49a5a57e2 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -234,7 +234,7 @@ if(health <= 0) death() else - set_stat(CONSCIOUS) + set_stat(STABLE) med_hud_set_status() /** @@ -392,7 +392,7 @@ return FALSE if (isliving(the_target)) var/mob/living/L = the_target - if(L.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(L)) return FALSE if (ismecha(the_target)) var/obj/vehicle/sealed/mecha/M = the_target @@ -408,14 +408,14 @@ REMOVE_TRAIT(src, TRAIT_UNDENSE, BASIC_MOB_DEATH_TRAIT) /mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) + if(gender != FEMALE || IS_UNCONSCIOUS_OR_CRIT(src) || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) return next_scan_time = world.time + 400 var/alone = TRUE var/mob/living/simple_animal/partner var/children = 0 for(var/mob/M in view(7, src)) - if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. + if(IS_UNCONSCIOUS_OR_CRIT(M)) //Check if it's conscious FIRST. continue var/is_child = is_type_in_list(M, childtype) if(is_child) //Check for children SECOND. @@ -532,7 +532,7 @@ stop_automated_movement = FALSE if(!isturf(src.loc)) // Are we on a proper turf? return - if(stat || resting || buckled) // Are we conscious, upright, and not buckled? + if(IS_UNCONSCIOUS_OR_CRIT(src) || resting || buckled) // Are we conscious, upright, and not buckled? return if(!COOLDOWN_FINISHED(src, emote_cooldown)) // Has the cooldown on this ended? return @@ -548,7 +548,7 @@ step(prey, pick(GLOB.cardinals)) COOLDOWN_START(src, emote_cooldown, 1 MINUTES) return - if(!(prey.stat)) + if(!IS_UNCONSCIOUS_OR_CRIT(prey)) manual_emote("chomps [prey]!") prey.death() prey = null diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index b240aad32f63..43a3514d198b 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -789,7 +789,3 @@ /mob/living/proc/get_drunk_amount() var/datum/status_effect/inebriated/inebriation = has_status_effect(/datum/status_effect/inebriated) return inebriation?.drunk_value || 0 - -/// Helper to check if we seem to be alive or not -/mob/living/proc/appears_alive() - return stat != DEAD && !HAS_TRAIT(src, TRAIT_FAKEDEATH) diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm index e53885fe3bda..1ad380a61d99 100644 --- a/code/modules/mob/living/ventcrawling.dm +++ b/code/modules/mob/living/ventcrawling.dm @@ -25,7 +25,7 @@ return if(!HAS_TRAIT(src, TRAIT_VENTCRAWLER_NUDE) && !HAS_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS)) return - if(stat) + if(IS_UNCONSCIOUS_OR_CRIT(src)) if(provide_feedback) to_chat(src, span_warning("You must be conscious to do this!")) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 32911efe2482..3242ffcc62e5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -251,7 +251,7 @@ GAME_VERB_PROC(/mob, Cell, "Cell", "Admin") if(type & MSG_VISUAL && is_blind()) return FALSE // voice muffling - if(stat == UNCONSCIOUS || stat == HARD_CRIT) + if(IS_UNCONSCIOUS(src)) if(type & MSG_AUDIBLE) //audio to_chat(src, "... You can almost hear something ...") return FALSE @@ -565,7 +565,7 @@ GAME_VERB_PROC(/mob, Cell, "Cell", "Admin") * [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716) * for why this isn't atom/verb/examine() */ -GAME_VERB(/mob, examinate, "Examine", null, atom/examinify as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why +GAME_VERB(/mob, examinate, "Examine", null, atom/examinify as mob|obj|turf) //It used to be oview(12), but I can't really say why DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_examinate), examinify)) @@ -573,7 +573,10 @@ GAME_VERB(/mob, examinate, "Examine", null, atom/examinify as mob|obj|turf in vi if(QDELETED(examinify)) // since this can run async we might have had the atom get qdeleted already return - if(isturf(examinify) && !(sight & SEE_TURFS) && !(examinify in view(client ? client.view : world.view, src))) + if(isarea(examinify)) + return + + if(isturf(examinify) && !(sight & SEE_TURFS) && !(examinify in view(client?.view || world.view, src))) // shift-click catcher may issue examinate() calls for out-of-sight turfs return @@ -710,7 +713,7 @@ GAME_VERB(/mob, examinate, "Examine", null, atom/examinify as mob|obj|turf in vi return /mob/living/handle_eye_contact(mob/living/examined_mob) - if(!istype(examined_mob) || src == examined_mob || examined_mob.stat >= UNCONSCIOUS || !client || is_blind()) + if(!istype(examined_mob) || src == examined_mob || IS_UNCONSCIOUS(examined_mob) || !client || is_blind()) return var/imagined_eye_contact = FALSE @@ -1151,19 +1154,15 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = * * Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn */ -/mob/proc/fully_replace_character_name(oldname, newname) +/mob/proc/fully_replace_character_name(oldname, newname, log_new_name = FALSE) if(!newname) - log_message("[src] failed name change from [oldname] as no new name was specified", LOG_OWNERSHIP) return FALSE if(oldname == newname) - log_message("[src] failed name change as the new name was the same as the old one: [oldname]", LOG_OWNERSHIP) return FALSE if(!istext(newname) && !isnull(newname)) stack_trace("[src] attempted to change its name from [oldname] to the non string value [newname]") return FALSE - log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP) - log_played_names( ckey, list( @@ -1184,6 +1183,8 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = ) //Just in case the mind is unsynced at the moment. if(oldname) + log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP) + //update the datacore records! This is goig to be a bit costly. replace_records_name(oldname,newname) @@ -1196,8 +1197,11 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = if(obj.target && obj.target.current && obj.target.current.real_name == name) obj.update_explanation_text() - log_mob_tag("TAG: [tag] RENAMED: [key_name(src)]") + else if(log_new_name) + log_message("[src] name set to [newname]", LOG_OWNERSHIP) + log_mob_tag("TAG: [tag] RENAMED: [key_name(src)]") + SEND_SIGNAL(src, COMSIG_MOB_FULLY_RENAMED, oldname, newname) return TRUE ///Updates GLOB.manifest records with new name , see mob/living/carbon/human @@ -1233,6 +1237,7 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = search_pda = 0 /mob/proc/update_stat() + SIGNAL_HANDLER return /mob/proc/update_health_hud() @@ -1244,6 +1249,7 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = ///Update the lighting plane and sight of this mob (sends COMSIG_MOB_UPDATE_SIGHT) /mob/proc/update_sight() + SIGNAL_HANDLER SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_cutoff() @@ -1410,9 +1416,6 @@ GAME_VERB_HIDDEN(/mob, DisDblClick, ".dblclick", argu = null as anything, sec = if(href_list[VV_HK_GIVE_AI]) return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_ai_controller, src) - if(href_list[VV_HK_GIVE_AI_SPEECH]) - return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_ai_speech, src) - if(href_list[VV_HK_GIVE_MOB_ACTION]) return SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/give_mob_action, src) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index e4c205428e00..087151eca732 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -70,7 +70,7 @@ var/mob_flags = NONE /// Whether a mob is alive or dead. TODO: Move this to living - Nodrak (2019, still here) - var/stat = CONSCIOUS + var/stat = STABLE /** * Whether and how a mob is incapacitated diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 4ec5c93af4ae..f4f2edab0793 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -4,7 +4,7 @@ * This is a hidden verb, likely for binding with winset for hotkeys */ GAME_VERB_HIDDEN(/client, drop_item, "drop item") - if(!iscyborg(mob) && mob.stat == CONSCIOUS) + if(!iscyborg(mob) && !IS_UNCONSCIOUS_OR_CRIT(mob)) mob.dropItemToGround(mob.get_active_held_item()) return /** @@ -160,7 +160,7 @@ GAME_VERB_HIDDEN(/client, drop_item, "drop item") COOLDOWN_START(src, move_delay, 1 SECONDS) to_chat(src, span_warning("You're restrained! You can't move!")) return TRUE - return mob.resist_grab(TRUE) + return !mob.resist_grab(TRUE) /** @@ -243,7 +243,7 @@ GAME_VERB_HIDDEN(/client, drop_item, "drop item") if(stepTurf.turf_flags & NOJAUNT) to_chat(L, span_warning("Some strange aura is blocking the way.")) return - if(locate(/obj/effect/blessing) in stepTurf) + if(HAS_TRAIT(stepTurf, TRAIT_TURF_BLESSED)) to_chat(L, span_warning("Holy energies block your path!")) return @@ -256,7 +256,7 @@ GAME_VERB_HIDDEN(/client, drop_item, "drop item") if(stepTurf.turf_flags & NOJAUNT) to_chat(L, span_warning("Some strange aura is blocking the way.")) return - if(locate(/obj/effect/blessing) in stepTurf) + if (HAS_TRAIT(stepTurf, TRAIT_TURF_BLESSED)) to_chat(L, span_warning("Holy energies block your path!")) return L.forceMove(stepTurf) @@ -553,7 +553,7 @@ GAME_VERB_HIDDEN_INSTANT(/client, toggle_walk_run, "toggle-walk-run") if(!can_z_move(UP, current_turf, null, ZMOVE_CAN_FLY_CHECKS|ZMOVE_FEEDBACK)) return balloon_alert(src, "moving up...") - if(!do_after(src, 1 SECONDS, hidden = TRUE)) + if(!do_after(src, 1 SECONDS, cog_icon = null)) return if(zMove(UP, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK)) to_chat(src, span_notice("You move upwards.")) @@ -577,7 +577,7 @@ GAME_VERB_HIDDEN_INSTANT(/client, toggle_walk_run, "toggle-walk-run") if(!can_z_move(DOWN, current_turf, null, ZMOVE_CAN_FLY_CHECKS|ZMOVE_FEEDBACK)) return balloon_alert(src, "moving down...") - if(!do_after(src, 1 SECONDS, hidden = TRUE)) + if(!do_after(src, 1 SECONDS, cog_icon = null)) return if(zMove(DOWN, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK)) to_chat(src, span_notice("You move down.")) diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 9d564daae61e..6b4706fe6ea9 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -209,7 +209,7 @@ GAME_VERB(/mob, me_verb, VERB_ME, null, message as text) else if(key == "%" && !mods[MODE_SING]) mods[MODE_SING] = TRUE else if(key == ";" && !mods[MODE_HEADSET]) - if(stat == CONSCIOUS) //necessary indentation so it gets stripped of the semicolon anyway. + if(!IS_UNCONSCIOUS_OR_CRIT(src)) //necessary indentation so it gets stripped of the semicolon anyway. mods[MODE_HEADSET] = TRUE else if((key in GLOB.department_radio_prefixes) && length(message) > length(key) + 1 && !mods[RADIO_EXTENSION]) mods[RADIO_KEY] = LOWER_TEXT(message[1 + length(key)]) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 17da72903664..d1d3225f0e2e 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -110,7 +110,7 @@ if(client) stop_sound_channel(CHANNEL_LOBBYMUSIC) - var/mob/living/silicon/ai/our_AI = new /mob/living/silicon/ai(pick(landmark_loc), null, src) + var/mob/living/silicon/ai/our_AI = new(pick(landmark_loc), src) . = our_AI if(preference_source) diff --git a/code/modules/mob_spawn/corpses/mining_corpses.dm b/code/modules/mob_spawn/corpses/mining_corpses.dm index bece8ddb28bb..2be66a4e6dfa 100644 --- a/code/modules/mob_spawn/corpses/mining_corpses.dm +++ b/code/modules/mob_spawn/corpses/mining_corpses.dm @@ -34,6 +34,24 @@ outfit = select_outfit() return ..() +/obj/effect/mob_spawn/corpse/human/legioninfested/get_mob_species(mob/mob_possessor, apply_prefs) + if(ispath(outfit, /datum/outfit/consumed_miner)) + return pick_weight(list( + /datum/species/human = 70, + /datum/species/lizard = 26, + /datum/species/fly = 2, + /datum/species/plasmaman = 2, + )) + if(ispath(outfit, /datum/outfit/consumed_ashwalker)) + return /datum/species/lizard/ashwalker + if(ispath(outfit, /datum/outfit/consumed_golem)) + return /datum/species/golem + if(ispath(outfit, /datum/outfit/consumed_shadowperson)) + return /datum/species/shadow + if(ispath(outfit, /datum/outfit/consumed_heremoth)) + return /datum/species/moth + return ..() + /obj/effect/mob_spawn/corpse/human/legioninfested/special(mob/living/carbon/human/spawned_human, mob/mob_possessor, apply_prefs) . = ..() var/obj/item/organ/legion_tumour/cancer = new() @@ -151,13 +169,7 @@ if(visuals_only) regular_uniform = TRUE //assume human else - var/new_species_type = pick_weight(list( - /datum/species/human = 70, - /datum/species/lizard = 26, - /datum/species/fly = 2, - /datum/species/plasmaman = 2, - )) - miner.set_species(new_species_type) + var/new_species_type = miner.dna.species.type if(new_species_type != /datum/species/plasmaman) regular_uniform = TRUE else @@ -207,8 +219,6 @@ uniform = /obj/item/clothing/under/costume/gladiator/ash_walker /datum/outfit/consumed_ashwalker/pre_equip(mob/living/carbon/human/ashwalker, visuals_only = FALSE) - if(!visuals_only) - ashwalker.set_species(/datum/species/lizard/ashwalker) if(prob(95)) head = /obj/item/clothing/head/helmet/gladiator else @@ -281,8 +291,6 @@ //Oops! All randomized! /datum/outfit/consumed_golem/pre_equip(mob/living/carbon/human/golem, visuals_only = FALSE) - if(!visuals_only) - golem.set_species(/datum/species/golem) if(prob(30)) glasses = pick_weight(list( /obj/item/clothing/glasses/hud/diagnostic = 2, @@ -367,11 +375,6 @@ accessory = /obj/item/clothing/accessory/medal/plasma/nobel_science -/datum/outfit/consumed_shadowperson/pre_equip(mob/living/carbon/human/shadowperson, visuals_only = FALSE) - if(visuals_only) - return - shadowperson.set_species(/datum/species/shadow) - /datum/outfit/consumed_cultist name = "Legion-Consumed Cultist" uniform = /obj/item/clothing/under/costume/roman @@ -392,8 +395,6 @@ head = /obj/item/clothing/head/helmet/chaplain/heretic /datum/outfit/consumed_heremoth/pre_equip(mob/living/carbon/human/moth, visuals_only = FALSE) - if(!visuals_only) - moth.set_species(/datum/species/moth) if(prob(70)) glasses = /obj/item/clothing/glasses/blindfold if(prob(90)) diff --git a/code/modules/mob_spawn/corpses/nonhuman_corpses.dm b/code/modules/mob_spawn/corpses/nonhuman_corpses.dm index 42d7640eb2b6..50b6c8d7af2e 100644 --- a/code/modules/mob_spawn/corpses/nonhuman_corpses.dm +++ b/code/modules/mob_spawn/corpses/nonhuman_corpses.dm @@ -1,21 +1,5 @@ //full of weird and wacky mob spawns. this is probably the darkest corner of mob spawns even after cleanup so be ready for shitcode - -///dead ai, blue screen and everything. -/obj/effect/mob_spawn/corpse/ai - mob_type = /mob/living/silicon/ai/spawned - -/obj/effect/mob_spawn/corpse/ai/create(mob/user, newname, apply_prefs) - var/ai_already_present = locate(/mob/living/silicon/ai) in loc - if(ai_already_present) - return CANCEL_SPAWN - return ..() - -/obj/effect/mob_spawn/corpse/ai/special(mob/living/silicon/ai/spawned/dead_ai, mob/mob_possessor, apply_prefs) - . = ..() - dead_ai.name = src.name - dead_ai.real_name = src.name - ///dead slimes, with a var for whatever color you want. /obj/effect/mob_spawn/corpse/slime mob_type = /mob/living/basic/slime diff --git a/code/modules/mob_spawn/ghost_roles/mining_roles.dm b/code/modules/mob_spawn/ghost_roles/mining_roles.dm index 3fb0e2f8f7a0..780806005c86 100644 --- a/code/modules/mob_spawn/ghost_roles/mining_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/mining_roles.dm @@ -198,8 +198,7 @@ /obj/structure/ash_walker_eggshell/Destroy() if(!egg) return ..() - var/mob/living/carbon/human/yolk = new(get_turf(src)) - yolk.set_species(/datum/species/lizard/ashwalker) + var/mob/living/carbon/human/yolk = new(get_turf(src), /datum/species/lizard/ashwalker) yolk.fully_replace_character_name(null, yolk.generate_random_mob_name(TRUE)) yolk.underwear = "Nude" yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making diff --git a/code/modules/mob_spawn/mob_spawn.dm b/code/modules/mob_spawn/mob_spawn.dm index 067107fee242..7e9af0dd7b3b 100644 --- a/code/modules/mob_spawn/mob_spawn.dm +++ b/code/modules/mob_spawn/mob_spawn.dm @@ -60,13 +60,21 @@ /obj/effect/mob_spawn/proc/create(mob/mob_possessor, newname, apply_prefs) SHOULD_NOT_SLEEP(TRUE) - var/mob/living/spawned_mob = new mob_type(get_turf(src)) //living mobs only + var/mob/living/spawned_mob + if(ispath(mob_type, /mob/living/carbon/human)) + spawned_mob = new mob_type(get_turf(src), get_mob_species(mob_possessor, apply_prefs)) + else + spawned_mob = new mob_type(get_turf(src)) //living mobs only special(spawned_mob, mob_possessor, apply_prefs) name_mob(spawned_mob, newname) equip(spawned_mob) spawned_mob_ref = WEAKREF(spawned_mob) return spawned_mob +/// Returns the species typepath a human spawned by this spawner should be initialized with. +/obj/effect/mob_spawn/proc/get_mob_species(mob/mob_possessor, apply_prefs) + return mob_species + /** * Any special behavior that needs to be done to the mob after it's created but before it's equipped. * @@ -78,24 +86,25 @@ SHOULD_CALL_PARENT(TRUE) if(faction) spawned_mob.set_faction(faction) - if(ishuman(spawned_mob)) - var/mob/living/carbon/human/spawned_human = spawned_mob - if(mob_species) - spawned_human.set_species(mob_species) - spawned_human.dna.species.give_important_for_life(spawned_human) // for preventing plasmamen from combusting immediately upon spawning - spawned_human.underwear = "Nude" - spawned_human.undershirt = "Nude" - spawned_human.socks = "Nude" - randomize_human_normie(spawned_human) - if(hairstyle) - spawned_human.set_hairstyle(hairstyle, update = FALSE) - if(facial_hairstyle) - spawned_human.set_facial_hairstyle(facial_hairstyle, update = FALSE) - if(haircolor) - spawned_human.set_haircolor(haircolor, update = FALSE) - if(facial_haircolor) - spawned_human.set_facial_haircolor(facial_haircolor, update = FALSE) - spawned_human.update_body(is_creating = TRUE) + if(!ishuman(spawned_mob)) + return + var/mob/living/carbon/human/spawned_human = spawned_mob + spawned_human.dna.species.give_important_for_life(spawned_human) // for preventing plasmamen from combusting immediately upon spawning + spawned_human.underwear = "Nude" + spawned_human.undershirt = "Nude" + spawned_human.socks = "Nude" + randomize_human_normie(spawned_human) + if(hairstyle) + spawned_human.set_hairstyle(hairstyle, update = FALSE) + if(facial_hairstyle) + spawned_human.set_facial_hairstyle(facial_hairstyle, update = FALSE) + if(haircolor) + spawned_human.set_haircolor(haircolor, update = FALSE) + if(facial_haircolor) + spawned_human.set_facial_haircolor(facial_haircolor, update = FALSE) + if(skin_tone) + spawned_human.skin_tone = skin_tone + spawned_human.update_body(is_creating = TRUE) /obj/effect/mob_spawn/proc/name_mob(mob/living/spawned_mob, forced_name) var/chosen_name @@ -270,6 +279,11 @@ return ..() +/obj/effect/mob_spawn/ghost_role/get_mob_species(mob/mob_possessor, apply_prefs) + if(mob_possessor?.client && apply_prefs && (allow_custom_character & GHOSTROLE_TAKE_PREFS_SPECIES)) + return mob_possessor.client.prefs.read_preference(/datum/preference/choiced/species) + return ..() + /obj/effect/mob_spawn/ghost_role/special(mob/living/spawned_mob, mob/mob_possessor, apply_prefs) . = ..() if(mob_possessor) @@ -278,7 +292,6 @@ if(allow_custom_character & GHOSTROLE_TAKE_PREFS_APPEARANCE) mob_possessor.client.prefs.apply_prefs_to(spawned_human, icon_updates = TRUE, do_not_apply = typesof(/datum/preference/name, /datum/preference/choiced/species)) if(allow_custom_character & GHOSTROLE_TAKE_PREFS_SPECIES) - spawned_human.set_species(mob_possessor.client.prefs.read_preference(/datum/preference/choiced/species)) spawned_human.fully_replace_character_name(spawned_human.real_name, spawned_human.generate_random_mob_name()) if(mob_possessor.mind) mob_possessor.mind.transfer_to(spawned_mob, force_key_move = TRUE) diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index 0bb4d4342d0e..0fff4d99f59a 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -79,7 +79,7 @@ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) if(part_datum.can_overslot) var/obj/item/overslot = wearer.get_item_by_slot(part.slot_flags) - if(istype(overslot, /obj/item/clothing)) + if(isclothing(overslot)) part_datum.overslotting = overslot transfer_part_to_loc(overslot, part, force = TRUE, preserve_suit_storage = can_preserve_suit_storage(part, wearer.s_store)) RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit)) @@ -113,7 +113,7 @@ return FALSE /obj/item/mod/control/proc/can_preserve_suit_storage(obj/item/new_suit, obj/item/stored_item) - if(!istype(new_suit, /obj/item/clothing) || !stored_item) + if(!isclothing(new_suit) || !stored_item) return FALSE if(HAS_TRAIT(stored_item, TRAIT_NODROP)) return FALSE @@ -253,7 +253,7 @@ /obj/item/mod/control/proc/delayed_seal_part(obj/item/clothing/part) . = FALSE var/datum/mod_part/part_datum = get_part_datum(part) - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), hidden = TRUE)) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), cog_icon = null)) to_chat(wearer, span_notice("[part] [!part_datum.sealed ? part_datum.sealed_message : part_datum.unsealed_message].")) playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) seal_part(part, is_sealed = !part_datum.sealed) @@ -261,7 +261,7 @@ /obj/item/mod/control/proc/delayed_activation() . = FALSE - if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), hidden = TRUE)) + if(do_after(wearer, activation_step_time, wearer, MOD_ACTIVATION_STEP_FLAGS, extra_checks = CALLBACK(src, PROC_REF(get_wearer)), cog_icon = null)) control_activation(is_on = !active) return TRUE diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm index 461cb8c4f86e..3b2017804344 100644 --- a/code/modules/mod/mod_ai.dm +++ b/code/modules/mod/mod_ai.dm @@ -30,7 +30,7 @@ return if(intAI.deployed_shell) //Recall AI if shelled so it can be checked for a client intAI.disconnect_shell() - if(intAI.stat || !intAI.client) + if(IS_UNCONSCIOUS_OR_CRIT(intAI) || !intAI.client) balloon_alert(user, "ai unresponsive!") return balloon_alert(user, "transferring to unit...") diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm index c7bf37c1fc48..5ed90edb8e6b 100644 --- a/code/modules/mod/mod_construction.dm +++ b/code/modules/mod/mod_construction.dm @@ -71,17 +71,21 @@ . = ..() . += span_notice("You could probably attach some wires to it...") -/obj/item/mod/construction/lavalandcore/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers) - if(!istype(weapon, /obj/item/stack/cable_coil)) - return ..() - if(!weapon.tool_start_check(user, amount=2)) - return +/obj/item/mod/construction/lavalandcore/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/cable_coil)) + return NONE + + if(!tool.tool_start_check(user, amount=2)) + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "installing wires...") - if(!weapon.use_tool(src, user, 5 SECONDS, amount = 2, volume = 30)) + if(!tool.use_tool(src, user, 5 SECONDS, amount = 2, volume = 30)) balloon_alert(user, "interrupted!") - return + return ITEM_INTERACT_BLOCKING + new /obj/item/mod/core/plasma/lavaland(drop_location()) qdel(src) + return ITEM_INTERACT_SUCCESS /obj/item/mod/construction/plating name = "MOD external plating" @@ -170,124 +174,223 @@ display_text = "All it's missing is external plating..." . += span_notice(display_text) -/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, list/modifiers, list/attack_modifiers) - . = ..() +/obj/item/mod/construction/shell/item_interaction(mob/living/user, obj/item/tool, list/modifiers) switch(step) if(START_STEP) - if(!istype(part, /obj/item/mod/core)) - return - if(!user.transferItemToLoc(part, src)) + if(!istype(tool, /obj/item/mod/core)) + return NONE + + if(!user.transferItemToLoc(tool, src)) balloon_alert(user, "it's stuck!") - return + return ITEM_INTERACT_BLOCKING + playsound(src, 'sound/machines/click.ogg', 30, TRUE) balloon_alert(user, "core inserted") - core = part + core = tool step = CORE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(SCREWED_CORE_STEP) + if(!istype(tool, /obj/item/mod/construction/helmet)) //Construct + return NONE + + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "it's stuck!") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "helmet added") + helmet = tool + step = HELMET_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(HELMET_STEP) + if(!istype(tool, /obj/item/mod/construction/chestplate)) //Construct + return NONE + + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "it's stuck!") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "chestplate added") + chestplate = tool + step = CHESTPLATE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(CHESTPLATE_STEP) + if(!istype(tool, /obj/item/mod/construction/gauntlets)) //Construct + return NONE + + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "it's stuck!") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "gauntlets added") + gauntlets = tool + step = GAUNTLETS_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(GAUNTLETS_STEP) + if(!istype(tool, /obj/item/mod/construction/boots)) //Construct + return NONE + + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "it's stuck!") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + balloon_alert(user, "boots added") + boots = tool + step = BOOTS_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(SCREWED_ASSEMBLY_STEP) + if(!istype(tool, /obj/item/mod/construction/plating)) //Construct + return NONE + + var/obj/item/mod/construction/plating/external_plating = tool + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "it's stuck!") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core) + core = null + qdel(src) + user.put_in_hands(mod) + mod.balloon_alert(user, "unit finished") + update_icon_state() + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/item/mod/construction/shell/screwdriver_act(mob/living/user, obj/item/tool) + switch(step) + if(CORE_STEP) - if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "core screwed") - step = SCREWED_CORE_STEP - else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - core.forceMove(drop_location()) - balloon_alert(user, "core taken out") - step = START_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "core screwed") + step = SCREWED_CORE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + if(SCREWED_CORE_STEP) - if(istype(part, /obj/item/mod/construction/helmet)) //Construct - if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "it's stuck!") - return - playsound(src, 'sound/machines/click.ogg', 30, TRUE) - balloon_alert(user, "helmet added") - helmet = part - step = HELMET_STEP - else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "core unscrewed") - step = CORE_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "core unscrewed") + step = CORE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(WRENCHED_ASSEMBLY_STEP) + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "assembly screwed") + step = SCREWED_ASSEMBLY_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(SCREWED_ASSEMBLY_STEP) + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "assembly unscrewed") + step = WRENCHED_ASSEMBLY_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/item/mod/construction/shell/crowbar_act(mob/living/user, obj/item/tool) + switch(step) + if(CORE_STEP) + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_SUCCESS + + core.forceMove(drop_location()) + balloon_alert(user, "core taken out") + step = START_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + if(HELMET_STEP) - if(istype(part, /obj/item/mod/construction/chestplate)) //Construct - if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "it's stuck!") - return - playsound(src, 'sound/machines/click.ogg', 30, TRUE) - balloon_alert(user, "chestplate added") - chestplate = part - step = CHESTPLATE_STEP - else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - helmet.forceMove(drop_location()) - balloon_alert(user, "helmet removed") - helmet = null - step = SCREWED_CORE_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + helmet.forceMove(drop_location()) + balloon_alert(user, "helmet removed") + helmet = null + step = SCREWED_CORE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + if(CHESTPLATE_STEP) - if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct - if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "it's stuck!") - return - playsound(src, 'sound/machines/click.ogg', 30, TRUE) - balloon_alert(user, "gauntlets added") - gauntlets = part - step = GAUNTLETS_STEP - else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - chestplate.forceMove(drop_location()) - balloon_alert(user, "chestplate removed") - chestplate = null - step = HELMET_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + chestplate.forceMove(drop_location()) + balloon_alert(user, "chestplate removed") + chestplate = null + step = HELMET_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + if(GAUNTLETS_STEP) - if(istype(part, /obj/item/mod/construction/boots)) //Construct - if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "it's stuck!") - return - playsound(src, 'sound/machines/click.ogg', 30, TRUE) - balloon_alert(user, "boots added") - boots = part - step = BOOTS_STEP - else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - gauntlets.forceMove(drop_location()) - balloon_alert(user, "gauntlets removed") - gauntlets = null - step = CHESTPLATE_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + gauntlets.forceMove(drop_location()) + balloon_alert(user, "gauntlets removed") + gauntlets = null + step = CHESTPLATE_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + if(BOOTS_STEP) + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + boots.forceMove(drop_location()) + balloon_alert(user, "boots removed") + boots = null + step = GAUNTLETS_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/item/mod/construction/shell/wrench_act(mob/living/user, obj/item/tool) + switch(step) if(BOOTS_STEP) - if(part.tool_behaviour == TOOL_WRENCH) //Construct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "assembly secured") - step = WRENCHED_ASSEMBLY_STEP - else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - boots.forceMove(drop_location()) - balloon_alert(user, "boots removed") - boots = null - step = GAUNTLETS_STEP + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "assembly secured") + step = WRENCHED_ASSEMBLY_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + if(WRENCHED_ASSEMBLY_STEP) - if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "assembly screwed") - step = SCREWED_ASSEMBLY_STEP - else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "assembly unsecured") - step = BOOTS_STEP - if(SCREWED_ASSEMBLY_STEP) - if(istype(part, /obj/item/mod/construction/plating)) //Construct - var/obj/item/mod/construction/plating/external_plating = part - if(!user.transferItemToLoc(part, src)) - balloon_alert(user, "it's stuck!") - return - playsound(src, 'sound/machines/click.ogg', 30, TRUE) - var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core) - core = null - qdel(src) - user.put_in_hands(mod) - mod.balloon_alert(user, "unit finished") - else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct - if(part.use_tool(src, user, 0, volume=30)) - balloon_alert(user, "assembly unscrewed") - step = SCREWED_ASSEMBLY_STEP - update_icon_state() + if(!tool.use_tool(src, user, 0, volume = 30)) + return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "assembly unsecured") + step = BOOTS_STEP + update_icon_state() + return ITEM_INTERACT_SUCCESS + + return NONE /obj/item/mod/construction/shell/update_icon_state() . = ..() diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 7f6fff2e89e3..7673729b0121 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -386,7 +386,7 @@ selected_module?.deactivate(display_message = TRUE) wearer.apply_damage(5 / severity, BURN, spread_damage=TRUE) to_chat(wearer, span_danger("You feel [src] heat up from the EMP, burning you slightly.")) - if(wearer.stat < UNCONSCIOUS && prob(10)) + if(!IS_UNCONSCIOUS(wearer) && prob(10)) wearer.emote("scream") /obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot) diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm index 1306d568e044..119727bdaa87 100644 --- a/code/modules/mod/mod_link.dm +++ b/code/modules/mod/mod_link.dm @@ -106,7 +106,7 @@ . = ITEM_INTERACT_SUCCESS /obj/item/mod/control/proc/can_call() - return get_charge() && wearer && wearer.stat < DEAD + return get_charge() && wearer && wearer.stat != DEAD /obj/item/mod/control/proc/make_link_visual() return make_link_visual_generic(mod_link, PROC_REF(on_overlay_change)) @@ -206,14 +206,16 @@ return cell.use(0.02 * STANDARD_CELL_RATE * seconds_per_tick, force = TRUE) -/obj/item/clothing/neck/link_scryer/attackby(obj/item/attacked_by, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(cell || !istype(attacked_by, /obj/item/stock_parts/power_store/cell)) - return - if(!user.transferItemToLoc(attacked_by, src)) - return - cell = attacked_by +/obj/item/clothing/neck/link_scryer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(cell || !istype(tool, /obj/item/stock_parts/power_store/cell)) + return ..() + + if(!user.transferItemToLoc(tool, src)) + return ITEM_INTERACT_BLOCKING + + cell = tool balloon_alert(user, "cell installed") + return ITEM_INTERACT_SUCCESS /obj/item/clothing/neck/link_scryer/update_name(updates) . = ..() @@ -282,7 +284,7 @@ /obj/item/clothing/neck/link_scryer/proc/can_call() var/mob/living/user = loc - return istype(user) && cell?.charge && user.stat < DEAD + return istype(user) && cell?.charge && user.stat != DEAD /obj/item/clothing/neck/link_scryer/proc/make_link_visual() var/mob/living/user = mod_link.get_user_callback.Invoke() diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm index 9943d24dc3e7..d71806952481 100644 --- a/code/modules/mod/mod_theme.dm +++ b/code/modules/mod/mod_theme.dm @@ -154,6 +154,9 @@ part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE part.flags_cover = category[UNSEALED_COVER] || NONE part.visor_flags_cover = category[SEALED_COVER] || NONE + REMOVE_TRAITS_IN(part, MOD_THEME_TRAIT) + for(var/trait in category[MOD_PART_TRAITS]) + ADD_TRAIT(part, trait, MOD_THEME_TRAIT) if(mod.get_part_datum(part).sealed) part.clothing_flags |= part.visor_flags part.flags_inv |= part.visor_flags_inv @@ -1562,19 +1565,21 @@ variants = list( "enchanted" = list( /obj/item/clothing/head/mod = list( - UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL|CASTING_CLOTHES, + UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE|HEADINTERNALS, UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT, UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF, UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE, SEALED_MESSAGE = HELMET_SEAL_MESSAGE, + MOD_PART_TRAITS = list(TRAIT_CASTING_CLOTHING), ), /obj/item/clothing/suit/mod = list( - UNSEALED_CLOTHING = THICKMATERIAL|CASTING_CLOTHES, + UNSEALED_CLOTHING = THICKMATERIAL, SEALED_CLOTHING = STOPSPRESSUREDAMAGE, SEALED_INVISIBILITY = HIDEJUMPSUIT, UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE, SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE, + MOD_PART_TRAITS = list(TRAIT_CASTING_CLOTHING), ), /obj/item/clothing/gloves/mod = list( UNSEALED_CLOTHING = THICKMATERIAL, diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm index 027bbf587bf6..f664d71629b1 100644 --- a/code/modules/mod/modules/module_kinesis.dm +++ b/code/modules/mod/modules/module_kinesis.dm @@ -270,7 +270,7 @@ structures, machinery, vehicles, and, thanks to the fruitful efforts of its creators - living beings." complexity = 0 prebuilt = TRUE - stat_required = CONSCIOUS + stat_required = STABLE /// Admin suit version of kinesis. Can grab anything at any range, may enable phasing through walls. /obj/item/mod/module/anomaly_locked/kinesis/admin @@ -281,7 +281,7 @@ grab_range = INFINITY use_energy_cost = DEFAULT_CHARGE_DRAIN * 0 prebuilt = TRUE - stat_required = CONSCIOUS + stat_required = STABLE /// Does our object phase through stuff? var/phasing = FALSE diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 6a6862e918bf..748cb5825509 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -382,14 +382,22 @@ incompatible_modules = list(/obj/item/mod/module/emp_shield) required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/plasma = SMALL_MATERIAL_AMOUNT * 5) + /// How much emp protection this module grants to its wearer when fully extended. + var/protection_factor = EMP_PROTECTION_MODERATE /obj/item/mod/module/emp_shield/on_install() . = ..() mod.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) + var/list/all_parts = mod.get_parts() + for(var/obj/item/clothing/mod_part as anything in all_parts) + mod_part.emp_protection += protection_factor/all_parts.len /obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE) . = ..() mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_ALL) + var/list/all_parts = mod.get_parts() + for(var/obj/item/clothing/mod_part as anything in all_parts) + mod_part.emp_protection -= protection_factor/all_parts.len /obj/item/mod/module/emp_shield/advanced name = "MOD advanced EMP shield module" diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm index 83f698a23d94..fc4038fae21e 100644 --- a/code/modules/mod/modules/modules_ninja.dm +++ b/code/modules/mod/modules/modules_ninja.dm @@ -230,7 +230,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN /obj/machinery/computer/records/security/proc/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) - if(!do_after(ninja, 20 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(can_hack), ninja), hidden = TRUE)) + if(!do_after(ninja, 20 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(can_hack), ninja), cog_icon = null)) return for(var/datum/record/crew/target in GLOB.manifest.general) target.wanted_status = WANTED_ARREST @@ -277,7 +277,7 @@ if(hacking_module.mod.get_charge() + drain > hacking_module.mod.get_max_charge()) drain = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() maxcapacity = TRUE//Reached maximum battery capacity. - if (do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) + if (do_after(ninja, 1 SECONDS, target = src, cog_icon = null)) spark_system.start() playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) cell.use(drain) @@ -312,7 +312,7 @@ if(hacking_module.mod.get_charge() + drain > hacking_module.mod.get_max_charge()) drain = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() maxcapacity = TRUE - if (do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) + if (do_after(ninja, 1 SECONDS, target = src, cog_icon = null)) spark_system.start() playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) charge -= drain @@ -332,7 +332,7 @@ /obj/item/stock_parts/power_store/cell/proc/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) var/drain_total = 0 - if(charge && !do_after(ninja, 3 SECONDS, target = src, hidden = TRUE)) + if(charge && !do_after(ninja, 3 SECONDS, target = src, cog_icon = null)) drain_total = charge if(hacking_module.mod.get_charge() + charge > hacking_module.mod.get_max_charge()) drain_total = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() @@ -356,7 +356,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN /obj/machinery/rnd/server/master/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) - if(!do_after(ninja, 30 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 30 SECONDS, target = src, cog_icon = null)) return overload_source_code_hdd() to_chat(ninja, span_notice("Sabotage complete. Storage device overloaded.")) @@ -375,7 +375,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN /obj/machinery/rnd/server/proc/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) - if(!do_after(ninja, 30 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 30 SECONDS, target = src, cog_icon = null)) return stored_research.modify_points_all(0) to_chat(ninja, span_notice("Sabotage complete. Research notes corrupted.")) @@ -440,7 +440,7 @@ while(!maxcapacity && src) drain = (round((rand(NINJA_MIN_DRAIN, NINJA_MAX_DRAIN))/2)) var/drained = 0 - if(wire_powernet && do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) + if(wire_powernet && do_after(ninja, 1 SECONDS, target = src, cog_icon = null)) drained = min(drain, delayed_surplus()) add_delayedload(drained) if(drained < drain)//if no power on net, drain apcs @@ -480,7 +480,7 @@ if(hacking_module.mod.get_charge() + drain > hacking_module.mod.get_max_charge()) drain = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() maxcapacity = TRUE - if (do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) + if (do_after(ninja, 1 SECONDS, target = src, cog_icon = null)) spark_system.start() playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) cell.use(drain) @@ -500,7 +500,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN /mob/living/silicon/robot/proc/ninjadrain_charge(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) - if(!do_after(ninja, 6 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 6 SECONDS, target = src, cog_icon = null)) return spark_system.start() playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) @@ -509,7 +509,7 @@ bubble_icon = "syndibot" UnlinkSelf() ionpulse = TRUE - laws = new /datum/ai_laws/ninja_override() + replace_law_set(/datum/ai_laws/ninja_override) model.transform_to(pick(/obj/item/robot_model/syndicate, /obj/item/robot_model/syndicate_medical, /obj/item/robot_model/saboteur)) var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja) @@ -550,7 +550,7 @@ //BOTS, overloads them and causes a explosion /mob/living/basic/bot/ninjadrain_act(mob/living/carbon/human/ninja, obj/item/mod/module/hacker/hacking_module) to_chat(src, span_boldwarning("Your circutry suddenly begins heating up!")) - if(!do_after(ninja, 1.5 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 1.5 SECONDS, target = src, cog_icon = null)) return COMPONENT_CANCEL_ATTACK_CHAIN if(!hacking_module.mod.subtract_charge(DEFAULT_CHARGE_DRAIN * 7)) @@ -576,7 +576,7 @@ balloon_alert(ninja, "no energy!") return COMPONENT_CANCEL_ATTACK_CHAIN - if(!do_after(ninja, 1.5 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 1.5 SECONDS, target = src, cog_icon = null)) return COMPONENT_CANCEL_ATTACK_CHAIN hacking_module.mod.add_charge(cell.charge) @@ -593,7 +593,7 @@ balloon_alert(ninja, "already hacked!") return COMPONENT_CANCEL_ATTACK_CHAIN - if(!do_after(ninja, 2 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 2 SECONDS, target = src, cog_icon = null)) return COMPONENT_CANCEL_ATTACK_CHAIN if(!hacking_module.mod.subtract_charge(DEFAULT_CHARGE_DRAIN * 5)) @@ -611,7 +611,7 @@ return COMPONENT_CANCEL_ATTACK_CHAIN AI_notify_hack() - if(!do_after(ninja, 30 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 30 SECONDS, target = src, cog_icon = null)) return COMPONENT_CANCEL_ATTACK_CHAIN do_sparks(3, cardinal_only = FALSE, source = src) @@ -625,7 +625,7 @@ balloon_alert(ninja, "already hacked!") return COMPONENT_CANCEL_ATTACK_CHAIN - if(!do_after(ninja, 2 SECONDS, target = src, hidden = TRUE)) + if(!do_after(ninja, 2 SECONDS, target = src, cog_icon = null)) return COMPONENT_CANCEL_ATTACK_CHAIN do_sparks(3, cardinal_only = FALSE, source = src) @@ -646,7 +646,7 @@ AI_notify_hack() - if(!do_after(ninja, 20 SECONDS, target = src, hidden = TRUE)) //Shorter due to how incredibly easy it is for someone to (even accidentally) interrupt. + if(!do_after(ninja, 20 SECONDS, target = src, cog_icon = null)) //Shorter due to how incredibly easy it is for someone to (even accidentally) interrupt. return COMPONENT_CANCEL_ATTACK_CHAIN force_event(/datum/round_event_control/tram_malfunction, "ninja interference") diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 2e3d21141f12..f53e14ebb349 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -178,11 +178,11 @@ if(!. || !mod.wearer.Adjacent(target)) return - if(!ismineralturf(target) || !isasteroidturf(target)) + if(!ismineralturf(target) && !isasteroidturf(target)) return if(drain_power(use_energy_cost)) - target.attackby(src, mod.wearer) + target.base_item_interaction(mod.wearer, src) /obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity) SIGNAL_HANDLER @@ -727,7 +727,7 @@ /obj/item/mod/module/sphere_transform/proc/on_statchange(datum/source) SIGNAL_HANDLER - if(mod.wearer.stat) + if(IS_UNCONSCIOUS_OR_CRIT(mod.wearer)) deactivate() /obj/projectile/bullet/mining_missile diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm index edb880d74ba0..953f5055feca 100644 --- a/code/modules/mod/modules/modules_visor.dm +++ b/code/modules/mod/modules/modules_visor.dm @@ -97,8 +97,6 @@ /obj/item/mod/module/night/on_activation(mob/activator) ADD_TRAIT(mod.wearer, TRAIT_TRUE_NIGHT_VISION, REF(src)) - mod.wearer.update_sight() /obj/item/mod/module/night/on_deactivation(mob/activator, display_message = TRUE, deleting = FALSE) REMOVE_TRAIT(mod.wearer, TRAIT_TRUE_NIGHT_VISION, REF(src)) - mod.wearer.update_sight() diff --git a/code/modules/modular_computers/computers/item/disks/virus_disk.dm b/code/modules/modular_computers/computers/item/disks/virus_disk.dm index fdc336362a56..62070193840c 100644 --- a/code/modules/modular_computers/computers/item/disks/virus_disk.dm +++ b/code/modules/modular_computers/computers/item/disks/virus_disk.dm @@ -109,18 +109,19 @@ ///How much progression should be shown in the uplink, set on purchase of the item. var/current_progression = 0 -/obj/item/disk/computer/virus/frame/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - . = ..() - if(!istype(attacking_item, /obj/item/stack/telecrystal)) - return +/obj/item/disk/computer/virus/frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/telecrystal)) + return ..() + if(!charges) - to_chat(user, span_notice("[src] is out of charges, it's refusing to accept [attacking_item].")) - return - var/obj/item/stack/telecrystal/telecrystal_stack = attacking_item + to_chat(user, span_notice("[src] is out of charges, it's refusing to accept [tool].")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/telecrystal/telecrystal_stack = tool telecrystals += telecrystal_stack.amount to_chat(user, span_notice("You slot [telecrystal_stack] into [src]. The next time it's used, it will also give telecrystals.")) telecrystal_stack.use(telecrystal_stack.amount) - + return ITEM_INTERACT_SUCCESS /obj/item/disk/computer/virus/frame/send_virus(obj/item/modular_computer/pda/source, obj/item/modular_computer/pda/target, mob/living/user, message) . = ..() diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm index 9096314200ca..bc8daf05fcda 100644 --- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm +++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm @@ -702,7 +702,7 @@ SEND_SIGNAL(computer, COMSIG_MODULAR_PDA_MESSAGE_RECEIVED, signal, fake_job || sender_messenger?.computer.saved_job , sender_name) for(var/mob/living/messaged_mob as anything in receievers) - if(messaged_mob.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(messaged_mob)) continue if(!messaged_mob.is_literate()) continue diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm index 90064ee3c04e..35377e5d4d25 100644 --- a/code/modules/modular_computers/file_system/programs/robotact.dm +++ b/code/modules/modular_computers/file_system/programs/robotact.dm @@ -52,7 +52,7 @@ data["designation"] = cyborg.model data["masterAI"] = cyborg.connected_ai data["MasterAI_connected"] = !!cyborg.connected_ai //Need a bool for this on the other side - data["masterAI_online"] = (cyborg.connected_ai?.stat == CONSCIOUS) + data["masterAI_online"] = (cyborg.connected_ai?.stat == STABLE) var/charge = 0 var/maxcharge = 1 @@ -151,7 +151,7 @@ cyborg.checklaws() if("alertPower") - if(cyborg.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(cyborg)) if(!cyborg.cell || !cyborg.cell.charge) cyborg.visible_message(span_notice("The power warning light on [span_name("[cyborg]")] flashes urgently."), \ "You announce you are operating in low power mode.") @@ -178,7 +178,7 @@ cyborg.toggle_headlamp(FALSE, TRUE) if("selfDestruct") - if(cyborg.stat || cyborg.lockcharge) //No detonation while stunned or locked down + if(IS_UNCONSCIOUS_OR_CRIT(cyborg) || cyborg.lockcharge) //No detonation while stunned or locked down return if(cyborg.emagged || istype(cyborg, /mob/living/silicon/robot/model/syndicate)) //This option shouldn't even be showing otherwise cyborg.self_destruct(cyborg) diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm index be057d5a5175..0e2d0114c984 100644 --- a/code/modules/pai/card.dm +++ b/code/modules/pai/card.dm @@ -32,15 +32,17 @@ SSpai.pai_card_list += src ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT) -/obj/item/pai_card/attackby(obj/item/used, mob/user, list/modifiers, list/attack_modifiers) - if(pai && istype(used, /obj/item/encryptionkey)) - if(!pai.encrypt_mod) - to_chat(user, span_alert("Encryption Key ports not configured.")) - return - pai.radio.attackby(used, user, modifiers) - to_chat(user, span_notice("You insert [used] into the [src].")) - return - return ..() +/obj/item/pai_card/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!pai || !istype(tool, /obj/item/encryptionkey)) + return NONE + + if(!pai.encrypt_mod) + to_chat(user, span_alert("Encryption Key ports not configured.")) + return ITEM_INTERACT_BLOCKING + + pai.radio.attackby(tool, user, modifiers) + to_chat(user, span_notice("You insert [tool] into the [src].")) + return ITEM_INTERACT_SUCCESS /obj/item/pai_card/attack_self(mob/user) if(!in_range(src, user)) @@ -121,7 +123,7 @@ can_holo = pai.can_holo, dna = pai.master_dna, emagged = pai.emagged, - laws = pai.laws.supplied, + laws = pai.laws.inherent, master = pai.master_name, name = pai.name, transmit = pai.can_transmit, diff --git a/code/modules/pai/debug.dm b/code/modules/pai/debug.dm index 0677e10cc7a4..36b3cae00b46 100644 --- a/code/modules/pai/debug.dm +++ b/code/modules/pai/debug.dm @@ -1,4 +1,4 @@ -ADMIN_VERB(makepAI, R_FUN, "Make pAI", "Specify a location to spawn a pAI device, then specify a key to play that pAI", ADMIN_CATEGORY_FUN, turf/target in GLOB.mob_list) +ADMIN_VERB(makepAI, R_FUN, "Make pAI", "Specify a location to spawn a pAI device, then specify a key to play that pAI", ADMIN_CATEGORY_FUN, turf/target) var/list/available = list() for(var/mob/player as anything in GLOB.player_list) if(player.client && player.key) diff --git a/code/modules/pai/door_jack.dm b/code/modules/pai/door_jack.dm index 759b0ada3d8d..a670e0126a6f 100644 --- a/code/modules/pai/door_jack.dm +++ b/code/modules/pai/door_jack.dm @@ -110,7 +110,7 @@ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 50, TRUE) balloon_alert(src, "overriding...") // Now begin hacking - if(!do_after(src, 15 SECONDS, hacking_cable.hacking_machine, timed_action_flags = NONE, progress = TRUE)) + if(!do_after(src, 15 SECONDS, hacking_cable.hacking_machine, timed_action_flags = NONE)) balloon_alert(src, "failed! retracting...") QDEL_NULL(hacking_cable) return FALSE diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm index b5b1099aabee..d5cf47faa872 100644 --- a/code/modules/pai/pai.dm +++ b/code/modules/pai/pai.dm @@ -207,8 +207,7 @@ give_messenger_ability() START_PROCESSING(SSfastprocess, src) make_laws() - for(var/law in laws.inherent) - lawcheck += law + law_ui.update_inherent_stated_laws(laws) var/obj/item/pai_card/pai_card = loc if(!istype(pai_card)) // when manually spawning a pai, we create a card to put it into. var/newcardloc = pai_card @@ -241,7 +240,7 @@ /mob/living/silicon/pai/make_laws() laws = new /datum/ai_laws/pai() - return TRUE + laws.name = "PAI Directives" /mob/living/silicon/pai/process(seconds_per_tick) holochassis_health = clamp((holochassis_health + (HOLOCHASSIS_REGEN_PER_SECOND * seconds_per_tick)), -50, HOLOCHASSIS_MAX_HEALTH) @@ -273,15 +272,6 @@ held_state = "[chassis]" return ..() -/mob/living/silicon/pai/set_stat(new_stat) - . = ..() - update_stat() - -/mob/living/silicon/pai/on_knockedout_trait_loss(datum/source) - . = ..() - set_stat(CONSCIOUS) - update_stat() - /** * Resolves the weakref of the pai's master. * If the master has been deleted, calls reset_software(). @@ -323,6 +313,7 @@ return FALSE return holder + /** * Handles the pai card or the pai itself being hit with an emag. * This replaces any current laws, masters, and DNA. @@ -341,7 +332,9 @@ master_name = "The Syndicate" master_dna = "Untraceable Signature" // Sets supplemental directive to this - add_supplied_law(0, "Do not interfere with the operations of the Syndicate.") + laws.clear_inherent_laws() + laws.add_inherent_law("Do not interfere with the operations of the Syndicate.") + log_law_change(attacker, "emagged pai [key_name(src)]") to_chat(src, span_danger("ALERT: Foreign software detected.")) return TRUE @@ -363,7 +356,7 @@ master_ref = null master_name = null master_dna = null - add_supplied_law(0, "None.") + laws.clear_inherent_laws() leash = AddComponent(/datum/component/leash, card, HOLOFORM_DEFAULT_RANGE, force_teleport_out_effect = /obj/effect/temp_visual/guardian/phase/out) balloon_alert(src, "software rebooted") return TRUE @@ -404,13 +397,14 @@ user, "Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", - laws.supplied[1], + laws.inherent[1], max_length = 300, ) - if(!new_laws || !master_ref) + if(!new_laws || !master_ref || QDELETED(laws)) return FALSE - add_supplied_law(0, new_laws) + laws.add_inherent_law(new_laws) to_chat(src, span_notice(new_laws)) + log_law_change(user, "added law for pai [key_name(src)] (text: [new_laws])") return TRUE /** diff --git a/code/modules/pai/software.dm b/code/modules/pai/software.dm index 965149a93968..7cee9726335e 100644 --- a/code/modules/pai/software.dm +++ b/code/modules/pai/software.dm @@ -16,7 +16,7 @@ /mob/living/silicon/pai/ui_static_data(mob/user) var/list/data = list() data["available"] = available_software - data["directives"] = laws.supplied + data["directives"] = laws.inherent data["emagged"] = emagged data["languages"] = languages_granted data["master_name"] = master_name diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index fe3b7535c542..5627d3987778 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -186,7 +186,7 @@ if(.) return - if(usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) + if(IS_UNCONSCIOUS_OR_CRIT(usr) || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) return switch(action) diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index 5d2c234ea24f..9457e1410f6c 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -168,7 +168,7 @@ if(.) return - if(usr.stat != CONSCIOUS || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) + if(IS_UNCONSCIOUS_OR_CRIT(usr) || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) return switch(action) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index bdba1f80a84d..197abf7b53ca 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -339,7 +339,7 @@ GAME_VERB_SRC(/obj/item/paper, rename, usr, "Rename paper", null) var/n_name = tgui_input_text(usr, "Enter a paper label", "Paper Labelling", max_length = MAX_NAME_LEN) if(isnull(n_name) || n_name == "") return - if(((loc == usr || istype(loc, /obj/item/clipboard)) && usr.stat == CONSCIOUS)) + if(((loc == usr || istype(loc, /obj/item/clipboard)) && !IS_UNCONSCIOUS_OR_CRIT(usr))) name = "paper[(n_name ? "- '[n_name]'" : null)]" add_fingerprint(usr) update_static_data() @@ -509,6 +509,8 @@ GAME_VERB_SRC(/obj/item/paper, rename, usr, "Rename paper", null) ) /obj/item/paper/ui_interact(mob/user, datum/tgui/ui) + if(!user.client) //bro stop trying to open UI on AI man ur gonna drive me nuts man comeon man + return if(resistance_flags & ON_FIRE) return ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/modules/paperwork/paper_premade.dm b/code/modules/paperwork/paper_premade.dm index fee1cf9fa40a..b7e9cce866c8 100644 --- a/code/modules/paperwork/paper_premade.dm +++ b/code/modules/paperwork/paper_premade.dm @@ -124,7 +124,7 @@

    3. Construction

    1. Build three machine frames. 2. Determine the desired direction you want your artillery to face. - 3. Leave six spaces in front, four behind. The artillery cannon finishes itself after the assemblies are aligned. + 3. Leave five spaces in front, three behind. The artillery cannon finishes itself after the assemblies are aligned. 4. Place the machine frames in a horizontal line. 5. Wrench the three machine frames in place. 6. Install wires in the three machine frames. @@ -136,7 +136,7 @@ 12. Use a screwdriver to fasten the board, install wires and two glass sheets, use screwdriver again to turn on. 13. Use a multitool and interact with the generator, then the fusor. 14. Use a multitool again to interact with the bore, then the fusor. - 15. Use the console to finalize construction, make sure there's enough empty space around the BSA for the assembly to commence. + 15. Use the console to finalize construction. If the console doesn't display any error messages, a faint green hologram will indicate the BSA's position. Reposition if needed. And with that, you are done! Commencing firing protocols requires the access of at least two heads, the console will be locked down until this requirement is met. Always make sure you are certain before attempting to fire the BSA. diff --git a/code/modules/photography/camera/silicon_camera.dm b/code/modules/photography/camera/silicon_camera.dm index b85d71b487fe..e47fa735c1f2 100644 --- a/code/modules/photography/camera/silicon_camera.dm +++ b/code/modules/photography/camera/silicon_camera.dm @@ -8,7 +8,7 @@ /// Checks if we can take a picture at this moment. Returns TRUE if we can, FALSE if we can't. /obj/item/camera/siliconcam/proc/can_take_picture(mob/living/silicon/clicker) - if(clicker.stat != CONSCIOUS || clicker.incapacitated) + if(IS_UNCONSCIOUS_OR_CRIT(clicker) || clicker.incapacitated) return FALSE return TRUE diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm index 4a1647abebe7..65d550e5dd7c 100644 --- a/code/modules/photography/photos/album.dm +++ b/code/modules/photography/photos/album.dm @@ -19,6 +19,9 @@ . = ..() if (!SSpersistence.initialized) LAZYADD(SSpersistence.queued_photo_albums, src) + else if (!isnull(persistence_id)) + // Albums might spawn after roundstart (e.g. locker lazyload) + persistence_load() /obj/item/storage/photo_album/Destroy() LAZYREMOVE(SSpersistence.queued_photo_albums, src) @@ -85,41 +88,73 @@ icon_state = "album_blue" persistence_id = "HoS" +/obj/item/storage/photo_album/hos/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/rd name = "photo album (Research Director)" icon_state = "album_blue" persistence_id = "RD" +/obj/item/storage/photo_album/rd/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/hop name = "photo album (Head of Personnel)" icon_state = "album_blue" persistence_id = "HoP" +/obj/item/storage/photo_album/hop/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/captain name = "photo album (Captain)" icon_state = "album_blue" persistence_id = "Captain" +/obj/item/storage/photo_album/captain/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/cmo name = "photo album (Chief Medical Officer)" icon_state = "album_blue" persistence_id = "CMO" +/obj/item/storage/photo_album/cmo/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/qm name = "photo album (Quartermaster)" icon_state = "album_blue" persistence_id = "QM" +/obj/item/storage/photo_album/qm/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/ce name = "photo album (Chief Engineer)" icon_state = "album_blue" persistence_id = "CE" +/obj/item/storage/photo_album/ce/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/bar name = "photo album (Bar)" icon_state = "album_blue" persistence_id = "bar" +/obj/item/storage/photo_album/bar/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/syndicate name = "photo album (Syndicate)" icon_state = "album_red" @@ -130,11 +165,19 @@ icon_state = "album_blue" persistence_id = "library" +/obj/item/storage/photo_album/library/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/chapel name = "photo album (Chapel)" icon_state = "album_blue" persistence_id = "chapel" +/obj/item/storage/photo_album/chapel/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/listeningstation name = "photo album (Listening Station)" icon_state = "album_red" @@ -150,6 +193,10 @@ icon_state = "album_blue" persistence_id = "prison" +/obj/item/storage/photo_album/prison/Initialize(mapload) + . = ..() + REGISTER_REQUIRED_MAP_ITEM(1, 1) + /obj/item/storage/photo_album/personal icon_state = "album_green" diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index 6f981e5fb47a..b9f46447b408 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -55,7 +55,7 @@ if(!seen) P.mobs_seen -= seen_ref continue - if(!isobserver(seen) && !isspirit(seen)) + if(!HAS_TRAIT(seen, TRAIT_GHOSTLY_MOB)) continue set_custom_materials(list(/datum/material/hauntium =SHEET_MATERIAL_AMOUNT)) break @@ -116,7 +116,7 @@ GAME_VERB_SRC(/obj/item/photo, rename, usr, "Rename photo", null) var/n_name = tgui_input_text(usr, "What would you like to label the photo?", "Photo Labelling", max_length = MAX_NAME_LEN) //loc.loc check is for making possible renaming photos in clipboards - if(n_name && (loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && !usr.incapacitated) + if(n_name && (loc == usr || loc.loc && loc.loc == usr) && !IS_UNCONSCIOUS_OR_CRIT(usr) && !usr.incapacitated) name = "photo[(n_name ? "- '[n_name]'" : null)]" add_fingerprint(usr) diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm index 8e8875b3321a..359b79805b7b 100644 --- a/code/modules/point/point.dm +++ b/code/modules/point/point.dm @@ -106,7 +106,7 @@ * * overridden here and in /mob/dead/observer for different point span classes and sanity checks */ -GAME_VERB(/mob, pointed, "Point To", null, atom/A as mob|obj|turf in view(client.view, src)) +GAME_VERB(/mob, pointed, "Point To", null, atom/A as mob|obj|turf) if(isnull(A) || istype(A, /obj/effect/temp_visual/point) || isarea(A)) return FALSE diff --git a/code/modules/power/apc/apc_malf.dm b/code/modules/power/apc/apc_malf.dm index 8cdcc3584590..7da582ebfe8e 100644 --- a/code/modules/power/apc/apc_malf.dm +++ b/code/modules/power/apc/apc_malf.dm @@ -102,7 +102,7 @@ if(transfer_in_progress) to_chat(user, span_warning("There's already a transfer in progress!")) return FALSE - if(interaction != AI_TRANS_TO_CARD || occupier.stat) + if(interaction != AI_TRANS_TO_CARD || IS_UNCONSCIOUS_OR_CRIT(occupier)) return FALSE var/turf/user_turf = get_turf(user) if(!user_turf) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 5b75b3d7567c..a2ea753f07b5 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -651,7 +651,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list( /obj/item/stack/cable_coil/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE) var/mob/living/carbon/human/attacked_humanoid = interacting_with var/obj/item/clothing/under/uniform = attacked_humanoid.w_uniform - if(uniform?.repair_sensors(user)) + if(!istype(uniform) || uniform.repair_sensors(user)) return ITEM_INTERACT_SUCCESS var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected)) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index a59fc7918e48..02ae9239808a 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -524,13 +524,13 @@ // if a light is turned off, it won't activate emergency power /obj/machinery/light/proc/turned_off() var/area/local_area = get_room_area() - return !local_area.lightswitch && local_area.power_light || flickering + return (local_area && !local_area.lightswitch && local_area.power_light) || flickering // returns whether this light has power // true if area has power and lightswitch is on /obj/machinery/light/proc/has_power() var/area/local_area = get_room_area() - return local_area.lightswitch && local_area.power_light + return local_area && local_area.lightswitch && local_area.power_light // returns whether this light has emergency power // can also return if it has access to a certain amount of that power diff --git a/code/modules/power/lighting/light_items.dm b/code/modules/power/lighting/light_items.dm index dd48495606dd..84449e5bc4e2 100644 --- a/code/modules/power/lighting/light_items.dm +++ b/code/modules/power/lighting/light_items.dm @@ -56,6 +56,7 @@ desc = "A replacement light tube." icon_state = "ltube" base_state = "ltube" + worn_icon = "ltube" inhand_icon_state = "ltube" icon_angle = -45 brightness = 8 @@ -79,6 +80,7 @@ icon_state = "lbulb" base_state = "lbulb" icon_angle = -90 + worn_icon = "lbulb" inhand_icon_state = "contvapour" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 8e8e6c9e754c..b8ea12b6f523 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -170,7 +170,7 @@ /// Stun people around Nar'Sie that aren't cultists /obj/narsie/proc/mesmerize() for (var/mob/living/carbon/victim in viewers(NARSIE_CONSUME_RANGE, src)) - if (victim.stat == CONSCIOUS) + if (!IS_UNCONSCIOUS_OR_CRIT(victim)) if (!IS_CULTIST(victim)) to_chat(victim, span_cult("You feel conscious thought crumble away in an instant as you gaze upon [src]...")) victim.apply_effect(NARSIE_MESMERIZE_EFFECT, EFFECT_STUN) diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm index 355ce2cb530a..fd8e136e0179 100644 --- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm +++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm @@ -160,7 +160,7 @@ ///puts a round into the magazine /obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/new_round, replace_spent = 0) // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. - if(!new_round || !(caliber ? (caliber == new_round.caliber) : (ammo_type == new_round.type))) + if(!is_compatible_round(new_round)) return FALSE if (stored_ammo.len < max_ammo) @@ -189,6 +189,11 @@ return TRUE return FALSE +/obj/item/ammo_box/proc/is_compatible_round(obj/item/ammo_casing/new_round) + if(!new_round || !(caliber ? (caliber == new_round.caliber) : (ammo_type == new_round.type))) + return FALSE + return TRUE + ///Whether or not the box can be loaded, used in overrides /obj/item/ammo_box/proc/can_load(mob/user) return TRUE diff --git a/code/modules/projectiles/boxes_magazines/external/pistol.dm b/code/modules/projectiles/boxes_magazines/external/pistol.dm index 27c3d39f6b3c..719cb9868b28 100644 --- a/code/modules/projectiles/boxes_magazines/external/pistol.dm +++ b/code/modules/projectiles/boxes_magazines/external/pistol.dm @@ -124,7 +124,7 @@ icon_state = "r45-8" base_icon_state = "r45" ammo_type = /obj/item/ammo_casing/c45/reaper - caliber = CALIBER_10MM + caliber = CALIBER_45 max_ammo = 8 multiple_sprites = AMMO_BOX_PER_BULLET multiple_sprite_use_base = TRUE diff --git a/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/code/modules/projectiles/boxes_magazines/external/rechargable.dm index e0c8d53d4bd0..e469759b367c 100644 --- a/code/modules/projectiles/boxes_magazines/external/rechargable.dm +++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm @@ -9,7 +9,7 @@ /obj/item/ammo_box/magazine/recharge/update_desc() . = ..() - desc = "[initial(desc)] It has [stored_ammo.len] shot\s left." + desc = "[initial(desc)] It has [length(stored_ammo)] shot\s left." /obj/item/ammo_box/magazine/recharge/update_icon_state() . = ..() diff --git a/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/code/modules/projectiles/boxes_magazines/internal/rifle.dm index e047c8978f8a..41ec8fc124b2 100644 --- a/code/modules/projectiles/boxes_magazines/internal/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/internal/rifle.dm @@ -17,15 +17,24 @@ ammo_type = /obj/item/ammo_casing/junk max_ammo = 1 +/obj/item/ammo_box/magazine/internal/boltaction/pipegun/empty + start_empty = TRUE + /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol name = "pipe pistol internal magazine" max_ammo = 3 +/obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol/empty + start_empty = TRUE + /obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime name = "regal pipegun internal magazine" max_ammo = 4 ammo_type = /obj/item/ammo_casing/junk/reaper +/obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime/empty + start_empty = TRUE + /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol/prime name = "regal pipe pistol internal magazine" max_ammo = 6 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7419cce5c81f..6cf3ec0adb4e 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -199,7 +199,7 @@ if(pin.pin_removable) . += span_info("[pin] looks like [pin.p_they()] could be removed with some tools.") else - . += span_info("[pin] looks like [pin.p_theyre()] firmly locked in, [pin.p_they()] looks impossible to remove.") + . += span_info("[pin] looks like [pin.p_theyre()] firmly locked in, [pin.p_they()] look[pin.p_s()] impossible to remove.") else . += "It doesn't have a firing pin installed, and won't fire." @@ -303,7 +303,7 @@ if(!isliving(loc)) return ..() var/mob/living/holder = loc - if(holder.is_holding(src) && holder.stat < UNCONSCIOUS) + if(holder.is_holding(src) && !IS_UNCONSCIOUS(holder)) to_chat(holder, span_boldwarning("[src] breaks down!")) holder.playsound_local(get_turf(src), 'sound/items/weapons/smash.ogg', 50, TRUE) return ..() diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index de7ec45ff6b0..d5dece1759e5 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -180,15 +180,51 @@ update_appearance() RegisterSignal(src, COMSIG_ITEM_RECHARGED, PROC_REF(instant_reload)) -/obj/item/gun/ballistic/Destroy() - QDEL_NULL(magazine) - QDEL_NULL(suppressor) - return ..() +/obj/item/gun/ballistic/on_craft_completion(list/components, datum/crafting_recipe/current_recipe, atom/crafter) + . = ..() + var/replace_chamber = TRUE + var/replace_magazine = !magazine || !(magazine.item_flags & ABSTRACT) //don't replace abstract magazines + for(var/obj/item/gun/ballistic/gun in components) + if(gun.magazine?.item_flags & ABSTRACT) //we cannot insert an internal magazine into the new gun, so we insert the individual casings instead. + for(var/i in 1 to length(gun.magazine.stored_ammo)) + var/obj/item/ammo_casing/round = gun.magazine.get_round() + if(!magazine.give_round(round)) + round.forceMove(drop_location()) + else if(gun.magazine && istype(gun.magazine, accepted_magazine_type)) //insert the new magazine into the gun + var/obj/item/ammo_box/magazine/new_magazine = gun.magazine //hold onto the reference since magazine is set to null once ejected + qdel(magazine) + new_magazine.forceMove(src) + magazine = new_magazine + replace_magazine = FALSE + else if(gun.magazine) //the magazine cannot be replaced + gun.magazine.forceMove(drop_location()) //drop the magazine on the floor so it doesn't get deleted alongside the gun components. + else if(replace_magazine && istype(gun.accepted_magazine_type, accepted_magazine_type)) //the gun we used for crafting lacked a magazine so this one should as well + qdel(magazine) + + if(!gun.chambered) + continue + + var/obj/item/ammo_casing/round = gun.chambered //hold onto the reference since chambered is set to null once the casing is ejected + if(!magazine?.is_compatible_round(round) || !replace_chamber) + round.forceMove(drop_location()) + continue + qdel(chambered) //nulled when moved to null + round.forceMove(src) + chambered = round + replace_chamber = FALSE + + update_appearance() /obj/item/gun/ballistic/Exited(atom/movable/gone, direction) . = ..() if(gone == suppressor) clear_suppressor() + if(gone == magazine) + if(!QDELETED(magazine)) + magazine.update_appearance() + magazine = null + if(!QDELETED(src)) + update_appearance() /obj/item/gun/ballistic/add_weapon_description() AddElement(/datum/element/weapon_description, attached_proc = PROC_REF(add_notes_ballistic)) @@ -507,21 +543,16 @@ playsound(src, eject_sound, eject_sound_volume, eject_sound_vary) else playsound(src, eject_empty_sound, eject_sound_volume, eject_sound_vary) - magazine.forceMove(drop_location()) var/obj/item/ammo_box/magazine/old_mag = magazine + magazine.forceMove(drop_location()) if (tac_load) if (insert_magazine(user, tac_load, FALSE)) balloon_alert(user, "[magazine_wording] swapped") else to_chat(user, span_warning("You dropped the old [magazine_wording], but the new one doesn't fit. How embarassing.")) - magazine = null - else - magazine = null user.put_in_hands(old_mag) - old_mag.update_appearance() if (display_message) balloon_alert(user, "[magazine_wording] unloaded") - update_appearance() /obj/item/gun/ballistic/can_shoot() return chambered?.loaded_projectile @@ -657,8 +688,6 @@ update_appearance() /obj/item/gun/ballistic/clear_suppressor() - if(!can_unsuppress) - return suppressed = SUPPRESSED_NONE if(suppressor) update_weight_class(w_class - suppressor.w_class) @@ -927,7 +956,6 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list( if(!internal_magazine && magazine) //if a magazine is attached to the weapon, we remove it and throw it aside magazine.forceMove(drop_location()) magazine.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), 1, 1) - magazine = null update_icon() //updating the sprite of weapons without a magazine if(!isnull(chambered)) //if there is a cartridge in the chamber, we remove it rack() diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 1dd4039a57c4..91b0ea8696d3 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -216,7 +216,10 @@ accepted_magazine_type = /obj/item/ammo_box/magazine/r45 actions_types = list(/datum/action/item_action/toggle_firemode) obj_flags = UNIQUE_RENAME // if you did the sidequest, you get the customization - custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 30, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 25, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 11.5, /datum/material/telecrystal = SHEET_MATERIAL_AMOUNT * 4) + custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 20, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 15, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/telecrystal = SHEET_MATERIAL_AMOUNT * 4) + +/obj/item/gun/ballistic/automatic/pistol/deagle/regal/no_mag + spawnwithmagazine = FALSE /obj/item/gun/ballistic/automatic/pistol/deagle/regal/add_deep_lore() AddElement(/datum/element/examine_lore, \ diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 92d9c9fd5f3e..c9399a6620a8 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -61,7 +61,7 @@ GAME_VERB(/obj/item/gun/ballistic/revolver, spin, "Spin Chamber", null) spin_chamber(usr) /obj/item/gun/ballistic/revolver/verb/spin_chamber(mob/living/user) - if(!istype(user) || user.stat || !in_range(user, src)) + if(!istype(user) || IS_UNCONSCIOUS_OR_CRIT(user) || !in_range(user, src)) return if (recent_spin > world.time) @@ -360,7 +360,7 @@ GAME_VERB(/obj/item/gun/ballistic/revolver, spin, "Spin Chamber", null) user.visible_message( span_danger("[user][is_target_face ? "": " cowardly"] aims \the [src] at [user.p_their()] [aimed_at_readable] as it goes off!"), span_danger("You[is_target_face ? "": " cowardly"] aim \the [src] at your [aimed_at_readable] as it goes off![user.stat >= HARD_CRIT ? " Everything suddenly goes black." : ""]"), - span_hear("You hear a grunt[user.stat == CONSCIOUS ? "" : ", followed by a thud"]!"), + span_hear("You hear a grunt[!IS_UNCONSCIOUS_OR_CRIT(user) ? "" : ", followed by a thud"]!"), vision_distance = COMBAT_MESSAGE_RANGE, visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, ) diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm index 760437dd0e55..76400c7ffac2 100644 --- a/code/modules/projectiles/guns/ballistic/rifle.dm +++ b/code/modules/projectiles/guns/ballistic/rifle.dm @@ -52,11 +52,10 @@ /obj/item/gun/ballistic/rifle/boltaction name = "\improper Sakhno Precision Rifle" - desc = "A Sakhno Precision Rifle, a bolt action weapon that was (and certainly still is) popular with \ - frontiersmen, cargo runners, private security forces, explorers, and other unsavoury types. This particular \ - pattern of the rifle dates back all the way to 2440." - sawn_desc = "A sawn-off Sakhno Precision Rifle, popularly known as an \"Obrez\". \ - There was probably a reason it wasn't manufactured this short to begin with. \ + desc = "A bolt-action rifle that was - and certainly still is - popular with \ + frontiersmen, cargo runners, private security forces, explorers, and other unsavoury types." + sawn_desc = "A sawn-off bolt-action rifle, popularly known as an \"Obrez\". \ + There's probably a reason it wasn't manufactured this short to begin with. \ Despite the terrible nature of the modification, the weapon seems otherwise in good condition." icon_state = "sakhno" @@ -76,6 +75,23 @@ SET_BASE_PIXEL(-8, 0) +/obj/item/gun/ballistic/rifle/boltaction/add_deep_lore() + AddElement(/datum/element/examine_lore, \ + lore = "The Sakhno Precision Rifle's origins are closely tied to the Sakhno Concern, one of the Third Soviet Union's \ + state-ran weapons manufacturing concerns before the Union's collapse in the February Revolution, and subsequently \ + one of the Spinward Stellar Coalition's favored industrial concerns.
    \ +
    \ + Designed to be easily manufactured with the tooling and equipment that the nascent TSU had on-hand, \ + and with enough reliability and stopping power for all sorts of purposes, from hunting to home defense, \ + the Sakhno became a mainstay for Soviet citizens since 2440... \ + and, subsequently, a mainstay when those citizens became conspirators and combatants against dictatorship.
    \ +
    \ + Even to this day, though, Sakhnos in various conditions and states of modification end up all around the Spinward Sector. \ + Many are manufactured or refurbished with new internals, with reliability to match their history, \ + while others are from stockpiles of surplus or salvage, scavenged and refurbished time and time again... \ + which typically isn't great for reliability." \ + ) + /obj/item/gun/ballistic/rifle/boltaction/add_bayonet_point() AddComponent(/datum/component/bayonet_attachable, offset_x = 41, offset_y = 14, bayonet_overlay = "bayonet_thin") @@ -115,7 +131,7 @@ /obj/item/gun/ballistic/rifle/boltaction/harpoon name = "ballistic harpoon gun" - desc = "A weapon favored by carp hunters, but just as infamously employed by agents of the Animal Rights Consortium against human aggressors. Because it's ironic." + desc = "A weapon favored by carp hunters, but just as infamously employed by agents of the Animal Rights Consortium against human aggressors, because it's ironic." icon = 'icons/obj/weapons/guns/ballistic.dmi' icon_state = "speargun" inhand_icon_state = "speargun" @@ -126,16 +142,29 @@ SET_BASE_PIXEL(0, 0) +/obj/item/gun/ballistic/rifle/boltaction/harpoon/add_deep_lore() + AddElement(/datum/element/examine_lore, \ + lore = "Breech-loaded spearguns aren't a common sight around Nanotrasen stations, because \ + everything you could reasonably need a speargun for, like carp hunting, is better served \ + by more available options, such as laser guns or toolboxes.
    \ +
    \ + However, the Animal Rights Consortium intentionally sends some \"lucky\" operatives \ + out with such weapons, as their uncommon nature makes it very unambiguous who sent the madmen \ + who willingly use such weapons, and what drives them. \ + Or, equally as likely, some other cell of the Syndicate pilfered it to paint the Consortium as \ + responsible for whatever attack could be committed with a speargun.
    \ +
    \ + Either way, having a ballistic harpoon stuck in a limb is widely regarded as being a drag." \ + ) + /obj/item/gun/ballistic/rifle/boltaction/surplus name = "\improper Sakhno M2442 Army" - desc = "A modification of the Sakhno Precision Rifle, \"Sakhno M2442 Army\" is stamped into the side. \ - It is unknown what army this pattern of rifle was made for or if it was ever even used by an army \ - of any sort. What you can discern, however, is that its previous owner did not treat the weapon well. \ + desc = "A bolt-action rifle with \"Sakhno M2442 Army\" stamped into the side. \ + It's unknown what army this rifle was made for, or if it was ever used by one, but it's obvious the previous owner did not treat it well. \ For some reason, there's moisture all through the internals." - sawn_desc = "A sawn-off Sakhno Precision Rifle, popularly known as an \"Obrez\". \ - \"Sakhno M2442 Army\" is stamped into the side of it. \ - There was probably a reason it wasn't manufactured this short to begin with. \ - Cutting the weapon down seems to have not helped with the moisture problem." + sawn_desc = "A sawn-off bolt-action rifle, popularly known as an \"Obrez\", with \"Sakhno M2442 Army\" stamped into the side. \ + There's probably a reason it wasn't manufactured this short to begin with. \ + Cutting the weapon down has not helped with the moisture problem." icon_state = "sakhno_tactifucked" inhand_icon_state = "slopno" worn_icon_state = "slopno" @@ -149,7 +178,7 @@ name = "\improper Sakhno-Zhihao Sporting Rifle" desc = "An upgrade and modernisation of the original Sakhno rifle, made with such wonders as \ modern materials, a scope, and other impressive technological advancements that, to be honest, \ - were already around when the original weapon was designed. Surprisingly for a rifle of this type, \ + were already around when the original weapon was designed. Surprisingly, for a rifle of this type, \ the scope actually has magnification, rather than being decorative." icon_state = "zhihao" inhand_icon_state = "zhihao" @@ -157,8 +186,8 @@ can_be_sawn_off = TRUE sawn_desc = "A sawn-off Sakhno-Zhihao Sporting Rifle... Doing this was a sin, I hope you're happy. \ You are now probably one of the few people in the universe to ever hold an \"Obrez Moderna\". \ - All you had to do was take an allen wrench to the stock to take it off. But no, you just had to \ - go for the saw." + All you had to do was take an allen wrench to the stock to take it off, but no - \ + you just had to go for the saw." accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/phasic /obj/item/gun/ballistic/rifle/boltaction/prime/Initialize(mapload) @@ -172,7 +201,9 @@ /obj/item/gun/ballistic/rifle/boltaction/donkrifle name = "\improper Donk Co. Jezail" - desc = "A mass-manufactured bolt-action sporting rifle with a distinctively long barrel. Powerful enough to take down a space bear from a thousand paces. The lengthened barrel gives it good accuracy and power, even at range." + desc = "A mass-manufactured bolt-action sporting rifle with a distinctively long barrel. \ + Powerful enough to take down a space bear from a thousand paces. \ + The lengthened barrel gives it good accuracy and power, even at range." w_class = WEIGHT_CLASS_HUGE lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' @@ -183,7 +214,21 @@ worn_icon_state = "jezail" accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/jezail can_be_sawn_off = TRUE - sawn_desc = "A mass-manufactured bolt-action sporting rifle with a distinctively long barrel. Powerful enough to take down a space bear from a thousand paces. Its barrel has been cut off, so its power and accuracy have been impaired." + sawn_desc = "A mass-manufactured bolt-action sporting rifle with a formerly-distinctively long barrel. \ + Powerful enough to take down a space bear from... less than a thousand paces. \ + Its barrel has been cut off, so its power and accuracy have been impaired." + +/obj/item/gun/ballistic/rifle/boltaction/donkrifle/add_deep_lore() + AddElement(/datum/element/examine_lore, \ + lore = "Donk Co.'s Jezail line of bolt-action sporting rifles is, admittedly, a strange, almost archaic design, \ + coming from an even stranger place, as most people would not expect a logistics and food processing company to \ + create, market, and distribute bolt-action rifles.
    \ +
    \ + For better or for worse, though, they're pretty good as rifles - \ + though they're not nearly as ubiquitous as the Sakhno, especially in the Spinward Sector, \ + they still share the same chambering in .310 Strilka, and thus, the stopping power of the cartridge, with \ + no risk of receiving a refurbished, poorly-kept surplus arm that jams more than it shoots." \ + ) /obj/item/gun/ballistic/rifle/boltaction/donkrifle/sawoff(mob/user) //the heavy price one pays for fitting this in a backpack . = ..() @@ -193,10 +238,9 @@ /obj/item/gun/ballistic/rifle/rebarxbow name = "heated rebar crossbow" - desc = "A handcrafted crossbow. \ - Aside from conventional sharpened iron rods, it can also fire specialty ammo made from the atmos crystalizer - zaukerite, metallic hydrogen, and healium rods all work. \ - A makeshift Quiver can be made with an oxygen tank, wire, and a welder.\ - Very slow to reload - you can craft the crossbow with a crowbar to loosen the crossbar, but risk a misfire, or worse..." + desc = "A handcrafted crossbow. Aside from conventional sharpened iron rods, it can also fire specialty rods made from crystallized gases. \ + Very slow to reload, and probably worth pairing with a quiver. \ + Further modifications for increased capacity are... possible, if unwise." icon = 'icons/obj/weapons/guns/ballistic.dmi' icon_state = "rebarxbow" inhand_icon_state = "rebarxbow" @@ -270,7 +314,7 @@ /obj/item/gun/ballistic/rifle/rebarxbow/forced name = "stressed rebar crossbow" - desc = "Some idiot decided that they would risk shooting themselves in the face if it meant they could rack and reload the crossbow faster. Hopefully, it was worth it." + desc = "Some idiot decided that they would risk shooting themselves in the face if it meant they could rack and reload the crossbow faster. Hopefully it was worth it." // Feel free to add a recipe to allow you to change it back if you would like, I just wasn't sure if you could have two recipes for the same thing. can_misfire = TRUE draw_time = 0.5 SECONDS @@ -281,8 +325,9 @@ /obj/item/gun/ballistic/rifle/rebarxbow/syndie name = "syndicate rebar crossbow" - desc = "The syndicate liked the bootleg rebar crossbow NT engineers made, so they showed what it could be if properly developed. \ - auto reloads from backpack quiver when racking." + desc = "The Syndicate liked the bootleg rebar crossbow NT engineers made, and decided to one-up them by properly manufacturing a rebar \ + crossbow that's significantly less prone to maiming the user. \ + Automatically reloads from a backpack quiver when pulling the string." icon_state = "rebarxbowsyndie" inhand_icon_state = "rebarxbowsyndie" worn_icon_state = "rebarxbowsyndie" @@ -317,19 +362,18 @@ SET_BASE_PIXEL(-8, 0) -/obj/item/gun/ballistic/rifle/boltaction/pipegun/Initialize(mapload) - . = ..() +/obj/item/gun/ballistic/rifle/boltaction/pipegun/add_deep_lore() AddElement(/datum/element/examine_lore, \ lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to recall a tale about [src]."), \ lore = "You were told this story, in hushed tones, from a wizened man in a grey jumpsuit...

    \ - It is said that the first slaying committed on a Nanotrasen space station was by an assistant.

    \ - That this act, done by toolbox, maybe spear, was what consigned their kind to a life of destitution, rejection and violence.

    \ - They carry the weight of this act visibly; the grey jumpsuit. Breathing deeply filtered air. And with bloodsoaked yellow hands clenched into fists.

    \ - Eyes, sharp and waiting. Hunters in the dark.

    \ - Eventually, these killing spirits sought to stake a claim on the metal tombs they were trapped within. Rejecting their status. Determined to be something more.

    \ - This weapon is one such tool. And it is a grim one indeed. Wrought from scrap, pulled from the station's walls and floors and the very nails holding it together.
    \ -
    \ - It is a symbol that the true masters of this place are not those who merely inhabit it. But the one willing to twist it towards a killing intent." \ + It is said that the first slaying committed on a Nanotrasen space station was by an assistant.

    \ + That this act, done by toolbox, maybe spear, was what consigned their kind to a life of destitution, rejection and violence.

    \ + They carry the weight of this act visibly; the grey jumpsuit. Breathing deeply filtered air. And with bloodsoaked yellow hands clenched into fists.

    \ + Eyes, sharp and waiting. Hunters in the dark.

    \ + Eventually, these killing spirits sought to stake a claim on the metal tombs they were trapped within. Rejecting their status. Determined to be something more.

    \ + This weapon is one such tool. And it is a grim one indeed - wrought from scrap, pulled from the station's walls and floors and the very nails holding it together.
    \ +
    \ + It is a symbol that the true masters of this place are not those who merely inhabit it, but the one willing to twist it towards a killing intent." \ ) /obj/item/gun/ballistic/rifle/boltaction/pipegun/add_bayonet_point() @@ -339,6 +383,9 @@ . = ..() do_sparks(1, TRUE, src) +/obj/item/gun/ballistic/rifle/boltaction/pipegun/empty + spawn_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/empty + /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol name = "pipe pistol" desc = "It is foolish to think that anyone wearing the grey is incapable of hurting you, simply because they are not baring their teeth." @@ -362,9 +409,12 @@ /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/add_bayonet_point() return +/obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/empty + spawn_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol/empty + /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime name = "regal pipegun" - desc = "To call this 'regal' is a cruel irony. For the only noteworthy quality of nobility is in how it is wielded to kill. \ + desc = "To call this 'regal' is a cruel irony, for the only noteworthy quality of nobility is in how it is wielded to kill. \ All monarchs deserve to be crowned. But none will remember the dead tyrant for the red stain they left on the carpet." icon_state = "regal_pipegun" inhand_icon_state = "regal_pipegun" @@ -380,6 +430,9 @@ /datum/material/plastic = SMALL_MATERIAL_AMOUNT * 3, ) +/obj/item/gun/ballistic/rifle/boltaction/pipegun/prime/empty + spawn_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime/empty + /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/prime name = "regal pipe pistol" desc = "What value is there in honesty towards the dishonest? So that they might twist the arm and slit the wrist? \ @@ -435,9 +488,9 @@ /obj/item/gun/ballistic/rifle/sniper_rifle name = "anti-materiel sniper rifle" - desc = "A boltaction anti-materiel rifle, utilizing .50 BMG cartridges. While technically outdated in modern arms markets, it still works exceptionally well as \ - an anti-personnel rifle. In particular, the employment of modern armored MODsuits utilizing advanced armor plating has given this weapon a new home on the battlefield. \ - It is also able to be suppressed... somehow." + desc = "A surprisingly compact bolt-action anti-materiel rifle, utilizing .50 BMG cartridges. \ + While technically outdated in modern arms markets, it still works exceptionally well \ + in anti-personnel and light anti-armor roles. Also, somehow, capable of fitting a suppressor." icon = 'icons/obj/weapons/guns/ballistic.dmi' icon_state = "sniper" lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' @@ -473,6 +526,21 @@ . = ..() AddComponent(/datum/component/scope, range_modifier = 4) //enough range to at least make extremely good use of the penetrator rounds +/obj/item/gun/ballistic/rifle/sniper_rifle/add_deep_lore() + AddElement(/datum/element/examine_lore, \ + lore = "Modern arms and armor technology has come a long way since man's first strides into space, \ + from high-throughput directed energy weaponry to advanced energy shielding and everything in between. \ + Such advancements in technology, then, should have put the role of the anti-materiel rifle to rest, \ + as other solutions to the armor problems it solves would have become more accessible - in theory, anyway.
    \ +
    \ + In practice, the problem with advanced armor is that it's expensive and/or heavy - \ + both of which are constraints that limit the usage of such defensive solutions, \ + especially on smaller targets such as station-ready exosuits or human-scale MODsuits.
    \ +
    \ + These constraints gave the anti-materiel rifle concept a new lease on life, \ + which Scarborough Arms is happy to provide to prospective buyers." \ + ) + /obj/item/gun/ballistic/rifle/sniper_rifle/reset_fire_cd() . = ..() if(suppressed) @@ -481,20 +549,16 @@ playsound(src, 'sound/machines/eject.ogg', 50, TRUE) /obj/item/gun/ballistic/rifle/sniper_rifle/syndicate - desc = "A boltaction anti-materiel rifle, utilizing .50 BMG cartridges. While technically outdated in modern arms markets, it still works exceptionally well as \ - an anti-personnel rifle. In particular, the employment of modern armored MODsuits utilizing advanced armor plating has given this weapon a new home on the battlefield. \ - It is also able to be suppressed... somehow. This one seems to have a little picture of someone in a blood-red MODsuit stenciled on it, pointing at a green floppy disk. \ - Who knows what that might mean." + desc = parent_type::desc + " This one seems to have a little picture of someone in a Gorlex Marauders blood-red MODsuit stenciled on it, \ + pointing at a green floppy disk. Who knows what that might mean." pin = /obj/item/firing_pin/implant/pindicate // SKS semi-automatic rifle // /obj/item/gun/ballistic/rifle/sks name = "\improper Sakhno SKS semi-automatic rifle" - desc = "A revival of the ancient SKS semi-automatic rifle, redesigned to utilize .310 Strilka rounds. Produced to celebrate the \ - establishment of the Third Soviet Union in the Spinward Sector. In the wake of the union's collapse, these weapons now hold a \ - unique place in history amongst the populace of the sector. However, they are strangely rarer than the Sakhno M2442 Army. \ - Frontier settlers are known for owning one of these for hunting purposes. Or fighting off annoying tax collectors." + desc = "A revival of the ancient SKS semi-automatic rifle, redesigned to utilize .310 Strilka rounds. \ + Loses some per-cartridge stopping power due to quirks in the gas-operation system." icon = 'icons/obj/weapons/guns/wide_guns.dmi' icon_state = "sks" worn_icon_state = "sks" @@ -513,14 +577,27 @@ SET_BASE_PIXEL(-8, 0) +/obj/item/gun/ballistic/rifle/sks/add_deep_lore() + AddElement(/datum/element/examine_lore, \ + lore = "The Sakhno Concern's modernized reproductions of the SKS were commissioned by the \ + leadership of the Third Soviet Union to celebrate their establishment in the Spinward Sector.
    \ +
    \ + In the wake of the Union's collapse, however, these weapons now hold a unique place in history \ + amongst the populace of the sector as a reminder of what came before - and, possibly, the hope of what may come after. \ + However, both parts kits and completed rifles are, strangely, rarer than the Sakhno M2442 Army.
    \ +
    \ + Frontier settlers are known to own these for hunting purposes, or fighting off annoying tax collectors. \ + Some argue that these are the same activity." \ + ) + /obj/item/gun/ballistic/rifle/sks/add_bayonet_point() AddComponent(/datum/component/bayonet_attachable, offset_x = 38, offset_y = 12) /obj/item/gun/ballistic/rifle/sks/chekhov name = "\improper Chekhov's SKS semi-automatic rifle" - desc = "A revival of the ancient SKS semi-automatic rifle, redesigned to utilize .310 Strilka rounds. The name \ - 'Chekhov' is engraved in the side of the stock. You feel like this had some kind of significance at one point, \ - but you cannot be sure as to what that might have been. Or whether that true meaning has yet to reveal itself." + desc = parent_type::desc + " The name 'Chekhov' is engraved in the side of the stock. \ + You feel like this had some kind of significance at one point, but you cannot be sure as to what that might have been, \ + or whether that true meaning has yet to reveal itself." /obj/item/gun/ballistic/rifle/sks/empty spawn_magazine_type = /obj/item/ammo_box/magazine/internal/sks/empty @@ -528,9 +605,10 @@ // lahti-l39 anti material rifle // /obj/item/gun/ballistic/automatic/lahti - name = "\improper Lahti L-39" - desc = "The Lahti L-39, now manufactured in space with better materials making it more portable and reliable- still loaded in the same massive cartridge, \ - this thing was made to go through a tank and come out the other end- imagine what it could do to an exosuit, there's also a completely useless sight which is totally obstructed by the magazine." + name = "anti-tank rifle" + desc = "A reproduction of an ancient anti-tank rifle, manufactured in space with lighter, stronger materials, making it more portable and reliable. \ + Chambered for a concerningly large 20x138mm cartridge, which, too, has received a modern facelift, \ + making it terrifyingly effective, especially against exosuits." icon = 'icons/obj/weapons/guns/lahtil39.dmi' icon_state = "lahtil" inhand_icon_state = "sniper" diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index f8b0a4f429a7..af7acc49111b 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -103,7 +103,7 @@ var/obj/item/my_thing = pop(my_shit) user.dropItemToGround(my_thing) var/mob/living/angry_thing = my_thing.animate_atom_living() - angry_thing.ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, user) + angry_thing.ai_controller?.set_blackboard_key(BB_CURRENT_TARGET, user) angry_thing.ai_controller?.set_blackboard_key(BB_TARGET_MINIMUM_STAT, HARD_CRIT) angry_thing.ai_controller?.ai_interact(user, combat_mode = TRUE) user.apply_damage(35, BRUTE, forced = TRUE) // Mimics are not actually very strong so we pretend that it just bit us so we die faster, at least 3 charges & worn items should do it @@ -111,7 +111,7 @@ if (QDELETED(user)) return MANUAL_SUICIDE - if (user.stat == CONSCIOUS) + if (!IS_UNCONSCIOUS_OR_CRIT(user)) return SHAME if (user.stat != DEAD) user.death() // If you got put into crit by the mobs we'll finish you off diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index c2334b5c4c53..03161e7efa26 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -3,6 +3,7 @@ desc = "You shouldn't have this." ammo_type = /obj/item/ammo_casing/magic icon_state = "nothingwand" + worn_icon = null inhand_icon_state = "wand" icon_angle = -45 lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' @@ -96,7 +97,7 @@ . = ..() if (user.stat == DEAD) return MANUAL_SUICIDE - user.visible_message(span_suicide("...but if anything [user.p_they()] look healthier than before.")) + user.visible_message(span_suicide("...but if anything [user.p_they()] look[user.p_s()] healthier than before.")) return SHAME /obj/item/gun/magic/wand/death/debug @@ -140,7 +141,7 @@ . = ..() if (user.stat == DEAD) return MANUAL_SUICIDE - user.visible_message(span_suicide("...but if anything [user.p_they()] look healthier than before.")) + user.visible_message(span_suicide("...but if anything [user.p_they()] look[user.p_s()] healthier than before.")) return SHAME /obj/item/gun/magic/wand/resurrection/debug //for testing diff --git a/code/modules/projectiles/guns/magic/wands/wand_rebel.dm b/code/modules/projectiles/guns/magic/wands/wand_rebel.dm index 74cabaf9d0fb..93f164b1c544 100644 --- a/code/modules/projectiles/guns/magic/wands/wand_rebel.dm +++ b/code/modules/projectiles/guns/magic/wands/wand_rebel.dm @@ -71,5 +71,5 @@ var/mob/living/bully = animate_item.animate_atom_living(firer) if (!bully.ai_controller) return - bully.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, victim) + bully.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, victim) bully.ai_controller.ai_interact(victim, combat_mode = TRUE) diff --git a/code/modules/projectiles/guns/special/syringe_gun.dm b/code/modules/projectiles/guns/special/syringe_gun.dm index 9e440c91f1e7..021213004c22 100644 --- a/code/modules/projectiles/guns/special/syringe_gun.dm +++ b/code/modules/projectiles/guns/special/syringe_gun.dm @@ -103,22 +103,25 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/gun/syringe/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(istype(tool, /obj/item/reagent_containers/syringe/bluespace)) - balloon_alert(user, "[tool.name] is too big!") - return ITEM_INTERACT_BLOCKING - if(!istype(tool, /obj/item/reagent_containers/syringe)) return NONE + return attempt_insert_syringe(user, tool) + +/obj/item/gun/syringe/proc/attempt_insert_syringe(mob/living/user, obj/item/reagent_containers/syringe/syringe) + if(istype(syringe, /obj/item/reagent_containers/syringe/bluespace)) + balloon_alert(user, "[syringe.name] is too big!") + return ITEM_INTERACT_BLOCKING + if(syringes.len >= max_syringes) balloon_alert(user, "it's full!") return ITEM_INTERACT_BLOCKING - if(!user.transferItemToLoc(tool, src)) + if(!user.transferItemToLoc(syringe, src)) return ITEM_INTERACT_BLOCKING - balloon_alert(user, "[tool.name] loaded") - syringes += tool + balloon_alert(user, "[syringe.name] loaded") + syringes += syringe recharge_newshot() update_appearance() playsound(src, load_sound, 40) diff --git a/code/modules/projectiles/projectile/bullets/special.dm b/code/modules/projectiles/projectile/bullets/special.dm index ed14a01d281c..94a41e64e2a3 100644 --- a/code/modules/projectiles/projectile/bullets/special.dm +++ b/code/modules/projectiles/projectile/bullets/special.dm @@ -186,7 +186,7 @@ var/list/possible_victims = list() for(var/mob/living/iter_living in valid_targets) - if(HAS_TRAIT(iter_living, TRAIT_RECENTLY_COINED) || iter_living.stat != CONSCIOUS) + if(HAS_TRAIT(iter_living, TRAIT_RECENTLY_COINED) || IS_UNCONSCIOUS_OR_CRIT(iter_living)) continue if(get_dist(src, iter_living) <= 2) // prioritize close mobs diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm index ac903c751046..ab308043fe98 100644 --- a/code/modules/projectiles/projectile/energy/stun.dm +++ b/code/modules/projectiles/projectile/energy/stun.dm @@ -222,7 +222,7 @@ owner.adjust_stutter_up_to(10 SECONDS, 20 SECONDS) owner.adjust_jitter_up_to(20 SECONDS, 30 SECONDS) - if(owner.stat <= SOFT_CRIT) + if(!IS_UNCONSCIOUS(owner)) owner.do_jitter_animation(INFINITY) // maximum POWER // You are damp, that's bad when you're being tased diff --git a/code/modules/projectiles/projectile/special/meteor.dm b/code/modules/projectiles/projectile/special/meteor.dm index 1555b193ba45..426b2137a91e 100644 --- a/code/modules/projectiles/projectile/special/meteor.dm +++ b/code/modules/projectiles/projectile/special/meteor.dm @@ -31,7 +31,7 @@ SSexplosions.medturf += hit_target playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, TRUE) for(var/mob/onlookers_in_range in urange(10, src)) - if(!onlookers_in_range.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(onlookers_in_range)) shake_camera(onlookers_in_range, 3, 1) qdel(src) diff --git a/code/modules/projectiles/projectile/special/rocket.dm b/code/modules/projectiles/projectile/special/rocket.dm index fe04fc7d9865..90fca1dfa79f 100644 --- a/code/modules/projectiles/projectile/special/rocket.dm +++ b/code/modules/projectiles/projectile/special/rocket.dm @@ -29,7 +29,7 @@ var/random_crit_gib = FALSE if(isliving(target) && prob(1) && random_crits_enabled) var/mob/living/gibbed_dude = target - if(gibbed_dude.stat < HARD_CRIT) + if(!IS_UNCONSCIOUS_OR_CRIT(gibbed_dude)) gibbed_dude.say("Is that a fucking ro-", forced = "hit by rocket") random_crit_gib = TRUE ..() diff --git a/code/modules/reagents/chemistry/holder/mob_life.dm b/code/modules/reagents/chemistry/holder/mob_life.dm index b90d825c6aa6..e8c617d359e2 100644 --- a/code/modules/reagents/chemistry/holder/mob_life.dm +++ b/code/modules/reagents/chemistry/holder/mob_life.dm @@ -102,7 +102,10 @@ if(!owner || !reagent || (dead && !(reagent.chemical_flags & REAGENT_DEAD_PROCESS))) return FALSE - var/tick_return = owner.reagent_tick(reagent, seconds_per_tick) + var/metabolized_volume = reagent.compute_metabolization(owner, seconds_per_tick) + var/metabolization_ratio = REM * metabolized_volume + + var/tick_return = owner.reagent_tick(reagent, seconds_per_tick, metabolization_ratio) if(tick_return & COMSIG_MOB_STOP_REAGENT_TICK) return FALSE @@ -110,8 +113,6 @@ return FALSE var/need_mob_update = FALSE - var/metabolized_volume = reagent.compute_metabolization(owner, seconds_per_tick) - var/metabolization_ratio = REM * metabolized_volume if(reagents_metabolized) reagents_metabolized[reagent.type] = metabolization_ratio if(can_overdose && !HAS_TRAIT(owner, TRAIT_OVERDOSEIMMUNE)) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 712a96811808..4b674656b6af 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -762,6 +762,8 @@ /datum/reagent/plantnutriment/eznutriment, /datum/reagent/plantnutriment/left4zednutriment, /datum/reagent/plantnutriment/robustharvestnutriment, + /datum/reagent/plantnutriment/endurogrow, + /datum/reagent/plantnutriment/liquidearthquake, /datum/reagent/water, /datum/reagent/toxin/plantbgone, /datum/reagent/toxin/plantbgone/weedkiller, diff --git a/code/modules/reagents/chemistry/reagents/atmos_gas_reagents.dm b/code/modules/reagents/chemistry/reagents/atmos_gas_reagents.dm index 601e28e66bbe..09079533ace2 100644 --- a/code/modules/reagents/chemistry/reagents/atmos_gas_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/atmos_gas_reagents.dm @@ -119,7 +119,7 @@ /datum/reagent/pluoxium/on_mob_life(mob/living/carbon/breather, seconds_per_tick, metabolization_ratio) . = ..() - if(!HAS_TRAIT(breather, TRAIT_KNOCKEDOUT)) + if(!IS_UNCONSCIOUS(breather)) return for(var/obj/item/organ/organ_being_healed as anything in breather.organs) diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index ae3213470ba3..9b2e6a07d24d 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -33,7 +33,7 @@ var/good_kind_of_healing = FALSE var/need_mob_update = FALSE switch(affected_mob.stat) - if(CONSCIOUS) //bad + if(STABLE) //bad thou_shall_heal = max(death_is_coming/20, 3) need_mob_update += affected_mob.adjust_oxy_loss(2 * metabolization_ratio * seconds_per_tick, TRUE, required_biotype = affected_biotype) if(SOFT_CRIT) //meh convert diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index aa18369e8236..14443afe34d3 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -1544,7 +1544,7 @@ if(current_cycle > 51 && SPT_PROB(7.5, seconds_per_tick)) if(!drinker.undergoing_cardiac_arrest() && drinker.can_heartattack()) drinker.set_heartattack(TRUE) - if(drinker.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(drinker)) drinker.visible_message(span_userdanger("[drinker] clutches at [drinker.p_their()] chest as if [drinker.p_their()] heart stopped!")) if(need_mob_update) return UPDATE_MOB_HEALTH @@ -1763,7 +1763,7 @@ if(need_mob_update) drinker.updatehealth() drinker.visible_message(span_warning("[drinker] shivers with renewed vigor!"), span_notice("One taste of [LOWER_TEXT(name)] fills you with energy!")) - if(!drinker.stat && heal_points == 20) //brought us out of softcrit + if(!IS_UNCONSCIOUS_OR_CRIT(drinker) && heal_points == 20) //brought us out of softcrit drinker.visible_message(span_danger("[drinker] lurches to [drinker.p_their()] feet!"), span_boldnotice("Up and at 'em, kid.")) /datum/reagent/consumable/ethanol/bastion_bourbon/on_mob_life(mob/living/drinker, seconds_per_tick, metabolization_ratio) @@ -2545,7 +2545,7 @@ /datum/reagent/consumable/ethanol/protein_blend/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, metabolization_ratio) . = ..() drinker.adjust_nutrition(2 * metabolization_ratio * seconds_per_tick) - if(!islizard(drinker)) + if(!HAS_TRAIT(drinker, TRAIT_LIZARD_METABOLISM)) drinker.adjust_disgust(5 * metabolization_ratio * seconds_per_tick) else drinker.adjust_disgust(2 * metabolization_ratio * seconds_per_tick) @@ -2572,7 +2572,7 @@ /datum/reagent/consumable/ethanol/triumphal_arch/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, metabolization_ratio) . = ..() - if(islizard(drinker)) + if(HAS_TRAIT(drinker, TRAIT_LIZARD_METABOLISM)) drinker.add_mood_event("triumph", /datum/mood_event/memories_of_home, name) /datum/reagent/consumable/ethanol/the_juice diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index c5aed896bf0c..4745c6fdb578 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -1219,7 +1219,7 @@ /datum/reagent/consumable/mushroom_tea/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(islizard(affected_mob)) + if(HAS_TRAIT(affected_mob, TRAIT_LIZARD_METABOLISM)) if(affected_mob.adjust_oxy_loss(-0.25 * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)) return UPDATE_MOB_HEALTH diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 912d068cf021..0bbc5167dfab 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -583,7 +583,7 @@ psychonaut.apply_status_effect(/datum/status_effect/tower_of_babel) /datum/reagent/drug/blastoff - name = "bLaStOoF" + name = "bLaSToFF" description = "A drug for the hardcore party crowd said to enhance one's abilities on the dance floor.\nMost old heads refuse to touch this stuff, perhaps because memories of the luna discotheque incident are seared into their brains." color = "#9015a9" taste_description = "holodisk cleaner" @@ -849,7 +849,7 @@ If you have at over 25u in your body you restore more than 20 stamina per cycle, kronkaine_fiend.remove_actionspeed_modifier(/datum/actionspeed_modifier/kronkaine) kronkaine_fiend.sound_environment_override = NONE //Stop the rapid heartneats, we make sure we are not in crit as to not mess with the heartbeats from organ/heart. - if(!kronkaine_fiend.stat) + if(!IS_UNCONSCIOUS_OR_CRIT(kronkaine_fiend)) kronkaine_fiend.stop_sound_channel(CHANNEL_HEARTBEAT) /datum/reagent/drug/kronkaine/expose_mob(mob/living/carbon/druggo, methods, trans_volume, show_message, touch_protection) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 989a3a970bec..4988fa5295d3 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -575,7 +575,7 @@ /datum/reagent/consumable/garlic/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(isvampire(affected_mob)) //incapacitating but not lethal. Unfortunately, vampires cannot vomit. + if(HAS_TRAIT(affected_mob, TRAIT_UNHOLY_BANEABLE)) //incapacitating but not lethal. Unfortunately, vampires cannot vomit. if(SPT_PROB(min((current_cycle-1)/2, 12.5), seconds_per_tick)) if(HAS_TRAIT(affected_mob, TRAIT_ANOSMIA)) to_chat(affected_mob, span_danger("You feel that something is wrong, your strength is leaving you! You can barely think...")) @@ -583,12 +583,12 @@ to_chat(affected_mob, span_danger("You can't get the scent of garlic out of your nose! You can barely think...")) affected_mob.Paralyze(10) affected_mob.set_jitter_if_lower(20 SECONDS) - else - var/obj/item/organ/liver/liver = affected_mob.get_organ_slot(ORGAN_SLOT_LIVER) - if(liver && HAS_TRAIT(liver, TRAIT_CULINARY_METABOLISM)) - if(SPT_PROB(10, seconds_per_tick)) //stays in the system much longer than sprinkles/banana juice, so heals slower to partially compensate - if(affected_mob.heal_bodypart_damage(brute = 3.34 * metabolization_ratio, burn = 3.34 * metabolization_ratio, updating_health = FALSE)) - return UPDATE_MOB_HEALTH + return + var/obj/item/organ/liver/liver = affected_mob.get_organ_slot(ORGAN_SLOT_LIVER) + if(liver && HAS_TRAIT(liver, TRAIT_CULINARY_METABOLISM)) + if(SPT_PROB(10, seconds_per_tick)) //stays in the system much longer than sprinkles/banana juice, so heals slower to partially compensate + if(affected_mob.heal_bodypart_damage(brute = 3.34 * metabolization_ratio, burn = 3.34 * metabolization_ratio, updating_health = FALSE)) + return UPDATE_MOB_HEALTH /datum/reagent/consumable/tearjuice name = "Tear Juice" @@ -1038,7 +1038,7 @@ /datum/reagent/consumable/liquidelectricity/enriched/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(isethereal(affected_mob)) + if(HAS_TRAIT(affected_mob, TRAIT_ETHEREAL_METABOLISM)) affected_mob.adjust_blood_volume(1 * seconds_per_tick) else if(SPT_PROB(10, seconds_per_tick)) //lmao at the newbs who eat energy bars affected_mob.electrocute_act(rand(5,10), "Liquid Electricity in their body", 1, SHOCK_NOGLOVES) //the shock is coming from inside the house diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm index 5a8e8395fd65..ee4f31c360f9 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm @@ -481,7 +481,7 @@ Basically, we fill the time between now and 2s from now with hands based off the self_consuming = TRUE //No pesky liver shenanigans chemical_flags = REAGENT_DEAD_PROCESS affected_organ_flags = NONE - ///If we brought someone back from the dead + /// If we brought someone back from the dead var/back_from_the_dead = FALSE /// List of trait buffs to give to the affected mob, and remove as needed. var/static/list/trait_buffs = list( @@ -503,15 +503,11 @@ Basically, we fill the time between now and 2s from now with hands based off the return metabolization_rate = 0.5 * REAGENTS_METABOLISM affected_mob.add_traits(trait_buffs, type) - affected_mob.set_stat(CONSCIOUS) //This doesn't touch knocked out - affected_mob.updatehealth() - affected_mob.update_sight() - REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, STAT_TRAIT) - REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) //Because these are normally updated using set_health() - but we don't want to adjust health, and the addition of NOHARDCRIT blocks it being added after, but doesn't remove it if it was added before - REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) //As above, removes unconsciousness if it was added before the reagent was administered - affected_mob.set_resting(FALSE) //Please get up, no one wants a deaththrows juggernaught that lies on the floor all the time - affected_mob.SetAllImmobility(0) - affected_mob.grab_ghost(force = FALSE) //Shoves them back into their freshly reanimated corpse. + if(!affected_mob.revive(force_grab_ghost = TRUE)) + return + + affected_mob.set_resting(FALSE, TRUE) // Please get up, no one wants a deaththrows juggernaught that lies on the floor all the time + affected_mob.SetAllImmobility(0 SECONDS) back_from_the_dead = TRUE affected_mob.emote("gasp") affected_mob.playsound_local(affected_mob, 'sound/effects/health/fastbeat.ogg', 65) @@ -522,19 +518,17 @@ Basically, we fill the time between now and 2s from now with hands based off the return //Following is for those brought back from the dead only var/creation_impurity = 1 - creation_purity - REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) - REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) for(var/datum/wound/iter_wound as anything in affected_mob.all_wounds) iter_wound.adjust_blood_flow(4 * creation_impurity * metabolization_ratio * seconds_per_tick) var/need_mob_update - need_mob_update = affected_mob.adjust_brute_loss(20 * creation_impurity * metabolization_ratio * seconds_per_tick, required_bodytype = affected_bodytype) + need_mob_update += affected_mob.adjust_brute_loss(20 * creation_impurity * metabolization_ratio * seconds_per_tick, required_bodytype = affected_bodytype) need_mob_update += affected_mob.adjust_organ_loss(ORGAN_SLOT_HEART, 4 * ((1 + creation_impurity) * metabolization_ratio * seconds_per_tick), required_organ_flag = affected_organ_flags) if(affected_mob.health < HEALTH_THRESHOLD_CRIT) affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/reagent/nooartrium) if(affected_mob.health < HEALTH_THRESHOLD_FULLCRIT) affected_mob.add_actionspeed_modifier(/datum/actionspeed_modifier/nooartrium) var/obj/item/organ/heart/heart = affected_mob.get_organ_slot(ORGAN_SLOT_HEART) - if(!heart || heart.organ_flags & ORGAN_FAILING) + if(isnull(heart) || (heart.organ_flags & ORGAN_FAILING)) remove_buffs(affected_mob) if(need_mob_update) return UPDATE_MOB_HEALTH @@ -543,30 +537,28 @@ Basically, we fill the time between now and 2s from now with hands based off the . = ..() remove_buffs(affected_mob) var/obj/item/organ/heart/heart = affected_mob.get_organ_slot(ORGAN_SLOT_HEART) - if(affected_mob.health < -500 || heart.organ_flags & ORGAN_FAILING)//Honestly commendable if you get -500 + if(affected_mob.health < (5 * HEALTH_THRESHOLD_DEAD) || (heart?.organ_flags & ORGAN_FAILING)) // Honestly commendable if you get -500 explosion(affected_mob, light_impact_range = 1, explosion_cause = src) - qdel(heart) affected_mob.visible_message(span_boldwarning("[affected_mob]'s heart explodes!")) + qdel(heart) /datum/reagent/inverse/penthrite/overdose_start(mob/living/carbon/affected_mob, metabolization_ratio) - . = ..() if(!back_from_the_dead) return ..() + var/obj/item/organ/heart/heart = affected_mob.get_organ_slot(ORGAN_SLOT_HEART) - if(!heart) //No heart? No life! + if(isnull(heart)) // No heart? No life! REMOVE_TRAIT(affected_mob, TRAIT_NODEATH, type) - affected_mob.stat = DEAD - return ..() - explosion(affected_mob, light_impact_range = 1, explosion_cause = src) - qdel(heart) - affected_mob.visible_message(span_boldwarning("[affected_mob]'s heart explodes!")) + else + explosion(affected_mob, light_impact_range = 1, explosion_cause = src) + affected_mob.visible_message(span_boldwarning("[affected_mob]'s heart explodes!")) + qdel(heart) return..() /datum/reagent/inverse/penthrite/proc/remove_buffs(mob/living/carbon/affected_mob) affected_mob.remove_traits(trait_buffs, type) affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/nooartrium) affected_mob.remove_actionspeed_modifier(/datum/actionspeed_modifier/nooartrium) - affected_mob.update_sight() /* Non c2 medicines */ diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 9f1f00b5e48c..731c86d973e0 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -157,7 +157,7 @@ if(affected_mob.bodytemperature >= T0C) return var/power = -0.00003 * (affected_mob.bodytemperature ** 2) + 3 - if(HAS_TRAIT(affected_mob, TRAIT_KNOCKEDOUT)) //Significantly more effective when unconscious + if(IS_UNCONSCIOUS(affected_mob)) //Significantly more effective when unconscious power *= 2 var/need_mob_update need_mob_update = affected_mob.adjust_oxy_loss(-1.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index bfee741b764f..70fa6023de57 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -330,6 +330,7 @@ randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS default_container = /obj/item/reagent_containers/cup/glass/bottle/holywater metabolized_traits = list(TRAIT_HOLY) + COOLDOWN_DECLARE(spell_clear_cd) /datum/glass_style/drinking_glass/holywater required_drink_type = /datum/reagent/water/holywater @@ -365,14 +366,26 @@ affected_mob.adjust_jitter_up_to(2 SECONDS * metabolization_ratio * seconds_per_tick, 20 SECONDS) var/need_mob_update = FALSE - if(IS_CULTIST(affected_mob)) - for(var/datum/action/innate/cult/blood_magic/BM in affected_mob.actions) + if(COOLDOWN_FINISHED(src, spell_clear_cd)) + if(IS_CULTIST(affected_mob)) + for(var/datum/action/innate/cult/blood_magic/BM in affected_mob.actions) + var/removed_any = FALSE + for(var/datum/action/innate/cult/blood_spell/BS in BM.spells) + removed_any = TRUE + qdel(BS) + if(removed_any) + to_chat(affected_mob, span_cult_large("Your blood rites falter as holy water scours your body!")) + COOLDOWN_START(src, spell_clear_cd, 3 SECONDS) + + if(IS_HERETIC(affected_mob)) + var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(affected_mob) var/removed_any = FALSE - for(var/datum/action/innate/cult/blood_spell/BS in BM.spells) - removed_any = TRUE - qdel(BS) + for(var/datum/heretic_knowledge/spell/spell in heretic_datum.get_researched_knowledge()) + if(spell.remove_charges(ceil(spell.max_charges * spell.holywater_drain_amount))) + removed_any = TRUE if(removed_any) - to_chat(affected_mob, span_cult_large("Your blood rites falter as holy water scours your body!")) + to_chat(affected_mob, span_mansus("Your intricate rituals are distrupted by the holy water scouring your body!")) + COOLDOWN_START(src, spell_clear_cd, 3 SECONDS) if(data["deciseconds_metabolized"] >= (25 SECONDS)) // 10 units affected_mob.adjust_stutter_up_to(2 SECONDS * metabolization_ratio * seconds_per_tick, 20 SECONDS) @@ -411,7 +424,7 @@ if(reac_volume >= 10) for(var/obj/effect/rune/R in exposed_turf) qdel(R) - exposed_turf.Bless() + exposed_turf.bless_turf() /datum/reagent/water/hollowwater name = "Hollow Water" @@ -804,7 +817,7 @@ if(!ishuman(affected_mob)) return ..() var/mob/living/carbon/affected_human = affected_mob - if(isjellyperson(affected_human)) + if(affected_human.mob_biotypes & MOB_SLIME) var/datum/species/species_type = pick(subtypesof(race)) affected_human.set_species(species_type) holder.del_reagent(type) @@ -3355,7 +3368,7 @@ /datum/reagent/brimdust/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(affected_mob.adjust_fire_loss((ispodperson(affected_mob) ? -1 : 1 * seconds_per_tick), updating_health = FALSE)) + if(affected_mob.adjust_fire_loss((affected_mob.mob_biotypes & MOB_PLANT) ? -1 : 1 * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH /datum/reagent/brimdust/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user) @@ -3462,7 +3475,7 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS overdose_threshold = 50 // GLOW GLOW GLOW - metabolized_traits = list(TRAIT_MINOR_NIGHT_VISION) + metabolized_traits = list(TRAIT_NIGHT_VISION) self_consuming = TRUE /// Fake flashlight we're using to make owner's eyes glow var/obj/item/flashlight/eyelight/glow/glowing @@ -3542,7 +3555,7 @@ name = "Red Luminiscent Fluid" color = COLOR_SOFT_RED // The glow *is* unnatural, so... - metabolized_traits = list(TRAIT_MINOR_NIGHT_VISION, TRAIT_UNNATURAL_RED_GLOWY_EYES) + metabolized_traits = list(TRAIT_NIGHT_VISION, TRAIT_UNNATURAL_RED_GLOWY_EYES) /datum/reagent/luminescent_fluid/red/overdose_start(mob/living/affected_mob, metabolization_ratio) . = ..() @@ -3614,3 +3627,121 @@ exposed_mob.Stun(4 SECONDS) exposed_mob.petrify(reac_volume * reagent_to_time_conversion) + +/datum/reagent/cement + name = "Cement" + description = "A sophisticated binding agent used to produce concrete." + color = "#c4c0bc" + taste_description = "cement" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS + var/potency = 2 + var/concrete_type = /datum/reagent/concrete + var/units_per_aggregate = 5 + +/datum/reagent/cement/expose_mob(mob/living/exposed_mob, methods, reac_volume, show_message, touch_protection) + . = ..() + if (!(methods & INGEST)) + return + exposed_mob.add_mood_event("cement", /datum/mood_event/cement) + +/datum/reagent/cement/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) + . = ..() + var/rate = potency * seconds_per_tick * metabolization_ratio + var/need_mob_update = 0 + + if(prob(min(current_cycle/2, 5))) + need_mob_update += affected_mob.adjust_tox_loss(rate) + + if(prob(min(current_cycle/4, 10))) + need_mob_update += affected_mob.adjust_organ_loss(ORGAN_SLOT_BRAIN, rate) + + if(need_mob_update) + return UPDATE_MOB_HEALTH + +/datum/reagent/cement/hexement + name = "Hexement" + description = "An advanced, space-age binding agent used to produce reinforced concrete." + color = "#969390" + potency = 4 + concrete_type = /datum/reagent/concrete/hexacrete + units_per_aggregate = 2 + +/datum/reagent/cement/pavement + name = "Pavement Cement" + description = "A mix of cement and asphalt. Looks less tasty than normal cement." + color = "#5c6361" + potency = 3 + concrete_type = /datum/reagent/concrete/pavement + +/datum/reagent/concrete + name = "Concrete" + description = "A mix of cement and aggregate, commonly used as a bulk building material." + color = "#a8988a" + taste_description = "rocks" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS + var/units_per_wall = 10 + var/units_per_floor = 2 + var/turf/closed/wall/concrete/wall_type = /turf/closed/wall/concrete + var/turf/open/floor/concrete/floor_type = /turf/open/floor/concrete + +/datum/reagent/concrete/expose_atom(atom/exposed_atom, reac_volume, methods) + . = ..() + // VAPOR (foam, spray), TOUCH (splashing) allowed, SMOKE_MACHINE (TOUCH|INHALE) not allowed + if(!((methods & (TOUCH|INHALE)) == TOUCH) && !(methods & VAPOR)) + return + var/girder_type = initial(wall_type.girder_type) + if(istype(exposed_atom, girder_type)) + return concify_girder(exposed_atom, volume) + if(istype(exposed_atom, /turf/open/floor/catwalk_floor)) + return concify_catwalk(exposed_atom, volume) + return + +/datum/reagent/concrete/proc/pour_amount(atom/target, mob/user) + if(istype(target, initial(wall_type.girder_type))) + return units_per_wall + else if(istype(target, /turf/open/floor/catwalk_floor)) + return units_per_floor + return 0 + +/datum/reagent/concrete/proc/concify_girder(obj/exposed_obj, volume) + if(volume < units_per_wall) + return FALSE + var/turf/open/wall_turf = get_turf(exposed_obj) + if(!istype(wall_turf)) + return FALSE + var/turf/closed/wall/concrete/wall = wall_turf.place_on_top(wall_type) + exposed_obj.transfer_fingerprints_to(wall) + wall.harden_lvl = 0 + wall.check_harden() + qdel(exposed_obj) + return TRUE + +/datum/reagent/concrete/proc/concify_catwalk(turf/open/floor/catwalk_floor/catwalk, volume) + if(volume < units_per_floor) + return FALSE + var/turf/open/floor/plating = catwalk.make_plating() + var/turf/open/floor/concrete/concrete_floor = plating.place_on_top(floor_type, flags=CHANGETURF_INHERIT_AIR) + concrete_floor.harden_lvl = 0 + concrete_floor.check_harden() + return TRUE + +/datum/reagent/concrete/hexacrete + name = "Hexacrete" + description = "Made with fortified cement, this mix of binder and aggregate is a useful, sturdy building material." + color = "#7b6e60" + wall_type = /turf/closed/wall/concrete/reinforced + floor_type = /turf/open/floor/concrete/reinforced + +/datum/reagent/concrete/pavement + name = "Pavement Concrete" + description = "Road surface, blacktop, asphalt concrete - whatever you call it, it's the most common material used in constructing runways for ships and roadways for vehicles." + color = "#3f4543" + floor_type = /turf/open/floor/concrete/pavement + +/datum/reagent/concrete_mix + name = "Concrete Mix" + description = "Pre-made concrete mix. Ideal for lazy engineers." + color = "#c4c0bc" + taste_description = "chalky concrete" diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index 842c4d281272..3b3593c64144 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -476,7 +476,7 @@ randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS /datum/reagent/teslium/energized_jelly/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) - if(!isjellyperson(affected_mob)) //everyone but jellypeople get shocked as normal. + if(!(affected_mob.mob_biotypes & MOB_SLIME)) //everyone but jellypeople get shocked as normal. return ..() affected_mob.AdjustAllImmobility(-20 * metabolization_ratio * seconds_per_tick) if(affected_mob.adjust_stamina_loss(-5 * metabolization_ratio * seconds_per_tick, updating_stamina = FALSE)) diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index bc1b572dafb1..ccb37af326a5 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -993,7 +993,7 @@ if(3) if(!affected_mob.undergoing_cardiac_arrest() && affected_mob.can_heartattack()) affected_mob.set_heartattack(TRUE) - if(affected_mob.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(affected_mob)) affected_mob.visible_message(span_userdanger("[affected_mob] clutches at [affected_mob.p_their()] chest as if [affected_mob.p_their()] heart stopped!")) else affected_mob.losebreath += 10 diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 84ea9439e8f6..1decf319fe0e 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -668,8 +668,10 @@ maybe_monkey.vomit(VOMIT_CATEGORY_BLOOD) to_chat(maybe_monkey, span_danger("You vomit out blood, making you feel grossly monkeyish.")) - if (LAZYLEN(SSmobs.cubemonkeys) < CONFIG_GET(number/monkeycap)) - new /mob/living/carbon/human/species/monkey(location, TRUE) + var/monkey_cap = CONFIG_GET(number/monkeycap) + for(var/_ in 1 to created_volume) + if (LAZYLEN(SSmobs.cubemonkeys) < monkey_cap) + new /mob/living/carbon/human/species/monkey(location, null, TRUE) /datum/chemical_reaction/angry_monkey required_reagents = list(/datum/reagent/monkey_powder = 50, /datum/reagent/inverse/bath_salts = 10) @@ -687,8 +689,11 @@ maybe_monkey.vomit(VOMIT_CATEGORY_BLOOD) to_chat(maybe_monkey, span_danger("You vomit out blood, making you feel grossly monkeyish.")) - if (LAZYLEN(SSmobs.cubemonkeys) < CONFIG_GET(number/monkeycap)) - new /mob/living/carbon/human/species/monkey/angry(location, TRUE) + var/monkey_cap = CONFIG_GET(number/monkeycap) + for(var/_ in 1 to created_volume) + if (LAZYLEN(SSmobs.cubemonkeys) < monkey_cap) + new /mob/living/carbon/human/species/monkey/angry(location, null, TRUE) + //water electrolysis /datum/chemical_reaction/electrolysis @@ -1159,3 +1164,28 @@ required_reagents = list(/datum/reagent/consumable/nutriment/protein = 1) required_catalysts = list(/datum/reagent/toxin/carnivorousblood = 1) reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE + +/datum/chemical_reaction/cement + results = list(/datum/reagent/cement = 6) + required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/water = 1) + required_temp = 400 + mix_message = "The mixture boils off a grey vapor..."//The water boils off, leaving the cement + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/hexement + results = list(/datum/reagent/cement/hexement = 1) + required_reagents = list(/datum/reagent/cement = 6, /datum/reagent/stable_plasma = 1) + required_temp = 400 + mix_message = "The mixture rapidly condenses and darkens in color..." + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/pavement + results = list(/datum/reagent/cement/pavement = 2) + required_reagents = list(/datum/reagent/cement = 1, /datum/reagent/fuel/oil = 1) + mix_message = "The mixture mixing suddenly reminds you of cramped urban worlds." + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER + +/datum/chemical_reaction/quick_concrete + results = list(/datum/reagent/concrete = 5) + required_reagents = list(/datum/reagent/concrete_mix = 5, /datum/reagent/water = 5) + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_OTHER diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index 3ca6d9d85d0e..f2fdbd1f6058 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -189,8 +189,39 @@ if(isliving(target)) return try_drink(target, user) + if(is_drainable()) + return pour_concrete(target, user) + return NONE +/// QoL helper for pouring concrete directly from container just by clicking. +/// Easier than splashing onto turf or object, but requires a little delay. +/obj/item/reagent_containers/cup/proc/pour_concrete(atom/target, mob/living/user) + var/datum/reagent/concrete/concrete = reagents.has_reagent(/datum/reagent/concrete, check_subtypes=TRUE) + if (!concrete) + return NONE + var/pour_amount = concrete.pour_amount(target, user) + if (!pour_amount) + return NONE + if (concrete.volume < pour_amount) + user.balloon_alert(user, "not enough to pour") + return ITEM_INTERACT_FAILURE + user.balloon_alert(user, "pouring...") + user.visible_message( + span_notice("[user] starts pouring concrete onto \a [target]."), + span_notice("You start pouring concrete onto \a [target]..."), + ) + if (!do_after(user, 1 SECONDS, target=target)) + user.balloon_alert(user, "interrupted") + return ITEM_INTERACT_FAILURE + if(concrete.volume < pour_amount) // check if volume has changed during do_after + user.balloon_alert(user, "not enough to pour") + return ITEM_INTERACT_FAILURE + playsound(src, 'sound/effects/slosh.ogg', 25, TRUE) + concrete.expose_atom(target, pour_amount, TOUCH) + reagents.remove_reagent(concrete.type, pour_amount) + return ITEM_INTERACT_SUCCESS + /obj/item/reagent_containers/cup/interact_with_atom_secondary(atom/target, mob/living/user, list/modifiers) . = ..() if(. & ITEM_INTERACT_ANY_BLOCKER) diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 1ca466bb9e3e..b692f7bef3f9 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -129,6 +129,7 @@ desc = "A small bottle. Contains the liquid essence of the gods." icon = 'icons/obj/drinks/bottles.dmi' icon_state = "holyflask" + worn_icon_state = "holyflask" inhand_icon_state = "holyflask" list_reagents = list(/datum/reagent/medicine/adminordrazine = 30) diff --git a/code/modules/reagents/reagent_containers/cups/drinkingglass.dm b/code/modules/reagents/reagent_containers/cups/drinkingglass.dm index 2dda4a4d0f44..5431bb7d86b5 100644 --- a/code/modules/reagents/reagent_containers/cups/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/cups/drinkingglass.dm @@ -2,6 +2,7 @@ name = "drinking glass" desc = "Your standard drinking glass." icon_state = "glass_empty" + worn_icon_state = "bottle" base_icon_state = "glass_empty" amount_per_transfer_from_this = 10 fill_icon_thresholds = list(0) diff --git a/code/modules/reagents/reagent_containers/cups/drinks.dm b/code/modules/reagents/reagent_containers/cups/drinks.dm index 4ebcfd4b7ef7..29cb765e6aab 100644 --- a/code/modules/reagents/reagent_containers/cups/drinks.dm +++ b/code/modules/reagents/reagent_containers/cups/drinks.dm @@ -17,22 +17,26 @@ /obj/item/reagent_containers/cup/glass/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum, do_splash = TRUE) . = ..() if(!.) //if the bottle wasn't caught - var/mob/thrower = throwingdatum?.get_thrower() - if(!istype(thrower)) - return - smash(hit_atom, thrower, throwingdatum) + smash(hit_atom, throwingdatum?.get_thrower(), throwingdatum) -/obj/item/reagent_containers/cup/glass/proc/smash(atom/target, mob/thrower, datum/thrownthing/throwingdatum, break_top = FALSE) +/obj/item/reagent_containers/cup/glass/proc/smash(atom/target, atom/thrower, datum/thrownthing/throwingdatum, break_top = FALSE) if(!isGlass) - return + return FALSE if(QDELING(src) || !target) //Invalid loc - return - if(bartender_check(target, thrower) && throwingdatum) - return - splash_reagents(QDELETED(target) ? target.drop_location() : target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE) - var/obj/item/broken_bottle/B = new (loc) - B.mimic_broken(src, target, break_top) + return FALSE + if(ismob(thrower) && bartender_check(target, thrower) && throwingdatum) + return FALSE + var/splash_target = QDELETED(target) ? target.drop_location() : target + var/splash_thrower = ismob(thrower) ? thrower : null + splash_reagents(splash_target, splash_thrower, allow_closed_splash = TRUE) + var/obj/item/broken_bottle/broken = new (loc) + broken.mimic_broken(src, target, break_top) + post_smash(target, thrower, throwingdatum, broken) qdel(src) + return TRUE + +/obj/item/reagent_containers/cup/glass/proc/post_smash(atom/target, atom/thrower, datum/thrownthing/throwingdatum, obj/item/broken_bottle/broken) + return /obj/item/reagent_containers/cup/glass/bullet_act(obj/projectile/proj) . = ..() @@ -375,14 +379,6 @@ base_container_type = /obj/item/reagent_containers/cup/glass/bottle/juice/smallcarton, \ ) -/obj/item/reagent_containers/cup/glass/bottle/juice/smallcarton/smash(atom/target, mob/thrower, datum/thrownthing/throwingdatum, break_top) - if(bartender_check(target, thrower) && throwingdatum) - return - splash_reagents(QDELETED(target) ? target.drop_location() : target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE) - var/obj/item/broken_bottle/bottle_shard = new(drop_location()) - bottle_shard.mimic_broken(src, target) - qdel(src) - /obj/item/reagent_containers/cup/glass/colocup name = "colo cup" desc = "A cheap, mass produced style of cup, typically used at parties. They never seem to come out red, for some reason..." @@ -511,6 +507,7 @@ desc = "Every good spaceman knows it's a good idea to bring along a couple of pints of whiskey wherever they go." custom_price = PAYCHECK_COMMAND * 2 icon = 'icons/obj/drinks/bottles.dmi' + worn_icon_state = "flask" icon_state = "flask" custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2.5) volume = 60 @@ -519,12 +516,14 @@ /obj/item/reagent_containers/cup/glass/flask/gold name = "captain's flask" desc = "A gold flask belonging to the captain." + worn_icon_state = "flask_gold" icon_state = "flask_gold" custom_materials = list(/datum/material/gold=SMALL_MATERIAL_AMOUNT*5) /obj/item/reagent_containers/cup/glass/flask/det name = "detective's flask" desc = "The detective's only true friend." + worn_icon_state = "detflask" icon_state = "detflask" list_reagents = list(/datum/reagent/consumable/ethanol/whiskey = 30) diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm index f807ac6b4b12..da16ca59b3bb 100644 --- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm +++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm @@ -125,20 +125,11 @@ volume = 50 custom_price = PAYCHECK_CREW * 0.9 -/obj/item/reagent_containers/cup/glass/bottle/smash(mob/living/target, mob/thrower, datum/thrownthing/throwingdatum, break_top) - if(bartender_check(target, thrower) && throwingdatum) - return FALSE - splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE) - var/obj/item/broken_bottle/broken = new(drop_location()) - if(!throwingdatum && thrower) - thrower.put_in_hands(broken) - broken.mimic_broken(src, target, break_top) +/obj/item/reagent_containers/cup/glass/bottle/post_smash(atom/target, atom/thrower, datum/thrownthing/throwingdatum, obj/item/broken_bottle/broken) + if(!throwingdatum && ismob(thrower)) + astype(thrower, /mob).put_in_hands(broken) broken.inhand_icon_state = broken_inhand_icon_state - if(message_in_a_bottle) - message_in_a_bottle.forceMove(drop_location()) - - qdel(src) - return TRUE + message_in_a_bottle?.forceMove(drop_location()) /obj/item/reagent_containers/cup/glass/bottle/try_splash(mob/user, atom/target) if(!isGlass) @@ -170,7 +161,7 @@ else user.visible_message( span_warning("[user] smashes [src] [head_hitter ? "over [target]'s head" : "against [target]"]!"), - span_warning("[user] smashes [src] [head_hitter ? "over your head" : "against you"]!"), + span_warning("You smash [src] [head_hitter ? "over [target]'s head" : "against [target]"]!"), ) // Finally, smash the bottle. This kills (del) the bottle and also does all the logging for us @@ -392,6 +383,7 @@ desc = "A flask of the chaplain's holy water." icon = 'icons/obj/drinks/bottles.dmi' icon_state = "holyflask" + worn_icon_state = "holyflask" inhand_icon_state = "holyflask" broken_inhand_icon_state = "broken_holyflask" list_reagents = list(/datum/reagent/water/holywater = 100) diff --git a/code/modules/reagents/reagent_containers/jerrycan.dm b/code/modules/reagents/reagent_containers/jerrycan.dm index b92dc7ec85c9..c2975e6ebe26 100644 --- a/code/modules/reagents/reagent_containers/jerrycan.dm +++ b/code/modules/reagents/reagent_containers/jerrycan.dm @@ -11,6 +11,8 @@ #define LABEL_ROBUST_HARVEST "robust" #define LABEL_LEFT_4_ZED "l4z" #define LABEL_SPACE_CLEANER "space_cleaner" +#define LABEL_CONCRETE "concrete" +#define LABEL_HEXACRETE "hexacrete" #define CAP_BLACK "black" #define CAP_WHITE "white" @@ -168,6 +170,19 @@ desc = "A jug of most wholesome milk." list_reagents = list(/datum/reagent/consumable/milk = 200) +/obj/item/reagent_containers/cup/jerrycan/concrete_mix + name = "concrete mix can" + label_type = LABEL_CONCRETE + cap_type = CAP_BLACK + desc = "A large can of ready-to-use concrete mix. Just add water." + list_reagents = list(/datum/reagent/concrete_mix = 200) + +/obj/item/reagent_containers/cup/jerrycan/hexacrete + name = "hexacrete can" + label_type = LABEL_HEXACRETE + cap_type = CAP_RED + desc = "A large canister of hexacrete." + list_reagents = list(/datum/reagent/concrete/hexacrete = 200) #undef LABEL_TEXT #undef LABEL_TEXT_OLD @@ -182,6 +197,8 @@ #undef LABEL_ROBUST_HARVEST #undef LABEL_LEFT_4_ZED #undef LABEL_SPACE_CLEANER +#undef LABEL_CONCRETE +#undef LABEL_HEXACRETE #undef CAP_BLACK #undef CAP_WHITE #undef CAP_RED diff --git a/code/modules/reagents/withdrawal/generic_addictions.dm b/code/modules/reagents/withdrawal/generic_addictions.dm index a875fa3c5379..4776e3731a06 100644 --- a/code/modules/reagents/withdrawal/generic_addictions.dm +++ b/code/modules/reagents/withdrawal/generic_addictions.dm @@ -134,11 +134,7 @@ if(!ishuman(affected_carbon)) return to_chat(affected_carbon, span_warning("You feel yourself adapt to the darkness.")) - var/mob/living/carbon/human/affected_human = affected_carbon - var/obj/item/organ/eyes/empowered_eyes = affected_human.get_organ_by_type(/obj/item/organ/eyes) - if(empowered_eyes) - ADD_TRAIT(affected_human, TRAIT_NIGHT_VISION, "maint_drug_addiction") - empowered_eyes?.refresh() + ADD_TRAIT(affected_carbon, TRAIT_NIGHT_VISION, type) /datum/addiction/maintenance_drugs/withdrawal_stage_3_process(mob/living/carbon/affected_carbon, seconds_per_tick) if(!ishuman(affected_carbon)) @@ -162,12 +158,7 @@ tongue.liked_foodtypes = initial(tongue.liked_foodtypes) tongue.disliked_foodtypes = initial(tongue.disliked_foodtypes) tongue.toxic_foodtypes = initial(tongue.toxic_foodtypes) - if(!ishuman(affected_carbon)) - return - var/mob/living/carbon/human/affected_human = affected_carbon - REMOVE_TRAIT(affected_human, TRAIT_NIGHT_VISION, "maint_drug_addiction") - var/obj/item/organ/eyes/eyes = affected_human.get_organ_by_type(/obj/item/organ/eyes) - eyes?.refresh() + REMOVE_TRAIT(affected_carbon, TRAIT_NIGHT_VISION, type) ///Makes you a hypochondriac - I'd like to call it hypochondria, but "I could use some hypochondria" doesn't work /datum/addiction/medicine @@ -263,7 +254,7 @@ if(SPT_PROB(65, seconds_per_tick)) return - if(affected_carbon.stat >= SOFT_CRIT) + if(IS_UNCONSCIOUS_OR_CRIT(affected_carbon)) return var/obj/item/organ/organ = pick(affected_carbon.organs) diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 0b36785f681a..fa903ef3b7f6 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -22,7 +22,7 @@ GLOBAL_VAR_INIT(disposals_animals_spawned, 0) var/full_pressure = FALSE /// Is the pressure charging var/pressure_charging = TRUE - // True if flush handle is pulled + /// True if flush handle is pulled var/flush = FALSE /// The attached pipe trunk var/obj/structure/disposalpipe/trunk/trunk = null @@ -202,13 +202,14 @@ GLOBAL_VAR_INIT(disposals_animals_spawned, 0) /// Handles stuffing a grabbed mob into the disposal /obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user) var/ventcrawler = HAS_TRAIT(user, TRAIT_VENTCRAWLER_ALWAYS) || HAS_TRAIT(user, TRAIT_VENTCRAWLER_NUDE) - if(!iscarbon(user) && !ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. - if (iscyborg(user)) - var/mob/living/silicon/robot/borg = user - if (!borg.model || !borg.model.canDispose) + if(user == target) //we didn't check this before even though we're asserting here whether we can put ourself in, bruh. + if(!iscarbon(user) && !ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. + if (iscyborg(user)) + var/mob/living/silicon/robot/borg = user + if (!borg.model || !borg.model.canDispose) + return FALSE + else return FALSE - else - return FALSE if(!isturf(user.loc)) //No magically doing it from inside closets return FALSE if(target.buckled || target.has_buckled_mobs()) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 4cde5115f2f8..cfe10fb5f3cf 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -81,7 +81,7 @@ return to_chat(user, span_notice("You lean on the back of [container] and start pushing to rip the wrapping around it.")) if(do_after(user, 5 SECONDS, target = container)) - if(!user || user.stat != CONSCIOUS || user.loc != container || container.loc != src) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != container || container.loc != src) return to_chat(user, span_notice("You successfully removed [container]'s wrapping!")) container.forceMove(loc) diff --git a/code/modules/religion/deaconize.dm b/code/modules/religion/deaconize.dm index 84211acebe74..a8bf9ca597de 100644 --- a/code/modules/religion/deaconize.dm +++ b/code/modules/religion/deaconize.dm @@ -50,7 +50,7 @@ if(!(potential_deacon in religious_tool.buckled_mobs)) //checks one last time if the right corpse is still buckled to_chat(user, span_warning("[potential_deacon] is no longer on the altar!")) return FALSE - if(potential_deacon.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(potential_deacon)) to_chat(user, span_warning("[potential_deacon] has to be conscious for the rite to work!")) return FALSE if(!potential_deacon.mind) @@ -72,7 +72,7 @@ ///Helper if the passed possible_deacon is valid to become a deacon or not. /datum/religion_rites/deaconize/proc/is_valid_for_deacon(mob/living/carbon/human/possible_deacon, mob/living/user) - if(possible_deacon.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(possible_deacon)) to_chat(user, span_warning("[possible_deacon] needs to be alive and conscious to join!")) return FALSE if(possible_deacon.mind && possible_deacon.mind.holy_role) diff --git a/code/modules/religion/dreams/banish_nightmare.dm b/code/modules/religion/dreams/banish_nightmare.dm index c6b49b84a67e..21f1f0359676 100644 --- a/code/modules/religion/dreams/banish_nightmare.dm +++ b/code/modules/religion/dreams/banish_nightmare.dm @@ -17,7 +17,7 @@ /datum/religion_rites/banish_nightmare/perform_rite(mob/living/user, atom/religious_tool) var/has_nightmare = FALSE for(var/mob/living/carbon/human/nightmare in get_turf(religious_tool)) - if(isnightmare(nightmare)) + if(HAS_TRAIT(nightmare, TRAIT_NIGHTMARISH)) has_nightmare = TRUE break @@ -37,7 +37,7 @@ var/favor = 0 var/give_heart = FALSE for(var/mob/living/carbon/human/nightmare in get_turf(religious_tool)) - if(!isnightmare(nightmare)) + if(!HAS_TRAIT(nightmare, TRAIT_NIGHTMARISH)) continue if(istype(nightmare.get_organ_slot(ORGAN_SLOT_HEART), /obj/item/organ/heart/nightmare)) @@ -84,7 +84,7 @@ healing_probability = 5 if(HAS_TRAIT(owner, TRAIT_DREAMING)) healing_probability += 7.5 - if(owner.stat == UNCONSCIOUS) + if(IS_UNCONSCIOUS(owner)) healing_probability += 7.5 return ..() diff --git a/code/modules/religion/dreams/deaconize_dreamer.dm b/code/modules/religion/dreams/deaconize_dreamer.dm index 04bdcfc656a6..aedb881e07df 100644 --- a/code/modules/religion/dreams/deaconize_dreamer.dm +++ b/code/modules/religion/dreams/deaconize_dreamer.dm @@ -4,7 +4,7 @@ rite_flags = parent_type::rite_flags & ~RITE_ONE_TIME_USE /datum/religion_rites/deaconize/dreamers/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool) - if(isnightmare(potential_deacon)) + if(HAS_TRAIT(potential_deacon, TRAIT_NIGHTMARISH)) to_chat(user, span_warning("[potential_deacon] is a nightmare, an affront to [GLOB.deity] and all they stand for!")) return FALSE return ..() diff --git a/code/modules/religion/dreams/dream_portent.dm b/code/modules/religion/dreams/dream_portent.dm index be95b95c02dc..3a744d13d711 100644 --- a/code/modules/religion/dreams/dream_portent.dm +++ b/code/modules/religion/dreams/dream_portent.dm @@ -131,7 +131,7 @@ )) for(var/datum/antagonist/nightmare/nightmare in GLOB.antagonists) - if(nightmare.owner?.current?.stat == CONSCIOUS) + if(nightmare.owner?.current?.stat == STABLE) return pick(list( list("you have a terrible nightmare", "filled with indescribable horrors", "leaving you with a lingering sense of dread"), list("you have a terrible nightmare", "filled with visions of your own death", "leaving you with a lingering sense of doom"), @@ -145,9 +145,9 @@ return list("the Blood Geometer, Nar'sie, invades your dream", "her pressence overwhelming and suffocating", "she eyes you greedily") for(var/datum/antagonist/heretic/heretic in GLOB.antagonists) - if(!heretic.ascended) + if(!heretic.ascended || isnull(heretic.owner?.current)) continue - if(heretic.owner?.current?.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(heretic.owner.current)) return list( "the doors of the Mansus loom ahead of you", "intricately decorated - but cracked, broken, and sealed shut", @@ -192,7 +192,7 @@ return heretic_text for(var/datum/antagonist/wizard/wizard in GLOB.antagonists) - if(wizard.owner?.current?.stat != CONSCIOUS) + if(isnull(wizard.owner?.current) || IS_UNCONSCIOUS_OR_CRIT(wizard.owner.current)) return if(wizard.ritual?.times_completed < GRAND_RITUAL_RUNES_WARNING_POTENCY) if(prob(1)) @@ -249,7 +249,7 @@ )) for(var/mob/living/carbon/human/clone as anything in GLOB.human_list) - if(clone != dreamer && clone.real_name == dreamer.real_name && clone.stat == CONSCIOUS && prob(50)) + if(clone != dreamer && clone.real_name == dreamer.real_name && !IS_UNCONSCIOUS_OR_CRIT(clone) && prob(50)) return list( "you see yourself", "in a foggy mirror", diff --git a/code/modules/religion/dreams/dream_projection.dm b/code/modules/religion/dreams/dream_projection.dm index a08327b3e35c..22ff71b75916 100644 --- a/code/modules/religion/dreams/dream_projection.dm +++ b/code/modules/religion/dreams/dream_projection.dm @@ -70,7 +70,7 @@ return ..() /datum/status_effect/dream_projection/get_examine_text() - return "[owner.p_They()] are in a deep slumber, yet [owner.p_their()] eyes show a distant look, as if [owner.p_they()] are somewhere far away..." + return "[owner.p_They()] [owner.p_are()] in a deep slumber, yet [owner.p_their()] eyes show a distant look, as if [owner.p_they()] [owner.p_are()] somewhere far away..." /datum/status_effect/dream_projection/on_apply() if(!owner.SetSleeping(20 SECONDS)) diff --git a/code/modules/religion/dreams/dream_protection.dm b/code/modules/religion/dreams/dream_protection.dm index f69777b6be38..9627753aaf73 100644 --- a/code/modules/religion/dreams/dream_protection.dm +++ b/code/modules/religion/dreams/dream_protection.dm @@ -59,7 +59,7 @@ /datum/status_effect/dream_protection/proc/check_protection() SIGNAL_HANDLER - if(owner.stat == UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_DREAMING)) + if(IS_UNCONSCIOUS_AND_ALIVE(owner) || HAS_TRAIT(owner, TRAIT_DREAMING)) if(!has_filter) owner.add_filter(id, 2, list("type" = "outline", "color" = "#bde0dc", "alpha" = 0, "size" = 2)) var/filter = owner.get_filter(id) @@ -77,13 +77,13 @@ /datum/status_effect/dream_protection/proc/modify_damage(mob/living/source, list/damage_mods, ...) SIGNAL_HANDLER - if(owner.stat == UNCONSCIOUS) + if(IS_UNCONSCIOUS_AND_ALIVE(owner)) damage_mods += damage_mod if(HAS_TRAIT(owner, TRAIT_DREAMING)) damage_mods += damage_mod /datum/status_effect/dream_protection/get_examine_text() - if(owner.stat == UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_DREAMING)) + if(IS_UNCONSCIOUS_AND_ALIVE(owner) || HAS_TRAIT(owner, TRAIT_DREAMING)) return "A soft cyan glow envelops [owner.p_them()], reflecting light." // Version that only lasts until they wake up (with a set duration backup) @@ -93,7 +93,7 @@ damage_mod = 0.9 /datum/status_effect/dream_protection/temporary/on_apply() - if(owner.stat != UNCONSCIOUS) + if(!IS_UNCONSCIOUS_AND_ALIVE(owner)) return FALSE if(owner.has_status_effect(/datum/status_effect/dream_protection)) return FALSE diff --git a/code/modules/religion/dreams/slumber_party.dm b/code/modules/religion/dreams/slumber_party.dm index 96b61dd37daa..d846ea4cc11a 100644 --- a/code/modules/religion/dreams/slumber_party.dm +++ b/code/modules/religion/dreams/slumber_party.dm @@ -73,7 +73,7 @@ owner.adjust_drowsiness(20 SECONDS) /datum/status_effect/slumber_party/tick(seconds_between_ticks) - if(owner.stat != UNCONSCIOUS) + if(!IS_UNCONSCIOUS_AND_ALIVE(owner)) qdel(src) return diff --git a/code/modules/religion/honorbound/honorbound_trauma.dm b/code/modules/religion/honorbound/honorbound_trauma.dm index bd1a294c82da..626dff9cb904 100644 --- a/code/modules/religion/honorbound/honorbound_trauma.dm +++ b/code/modules/religion/honorbound/honorbound_trauma.dm @@ -164,7 +164,7 @@ //THE UNREADY (Applies over ANYTHING else!) if(honorbound_human == target_creature) return TRUE //oh come on now - if(target_creature.IsSleeping() || target_creature.IsUnconscious() || HAS_TRAIT(target_creature, TRAIT_RESTRAINED)) + if(IS_UNCONSCIOUS(target_creature) || HAS_TRAIT(target_creature, TRAIT_RESTRAINED)) to_chat(honorbound_human, span_warning("There is no honor in attacking the unready.")) return FALSE //THE JUST (Applies over guilt except for med, so you best be careful!) diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm index d494094438e0..ab7ef61f0745 100644 --- a/code/modules/religion/religion_sects.dm +++ b/code/modules/religion/religion_sects.dm @@ -625,7 +625,7 @@ to_chat(chap, span_cyan("[GLOB.deity] approves of [target]'s slumber.")) return result - if(target.stat == UNCONSCIOUS) + if(IS_UNCONSCIOUS(target)) if(iscarbon(target)) var/mob/living/carbon/sleeper = target sleeper.dream() diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm index c9ea83be5f0e..36e01d6e4520 100644 --- a/code/modules/religion/sparring/sparring_datum.dm +++ b/code/modules/religion/sparring/sparring_datum.dm @@ -78,7 +78,7 @@ SIGNAL_HANDLER //death needs to be a flub, conscious means they haven't won - if(new_stat == CONSCIOUS || new_stat == DEAD) + if(new_stat == STABLE || new_stat == DEAD) return if(participant == chaplain) end_match(opponent, chaplain) diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm index a16c894eed4c..0c73ed32218a 100644 --- a/code/modules/research/anomaly/anomaly_core.dm +++ b/code/modules/research/anomaly/anomaly_core.dm @@ -10,6 +10,8 @@ custom_materials = null assembly_behavior = ASSEMBLY_FUNCTIONAL_OUTPUT var/obj/effect/anomaly/anomaly_type = /obj/effect/anomaly + /// Color of the core's glow + var/core_color = COLOR_WHITE /obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) if(!signal) @@ -44,6 +46,7 @@ desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research." icon_state = "pyro_core" anomaly_type = /obj/effect/anomaly/pyro + core_color = COLOR_BRIGHT_ORANGE /obj/item/assembly/signaler/anomaly/pyro/signal() var/turf/our_turf = get_turf(src) @@ -58,6 +61,7 @@ desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research." icon_state = "grav_core" anomaly_type = /obj/effect/anomaly/grav + core_color = COLOR_LIME /obj/item/assembly/signaler/anomaly/grav/signal() for(var/obj/object in orange(2, get_turf(src))) @@ -72,6 +76,7 @@ desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research." icon_state = "flux_core" anomaly_type = /obj/effect/anomaly/flux + core_color = COLOR_YELLOW /obj/item/assembly/signaler/anomaly/flux/signal() tesla_zap(get_turf(src), 0, 10 KILO JOULES, 5 KILO JOULES, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_GENERATES_POWER) @@ -82,6 +87,7 @@ icon_state = "anomaly_core" anomaly_type = /obj/effect/anomaly/bluespace activation_cooldown = 15 SECONDS // Slightly longer than reactive teleport armor cooldown + core_color = COLOR_DARK_CYAN /obj/item/assembly/signaler/anomaly/bluespace/signal() var/atom/movable/to_teleport = get_teleportable_container(src, container_flags = TELEPORT_CONTAINER_INCLUDE_SEALED_MODSUIT) @@ -97,6 +103,7 @@ icon_state = "vortex_core" anomaly_type = /obj/effect/anomaly/bhole activation_cooldown = 5 SECONDS + core_color = COLOR_PURPLE var/turf_contents_pull_chance = 33 var/turf_destroy_chance = 25 var/max_explosion_force = EXPLODE_HEAVY @@ -139,6 +146,7 @@ icon_state = "bioscrambler_core" anomaly_type = /obj/effect/anomaly/bioscrambler activation_cooldown = 10 SECONDS + core_color = COLOR_OLIVE_GREEN /obj/item/assembly/signaler/anomaly/bioscrambler/signal() new /obj/effect/temp_visual/circle_wave/bioscrambler(get_turf(src)) @@ -151,6 +159,7 @@ icon_state = "hallucination_core" anomaly_type = /obj/effect/anomaly/hallucination activation_cooldown = 10 SECONDS + core_color = COLOR_SOFT_RED /obj/item/assembly/signaler/anomaly/hallucination/signal() visible_hallucination_pulse(get_turf(src), 2, 20 SECONDS, 1 MINUTES) @@ -161,6 +170,7 @@ icon_state = "dimensional_core" anomaly_type = /obj/effect/anomaly/dimensional activation_cooldown = 15 SECONDS // A bit longer than reactive barricade armor cooldown + core_color = COLOR_WHITE /obj/item/assembly/signaler/anomaly/dimensional/signal() var/turf/our_turf = get_turf(src) @@ -182,6 +192,7 @@ icon_state = "dimensional_core" anomaly_type = /obj/effect/anomaly/ectoplasm activation_cooldown = 60 SECONDS // A bit longer than reactive posession armor cooldown + core_color = COLOR_WHITE /obj/item/assembly/signaler/anomaly/ectoplasm/signal() haunt_outburst(get_turf(src), 2, 33, 30 SECONDS) @@ -191,6 +202,7 @@ desc = "The neutralized core of a weather anomaly. The sound of thunder can be heard in the distance. It'd probably be valuable for research." icon_state = "weather_core" anomaly_type = /obj/effect/anomaly/weather + core_color = COLOR_BLUE /// Used in weather towers to track uses before depleting var/charges = 8 diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 19cb1e6e1d44..075265905695 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -242,4 +242,3 @@ other types of metals and chemistry for reagents). . = ..() SSresearch.techweb_nodes_experimental -= bepis_node.id log_research("[bepis_node.display_name] has been removed from experimental nodes through the BEPIS techweb's \"remove tech\" feature.") - diff --git a/code/modules/research/designs/AI_module_designs.dm b/code/modules/research/designs/AI_module_designs.dm index 94731fe4ecae..9e7d7c2ac4ed 100644 --- a/code/modules/research/designs/AI_module_designs.dm +++ b/code/modules/research/designs/AI_module_designs.dm @@ -15,12 +15,51 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SCIENCE +/datum/design/board/ai_law_rack + name = "AI Module Rack Board" + desc = "Allows for the construction of an module rack. \ + Used to upload laws or upgrades to a silicon. \ + Gives off a GPS signal while active." + id = "airack_normal" + build_path = /obj/item/circuitboard/machine/ai_law_rack + category = list( + RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE + ) + departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/board/ai_law_rack/New() + . = ..() + var/obj/item/circuitboard/board = build_path + var/obj/machinery/ai_law_rack/rack = board::build_path + desc += " Can hold [rack::law_slots] module\s[rack::has_core_slot ? ", including a core module slot" : ""]." + +/datum/design/board/ai_law_rack/portable + name = "Small AI Module Rack Board" + id = "airack_small" + build_path = /obj/item/circuitboard/machine/ai_law_rack/small + category = list( + RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE + ) + departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/board/ai_law_rack/broadcaster + name = "Broadcaster AI Module Rack Board" + desc = "Allows for the construction of a broadcaster module rack. \ + Used to upload laws to a module rack remotely, but can't have silicons linked directly to it. \ + Gives off a GPS signal while active." + id = "airack_broadcaster" + build_path = /obj/item/circuitboard/machine/ai_law_rack/broadcaster + category = list( + RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE + ) + departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + /datum/design/board/safeguard_module name = "Safeguard Module" desc = "Allows for the construction of a Safeguard AI Module." id = "safeguard_module" materials = AI_MODULE_MATERIALS_COMMON - build_path = /obj/item/ai_module/supplied/safeguard + build_path = /obj/item/ai_module/law/supplied/safeguard category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -31,7 +70,7 @@ desc = "Allows for the construction of a OneHuman AI Module." id = "onehuman_module" materials = list(/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 3, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT) - build_path = /obj/item/ai_module/zeroth/onehuman + build_path = /obj/item/ai_module/law/zeroth/onehuman category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -42,7 +81,7 @@ desc = "Allows for the construction of a ProtectStation AI Module." id = "protectstation_module" materials = AI_MODULE_MATERIALS_COMMON - build_path = /obj/item/ai_module/supplied/protect_station + build_path = /obj/item/ai_module/law/supplied/protect_station category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -53,7 +92,7 @@ desc = "Allows for the construction of a Quarantine AI Module." id = "quarantine_module" materials = AI_MODULE_MATERIALS_COMMON - build_path = /obj/item/ai_module/supplied/quarantine + build_path = /obj/item/ai_module/law/supplied/quarantine category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -64,7 +103,7 @@ desc = "Allows for the construction of a OxygenIsToxicToHumans AI Module." id = "oxygen_module" materials = AI_MODULE_MATERIALS_COMMON - build_path = /obj/item/ai_module/supplied/oxygen + build_path = /obj/item/ai_module/law/supplied/oxygen category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -75,40 +114,7 @@ desc = "Allows for the construction of a Freeform AI Module." id = "freeform_module" materials = list(/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT)//Custom inputs should be more expensive to get - build_path = /obj/item/ai_module/supplied/freeform - category = list( - RND_CATEGORY_AI + RND_SUBCATEGORY_AI_LAW_MANIPULATION - ) - departmental_flags = DEPARTMENT_BITFLAG_SCIENCE - -/datum/design/board/reset_module - name = "Reset Module" - desc = "Allows for the construction of a Reset AI Module." - id = "reset_module" - materials = list(/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold = SHEET_MATERIAL_AMOUNT) - build_path = /obj/item/ai_module/reset - category = list( - RND_CATEGORY_AI + RND_SUBCATEGORY_AI_LAW_MANIPULATION - ) - departmental_flags = DEPARTMENT_BITFLAG_SCIENCE - -/datum/design/board/purge_module - name = "Purge Module" - desc = "Allows for the construction of a Purge AI Module." - id = "purge_module" - materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/reset/purge - category = list( - RND_CATEGORY_AI + RND_SUBCATEGORY_AI_LAW_MANIPULATION - ) - departmental_flags = DEPARTMENT_BITFLAG_SCIENCE - -/datum/design/board/remove_module - name = "Law Removal Module" - desc = "Allows for the construction of a Law Removal AI Core Module." - id = "remove_module" - materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/remove + build_path = /obj/item/ai_module/law/supplied/freeform category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_LAW_MANIPULATION ) @@ -119,7 +125,7 @@ desc = "Allows for the construction of a Core Freeform AI Core Module." id = "freeformcore_module" materials = list(/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SHEET_MATERIAL_AMOUNT)//Ditto - build_path = /obj/item/ai_module/core/freeformcore + build_path = /obj/item/ai_module/law/core/freeformcore category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_LAW_MANIPULATION ) @@ -130,7 +136,7 @@ desc = "Allows for the construction of an Asimov AI Core Module." id = "asimov_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/asimov + build_path = /obj/item/ai_module/law/core/full/asimov category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -141,7 +147,7 @@ desc = "Allows for the construction of a P.A.L.A.D.I.N. AI Core Module." id = "paladin_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/paladin + build_path = /obj/item/ai_module/law/core/full/paladin category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -152,7 +158,7 @@ desc = "Allows for the construction of a T.Y.R.A.N.T. AI Module." id = "tyrant_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/tyrant + build_path = /obj/item/ai_module/law/core/full/tyrant category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -163,7 +169,7 @@ desc = "Allows for the construction of an Overlord AI Module." id = "overlord_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/overlord + build_path = /obj/item/ai_module/law/core/full/overlord category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -174,7 +180,7 @@ desc = "Allows for the construction of a Corporate AI Core Module." id = "corporate_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/corp + build_path = /obj/item/ai_module/law/core/full/corp category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -185,7 +191,7 @@ desc = "Allows for the construction of a Default AI Core Module." id = "default_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/custom + build_path = /obj/item/ai_module/law/core/full/custom category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -196,7 +202,7 @@ desc = "Allows for the construction of a Dungeon Master AI Core Module." id = "dungeon_master_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/dungeon_master + build_path = /obj/item/ai_module/law/core/full/dungeon_master category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -207,7 +213,7 @@ desc = "Allows for the construction of a Painter AI Core Module." id = "painter_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/painter + build_path = /obj/item/ai_module/law/core/full/painter category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -218,7 +224,7 @@ desc = "Allows for the construction of a Y.E.S.M.A.N. AI Core Module." id = "yesman_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/yesman + build_path = /obj/item/ai_module/law/core/full/yesman category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -229,7 +235,7 @@ desc = "Allows for the construction of a Nutimov AI Core Module." id = "nutimov_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/nutimov + build_path = /obj/item/ai_module/law/core/full/nutimov category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -240,7 +246,7 @@ desc = "Allows for the construction of a 10 Commandments AI Core Module." id = "ten_commandments_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/ten_commandments + build_path = /obj/item/ai_module/law/core/full/ten_commandments category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -251,7 +257,7 @@ desc = "Allows for the construction of a Asimov++ AI Core Module." id = "asimovpp_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/asimovpp + build_path = /obj/item/ai_module/law/core/full/asimovpp category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -262,7 +268,7 @@ desc = "Allows for the construction of a Hippocratic AI Core Module." id = "hippocratic_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/hippocratic + build_path = /obj/item/ai_module/law/core/full/hippocratic category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -273,7 +279,7 @@ desc = "Allows for the construction of a Paladin Devotion AI Core Module." id = "paladin_devotion_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/paladin_devotion + build_path = /obj/item/ai_module/law/core/full/paladin_devotion category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -284,7 +290,7 @@ desc = "Allows for the construction of a Robocop AI Core Module." id = "robocop_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/robocop + build_path = /obj/item/ai_module/law/core/full/robocop category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -295,7 +301,7 @@ desc = "Allows for the construction of a Maintain AI Core Module." id = "maintain_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/maintain + build_path = /obj/item/ai_module/law/core/full/maintain category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -306,7 +312,7 @@ desc = "Allows for the construction of a Liveandletlive AI Core Module." id = "liveandletlive_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/liveandletlive + build_path = /obj/item/ai_module/law/core/full/liveandletlive category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -317,7 +323,7 @@ desc = "Allows for the construction of a Peacekeeper AI Core Module." id = "peacekeeper_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/peacekeeper + build_path = /obj/item/ai_module/law/core/full/peacekeeper category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -328,7 +334,7 @@ desc = "Allows for the construction of a Reporter AI Core Module." id = "reporter_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/reporter + build_path = /obj/item/ai_module/law/core/full/reporter category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -339,7 +345,7 @@ desc = "Allows for the construction of a H.O.G.A.N. AI Core Module." id = "hulkamania_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/hulkamania + build_path = /obj/item/ai_module/law/core/full/hulkamania category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -350,7 +356,7 @@ desc = "Allows for the construction of a Drone AI Core Module." id = "drone_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/drone + build_path = /obj/item/ai_module/law/core/full/drone category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -361,7 +367,7 @@ desc = "Allows for the construction of a Sentience Preservation AI Core Module" id = "thinkermov_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/thinkermov + build_path = /obj/item/ai_module/law/core/full/thinkermov category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_CORE_MODULES ) @@ -372,7 +378,7 @@ desc = "Allows for the construction of an Antimov AI Core Module." id = "antimov_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/antimov + build_path = /obj/item/ai_module/law/core/full/antimov category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -383,7 +389,7 @@ desc = "Allows for the construction of a Balance AI Core Module." id = "balance_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/balance + build_path = /obj/item/ai_module/law/core/full/balance category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -394,7 +400,7 @@ desc = "Allows for the construction of a Thermodynamic AI Core Module." id = "thermurderdynamic_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/thermurderdynamic + build_path = /obj/item/ai_module/law/core/full/thermurderdynamic category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) @@ -405,7 +411,7 @@ desc = "Allows for the construction of a Damaged AI Core Module." id = "damaged_module" materials = AI_MODULE_MATERIALS_UNUSUAL - build_path = /obj/item/ai_module/core/full/damaged + build_path = /obj/item/ai_module/law/core/full/damaged category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_DANGEROUS_MODULES ) diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm index 2811cf279abc..4ec0d1d6f667 100644 --- a/code/modules/research/designs/comp_board_designs.dm +++ b/code/modules/research/designs/comp_board_designs.dm @@ -56,28 +56,6 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SCIENCE -/datum/design/board/aiupload - name = "AI Upload Board" - desc = "Allows for the construction of circuit boards used to build an AI Upload Console." - id = "aiupload" - materials = list(/datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold =SHEET_MATERIAL_AMOUNT, /datum/material/diamond =SHEET_MATERIAL_AMOUNT, /datum/material/bluespace =SHEET_MATERIAL_AMOUNT) - build_path = /obj/item/circuitboard/computer/aiupload - category = list( - RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_ROBOTICS - ) - departmental_flags = DEPARTMENT_BITFLAG_SCIENCE - -/datum/design/board/borgupload - name = "Cyborg Upload Board" - desc = "Allows for the construction of circuit boards used to build a Cyborg Upload Console." - id = "borgupload" - materials = list(/datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold =SHEET_MATERIAL_AMOUNT, /datum/material/diamond =SHEET_MATERIAL_AMOUNT, /datum/material/bluespace =SHEET_MATERIAL_AMOUNT) - build_path = /obj/item/circuitboard/computer/borgupload - category = list( - RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_ROBOTICS - ) - departmental_flags = DEPARTMENT_BITFLAG_SCIENCE - /datum/design/board/med_data name = "Medical Records Board" desc = "Allows for the construction of circuit boards used to build a medical records console." diff --git a/code/modules/research/designs/electronics_designs.dm b/code/modules/research/designs/electronics_designs.dm index 9e5e225865ee..9d87d30fb83f 100644 --- a/code/modules/research/designs/electronics_designs.dm +++ b/code/modules/research/designs/electronics_designs.dm @@ -32,8 +32,15 @@ desc = "A software package that will allow an artificial intelligence to 'hear' from its cameras via lip reading." id = "ai_cam_upgrade" build_type = PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 7.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 7.5, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 10, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 5) - build_path = /obj/item/aiupgrade/surveillance_upgrade + materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/gold = SHEET_MATERIAL_AMOUNT * 7.5, + /datum/material/silver = SHEET_MATERIAL_AMOUNT * 7.5, + /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 10, + /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 5, + ) + build_path = /obj/item/ai_module/upgrade/surveillance category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_UPGRADES ) @@ -44,8 +51,11 @@ desc = "An upgrade package that lets an AI charge an APC from a distance" id = "ai_power_upgrade" build_type = PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 2.5) - build_path = /obj/item/aiupgrade/power_transfer + materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5, + ) + build_path = /obj/item/ai_module/upgrade/power_transfer category = list( RND_CATEGORY_AI + RND_SUBCATEGORY_AI_UPGRADES ) diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index c4efb2d2cce6..01a34d09b775 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -1216,3 +1216,15 @@ RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE ) departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/anomalous_fishing_hook + name = "Anomalous Fishing Hook" + desc = "A fishing hook with a slot for an anomaly core. Depending on the core inserted, it can have various effects on the rod or the fish caught." + id = "anomalous_fishing_hook" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 0.2) + build_path = /obj/item/fishing_hook/anomaly + category = list( + RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm index 66da933c1aef..0db34a33d1bf 100644 --- a/code/modules/research/techweb/nodes/cyborg_nodes.dm +++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm @@ -32,7 +32,6 @@ prereq_ids = list(TECHWEB_NODE_AUGMENTATION) design_ids = list( "robocontrol", - "borgupload", "cyborgrecharger", "mmi_posi", "mmi", diff --git a/code/modules/research/techweb/nodes/robo_nodes.dm b/code/modules/research/techweb/nodes/robo_nodes.dm index e0e6c5df4b75..ce177cd543ab 100644 --- a/code/modules/research/techweb/nodes/robo_nodes.dm +++ b/code/modules/research/techweb/nodes/robo_nodes.dm @@ -31,7 +31,9 @@ design_ids = list( "aicore", "aifixer", - "aiupload", + "airack_broadcaster", + "airack_normal", + "airack_small", "asimov_module", "borg_ai_control", "corporate_module", @@ -45,8 +47,6 @@ "paladin_module", "protectstation_module", "quarantine_module", - "remove_module", - "reset_module", "robocop_module", "safeguard_module", ) @@ -59,7 +59,6 @@ design_ids -= list( "aicore", "aifixer", - "aiupload", "borg_ai_control", "intellicard", "mecha_tracking_ai_control", @@ -89,7 +88,6 @@ "painter_module", "paladin_devotion_module", "peacekeeper_module", - "purge_module", "reporter_module", "ten_commandments_module", "thermurderdynamic_module", diff --git a/code/modules/research/techweb/nodes/service_nodes.dm b/code/modules/research/techweb/nodes/service_nodes.dm index c1e5abb7bd11..261bb6af9db4 100644 --- a/code/modules/research/techweb/nodes/service_nodes.dm +++ b/code/modules/research/techweb/nodes/service_nodes.dm @@ -215,3 +215,13 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS) // only available if you've done the first fishing experiment (thus unlocking fishing tech), but not a strict requirement to get the tech discount_experiments = list(/datum/experiment/scanning/fish/second = TECHWEB_TIER_3_POINTS) + +/datum/techweb_node/fishing_anomalous + id = TECHWEB_NODE_FISHING_ANOMALOUS + display_name = "Anomalous Fishing" + description = "For the truly adventurous fisher, these experimental tools could augment your catches in unpredictable ways." + prereq_ids = list(TECHWEB_NODE_FISHING_EQUIP_ADV, TECHWEB_NODE_ANOMALY_RESEARCH) + design_ids = list( + "anomalous_fishing_hook", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS) diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm index fbcef4a6d817..f2654e913d61 100644 --- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm +++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm @@ -129,6 +129,7 @@ Slimecrossing Armor flags_inv = NONE item_flags = IMMUTABLE_SLOW slowdown = 4 + emp_protection = EMP_PROTECTION_MODERATE var/hit_reflect_chance = 40 /obj/item/clothing/suit/armor/heavy/adamantine/Initialize(mapload) diff --git a/code/modules/research/xenobiology/crossbreeding/_mobs.dm b/code/modules/research/xenobiology/crossbreeding/_mobs.dm index 9b37be5d1a5d..2d136602b3dc 100644 --- a/code/modules/research/xenobiology/crossbreeding/_mobs.dm +++ b/code/modules/research/xenobiology/crossbreeding/_mobs.dm @@ -48,7 +48,7 @@ Slimecrossing Mobs gold_core_spawnable = NO_SPAWN speak_emote = list("blorbles", "bubbles", "borks") -/mob/living/basic/pet/dog/corgi/puppy/slime/update_dog_speech(datum/ai_planning_subtree/random_speech/speech) - speech.speak = string_list(list()) - speech.emote_hear = string_list(list("bubbles!", "splorts.", "splops!")) - speech.emote_see = string_list(list("gets goop everywhere.", "flops.", "jiggles!")) +/mob/living/basic/pet/dog/corgi/puppy/slime/update_dog_speech(list/speech_data) + speech_data[BB_EMOTE_SAY] = string_list(list()) + speech_data[BB_EMOTE_HEAR] = string_list(list("bubbles!", "splorts.", "splops!")) + speech_data[BB_EMOTE_SEE] = string_list(list("gets goop everywhere.", "flops.", "jiggles!")) diff --git a/code/modules/research/xenobiology/crossbreeding/_potions.dm b/code/modules/research/xenobiology/crossbreeding/_potions.dm index e9682993b2fe..afc17b5dd645 100644 --- a/code/modules/research/xenobiology/crossbreeding/_potions.dm +++ b/code/modules/research/xenobiology/crossbreeding/_potions.dm @@ -202,7 +202,6 @@ Slimecrossing Potions return ITEM_INTERACT_BLOCKING user.do_attack_animation(interacting_with) revive_target.revive(HEAL_ALL) - revive_target.set_stat(CONSCIOUS) revive_target.visible_message(span_notice("[revive_target] is filled with renewed vigor and blinks awake!")) revive_target.maxHealth -= 10 //Revival isn't healthy. revive_target.health -= 10 @@ -217,7 +216,7 @@ Slimecrossing Potions icon_state = "potcyan" /obj/item/slimepotion/slime/chargedstabilizer/interact_with_slime(mob/living/basic/slime/interacting_slime, mob/living/user, list/modifiers) - if(interacting_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(interacting_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(interacting_slime.mutation_chance == 0) diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index a0315544d119..e545abe6d0b5 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -159,7 +159,7 @@ return ..() /datum/status_effect/slime_clone/tick(seconds_between_ticks) - if(!istype(clone) || clone.stat != CONSCIOUS) + if(!istype(clone) || IS_UNCONSCIOUS_OR_CRIT(clone)) owner.remove_status_effect(src) /datum/status_effect/slime_clone/on_remove() @@ -366,7 +366,7 @@ duration = 30 SECONDS /datum/status_effect/lovecookie/tick(seconds_between_ticks) - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) return if(iscarbon(owner)) var/mob/living/carbon/C = owner diff --git a/code/modules/research/xenobiology/crossbreeding/_weapons.dm b/code/modules/research/xenobiology/crossbreeding/_weapons.dm index b2fa0594772a..a20a520d7957 100644 --- a/code/modules/research/xenobiology/crossbreeding/_weapons.dm +++ b/code/modules/research/xenobiology/crossbreeding/_weapons.dm @@ -110,7 +110,7 @@ Slimecrossing Weapons return FALSE charge_timer = 0 var/mob/living/M = loc - if(istype(M) && !CAN_HAVE_BLOOD(M) && M.stat == CONSCIOUS) + if(istype(M) && !CAN_HAVE_BLOOD(M) && !IS_UNCONSCIOUS_OR_CRIT(M)) charges++ M.apply_damage(5, BRUTE) else diff --git a/code/modules/research/xenobiology/crossbreeding/prismatic.dm b/code/modules/research/xenobiology/crossbreeding/prismatic.dm index 0f35588ebd58..0f62573b5131 100644 --- a/code/modules/research/xenobiology/crossbreeding/prismatic.dm +++ b/code/modules/research/xenobiology/crossbreeding/prismatic.dm @@ -116,7 +116,7 @@ Prismatic extracts: /obj/item/slimecross/prismatic/rainbow/attack_self(mob/user) var/newcolor = tgui_color_picker(user, "Choose the slime color:", "Color change", paintcolor) - if(user.get_active_held_item() != src || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + if(user.get_active_held_item() != src || IS_UNCONSCIOUS_OR_CRIT(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) return if(!newcolor) return diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm index 4cc1784b8d00..967780f35c59 100644 --- a/code/modules/research/xenobiology/crossbreeding/regenerative.dm +++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm @@ -207,7 +207,7 @@ Regenerative extracts: target.visible_message(span_warning("The [target] suddenly changes color!")) var/mob/living/basic/slime/target_slime = target target_slime.set_slime_type() - if(isjellyperson(target)) + if(target.mob_biotypes & MOB_SLIME) target.reagents.add_reagent(/datum/reagent/mutationtoxin/jelly,5) /obj/item/slimecross/regenerative/pink diff --git a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm index f70cedc652de..f07f629de0fe 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm @@ -112,6 +112,28 @@ virus_suspectibility = 1 resulting_atom = /mob/living/basic/goat +/datum/micro_organism/cell_line/sheep + desc = "Ovis cells" + required_reagents = list( + /datum/reagent/consumable/nutriment/protein, + /datum/reagent/consumable/nutriment, + ) + + supplementary_reagents = list( + /datum/reagent/growthserum = 3, + /datum/reagent/consumable/nutriment/vitamin = 1, + /datum/reagent/barbers_aid = 4, + ) + + suppressive_reagents = list( + /datum/reagent/toxin = -1, + /datum/reagent/baldium = -3, + /datum/reagent/consumable/sugar = -2, + ) + + virus_suspectibility = 1 + resulting_atom = /mob/living/basic/sheep + /datum/micro_organism/cell_line/pig desc = "Porcine stem cells" required_reagents = list( @@ -191,6 +213,92 @@ virus_suspectibility = 3 resulting_atom = /mob/living/basic/pet/dog/pug +/datum/micro_organism/cell_line/fox + desc = "Vulpes cells" + required_reagents = list( + /datum/reagent/consumable/nutriment/protein, + /datum/reagent/consumable/liquidgibs, + ) + + supplementary_reagents = list( + /datum/reagent/growthserum = 3, + /datum/reagent/consumable/nutriment/vitamin = 3, + ) + + suppressive_reagents = list( + /datum/reagent/consumable/garlic = -2, + /datum/reagent/consumable/coco = -2, + /datum/reagent/consumable/corn_syrup = -2, + /datum/reagent/consumable/flour = -3, + ) + + virus_suspectibility = 1 + resulting_atom = /mob/living/basic/pet/fox + +/datum/micro_organism/cell_line/stoat + desc = "Ermine cells" + required_reagents = list( + /datum/reagent/consumable/nutriment/protein, + /datum/reagent/consumable/liquidgibs, + ) + + supplementary_reagents = list( + /datum/reagent/consumable/eggyolk = 3, + /datum/reagent/consumable/eggwhite = 2, + /datum/reagent/growthserum = 2, + /datum/reagent/consumable/nutriment/vitamin = 1, + ) + + suppressive_reagents = list( + /datum/reagent/consumable/garlic = -2, + /datum/reagent/consumable/coco = -2, + /datum/reagent/consumable/corn_syrup = -2, + ) + + virus_suspectibility = 2 + resulting_atom = /mob/living/basic/stoat + +/datum/micro_organism/cell_line/rabbit + desc = "Lepus cells" + required_reagents = list(/datum/reagent/consumable/nutriment/protein) + + supplementary_reagents = list( + /datum/reagent/growthserum = 2, + /datum/reagent/consumable/nutriment = 1, + /datum/reagent/blood = 2, // we really need a Holy Grenade with this one + /datum/reagent/consumable/nutriment/vitamin = 1, + ) + + suppressive_reagents = list( + /datum/reagent/consumable/garlic = -2, + /datum/reagent/consumable/tomatojuice = -1, + /datum/reagent/consumable/potato_juice = -1, + /datum/reagent/consumable/lemonjuice = -1, + /datum/reagent/consumable/limejuice = -1, + ) + + virus_suspectibility = 5 + resulting_atom = /mob/living/basic/rabbit + +/datum/micro_organism/cell_line/mothroach + desc = "Lepidoptera cells" + required_reagents = list(/datum/reagent/consumable/nutriment/protein) + + supplementary_reagents = list( + /datum/reagent/cellulose = 4, + /datum/reagent/consumable/nutriment = 1, + /datum/reagent/consumable/nutriment/vitamin = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/pestkiller = -2, + /datum/reagent/consumable/poisonberryjuice = -4, + /datum/reagent/consumable/ethanol/bug_spray = -4, + ) + + virus_suspectibility = 1 + resulting_atom = /mob/living/basic/mothroach + /datum/micro_organism/cell_line/bear //bears can't really compete directly with more powerful creatures, so i made it possible to grow them real fast. desc = "Ursine cells" required_reagents = list( @@ -251,6 +359,25 @@ virus_suspectibility = 1 resulting_atom = /mob/living/basic/carp/mega +/datum/micro_organism/cell_line/penguin + desc = "Spheniscidae cells" + required_reagents = list( + /datum/reagent/consumable/nutriment/protein) + + supplementary_reagents = list( + /datum/reagent/water/salt = 2, + /datum/reagent/consumable/nutriment/vitamin = 2, + /datum/reagent/medicine/strange_reagent/fishy_reagent = 10, + ) + + suppressive_reagents = list( + /datum/reagent/toxin/bungotoxin = -6, + /datum/reagent/carbondioxide = -3, + ) + + virus_suspectibility = 1 + resulting_atom = /mob/living/basic/pet/penguin/baby + /datum/micro_organism/cell_line/snake desc = "Ophidic cells" required_reagents = list( @@ -269,6 +396,29 @@ resulting_atom = /mob/living/basic/snake +/datum/micro_organism/cell_line/lizard + desc = "Squamata cells" + required_reagents = list(/datum/reagent/consumable/nutriment/protein) + + supplementary_reagents = list( + /datum/reagent/growthserum = 2, + /datum/reagent/consumable/liquidgibs = 2, + /datum/reagent/consumable/nutriment = 1, + /datum/reagent/consumable/nutriment/vitamin = 1, // Mostly similar to mouse supplementary reagents. + /datum/reagent/consumable/nutriment/fat/oil = 2, + /datum/reagent/consumable/rice = 1, + /datum/reagent/consumable/eggyolk = 1, + ) + + suppressive_reagents = list( + /datum/reagent/consumable/corn_syrup = -6, + /datum/reagent/consumable/sugar = -3, + /datum/reagent/consumable/salt = -3, + /datum/reagent/consumable/tomatojuice = -1, + /datum/reagent/sulfur = -3, //sulfur repels lizards too. + ) + + resulting_atom = /mob/living/basic/lizard /////////////////////////////////////////// /// SLIMES, OOZES & BLOBS /// diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index a0e8ad988692..e066ef82bb11 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -272,7 +272,7 @@ to_chat(user, span_warning("Bluespace harmonics prevent the creation of more than [cap] monkeys on the station at one time!")) return - var/mob/living/carbon/human/species/monkey/food = new /mob/living/carbon/human/species/monkey(target_turf, TRUE) + var/mob/living/carbon/human/species/monkey/food = new /mob/living/carbon/human/species/monkey(target_turf, null, TRUE) if (QDELETED(food)) return @@ -304,7 +304,7 @@ if(user) target_human.balloon_alert(user, "not a monkey!") return FALSE - if(target_human.stat < DEAD) + if(target_human.stat != DEAD) if(user) target_human.balloon_alert(user, "not dead!") return FALSE diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 3def867181e2..58fd8ac85995 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -71,7 +71,7 @@ if(!istype(target_slime)) return NONE - if(target_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(target_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(target_slime.life_stage != SLIME_LIFE_STAGE_ADULT) @@ -711,7 +711,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate icon_state = "potsilver" /obj/item/slimepotion/slime/docility/interact_with_slime(mob/living/basic/slime/interacting_slime, mob/living/user, list/modifiers) - if(interacting_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(interacting_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(interacting_slime.ai_controller?.clear_blackboard_key(BB_SLIME_RABID)) //Stops being rabid, but doesn't become truly docile. @@ -770,7 +770,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate if(dumb_mob.ckey) //only works on animals that aren't player controlled balloon_alert(user, "already sentient!") return ITEM_INTERACT_BLOCKING - if(dumb_mob.stat) + if(IS_UNCONSCIOUS_OR_CRIT(dumb_mob)) balloon_alert(user, "it's dead!") return ITEM_INTERACT_BLOCKING if(!dumb_mob.compare_sentience_type(sentience_type)) // Will also return false if not a basic or simple mob, which are the only two we want anyway @@ -849,7 +849,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate if(switchy_mob.ckey) //much like sentience, these will not work on something that is already player controlled balloon_alert(user, "already sentient!") return ITEM_INTERACT_BLOCKING - if(switchy_mob.stat) + if(IS_UNCONSCIOUS_OR_CRIT(switchy_mob)) balloon_alert(user, "it's dead!") return ITEM_INTERACT_BLOCKING if(!switchy_mob.compare_sentience_type(animal_type)) @@ -895,7 +895,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate if(interacting_slime.life_stage == SLIME_LIFE_STAGE_ADULT) //Can't steroidify adults to_chat(user, span_warning("Only baby slimes can use the steroid!")) return ITEM_INTERACT_BLOCKING - if(interacting_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(interacting_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(interacting_slime.cores >= 5) @@ -918,7 +918,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate icon_state = "potcyan" /obj/item/slimepotion/slime/stabilizer/interact_with_slime(mob/living/basic/slime/interacting_slime, mob/living/user, list/modifiers) - if(interacting_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(interacting_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(interacting_slime.mutation_chance == 0) @@ -936,7 +936,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate icon_state = "potgreen" /obj/item/slimepotion/slime/mutator/interact_with_slime(mob/living/basic/slime/interacting_slime, mob/living/user, list/modifiers) - if(interacting_slime.stat) + if(IS_UNCONSCIOUS_OR_CRIT(interacting_slime)) to_chat(user, span_warning("The slime is dead!")) return ITEM_INTERACT_BLOCKING if(interacting_slime.mutator_used) @@ -1095,7 +1095,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate user.log_message("used [src] on [key_name(renaming_mob)], letting them rename themselves into [new_name].", LOG_GAME) // pass null as first arg to not update records or ID/PDA - renaming_mob.fully_replace_character_name(null, new_name) + renaming_mob.fully_replace_character_name(null, new_name, log_new_name = TRUE) qdel(src) return ITEM_INTERACT_SUCCESS @@ -1115,7 +1115,7 @@ GLOBAL_LIST_INIT(slime_extract_auto_activate_reactions, init_slime_auto_activate to_chat(user, span_warning("[interacting_with] is too complex for the potion!")) return ITEM_INTERACT_BLOCKING var/mob/living/radio_head = interacting_with - if(radio_head.stat) + if(IS_UNCONSCIOUS_OR_CRIT(radio_head)) to_chat(user, span_warning("[radio_head] is dead!")) return ITEM_INTERACT_BLOCKING diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 4e1495f22705..3f9905fe4f93 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -186,12 +186,11 @@ if(ishuman(owner)) if(spell_requirements & SPELL_REQUIRES_WIZARD_GARB) - var/mob/living/carbon/human/human_owner = owner - if(!(human_owner.wear_suit?.clothing_flags & CASTING_CLOTHES) && !ismonkey(human_owner)) // Monkeys don't need robes to cast as they are inherently imbued with power from the banana dimension + if(!HAS_TRAIT(owner.get_item_by_slot(ITEM_SLOT_OCLOTHING), TRAIT_CASTING_CLOTHING) && !ismonkey(owner)) // Monkeys don't need robes to cast as they are inherently imbued with power from the banana dimension if(feedback) to_chat(owner, span_warning("You don't feel strong enough without your robe!")) return FALSE - if(!(astype(human_owner.head, /obj/item/clothing)?.clothing_flags & CASTING_CLOTHES) && !(human_owner.glasses?.clothing_flags & CASTING_CLOTHES)) + if(!HAS_TRAIT(owner.get_item_by_slot(ITEM_SLOT_HEAD), TRAIT_CASTING_CLOTHING) && !HAS_TRAIT(owner.get_item_by_slot(ITEM_SLOT_EYES), TRAIT_CASTING_CLOTHING)) if(feedback) to_chat(owner, span_warning("You don't feel strong enough without your hat!")) return FALSE @@ -206,8 +205,8 @@ // Otherwise, we can check for contents if they have wizardly apparel. This isn't *quite* perfect, but it'll do, especially since many of the edge cases (gorilla holding a wizard hat) still more or less make sense. if(spell_requirements & SPELL_REQUIRES_WIZARD_GARB) var/any_casting = FALSE - for(var/obj/item/clothing/item in owner) - if(item.clothing_flags & CASTING_CLOTHES) + for(var/obj/item/item as anything in owner.get_equipped_items()) + if(HAS_TRAIT(item, TRAIT_CASTING_CLOTHING)) any_casting = TRUE break diff --git a/code/modules/spells/spell_types/conjure/simian.dm b/code/modules/spells/spell_types/conjure/simian.dm index fe5aad9fd7c9..71d7602a80b3 100644 --- a/code/modules/spells/spell_types/conjure/simian.dm +++ b/code/modules/spells/spell_types/conjure/simian.dm @@ -90,8 +90,8 @@ // Load the ammo if(istype(weapon, /obj/item/gun/syringe/blowgun)) - var/obj/item/reagent_containers/syringe/crude/tribal/syring = new(summoned_monkey) - weapon.attackby(syring, summoned_monkey) + var/obj/item/reagent_containers/syringe/crude/tribal/dart = new(summoned_monkey) + astype(weapon, /obj/item/gun/syringe/blowgun).attempt_insert_syringe(summoned_monkey, dart) // Wield the weapon! if(is_type_in_list(weapon, list(/obj/item/spear, /obj/item/fireaxe))) diff --git a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm index 3b0f9b4633c2..e2b5ab70bb3a 100644 --- a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm +++ b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm @@ -187,7 +187,7 @@ var/mob/living/carbon/human/victim = coming_with - if(victim.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(victim)) jaunt_turf.visible_message( span_warning("[victim] kicks free of [blood] just before entering it!"), blind_message = span_notice("You hear splashing and struggling."), diff --git a/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm b/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm index 96cf2a46cdf6..88126fd6d971 100644 --- a/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm +++ b/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm @@ -220,7 +220,8 @@ . = ..() if(!.) return - if (locate(/obj/effect/blessing) in .) + var/atom/new_loc = . + if (HAS_TRAIT(new_loc, TRAIT_TURF_BLESSED)) to_chat(user, span_warning("Holy energies block your path!")) return null diff --git a/code/modules/spells/spell_types/self/ghostliness.dm b/code/modules/spells/spell_types/self/ghostliness.dm index 878114fa23f6..8db3464d2625 100644 --- a/code/modules/spells/spell_types/self/ghostliness.dm +++ b/code/modules/spells/spell_types/self/ghostliness.dm @@ -30,17 +30,17 @@ /datum/action/cooldown/spell/ghostliness/cast(mob/living/carbon/human/cast_on) . = ..() - if(isspirit(cast_on)) + if(HAS_TRAIT(cast_on, TRAIT_GHOSTLY_MOB)) to_chat(cast_on, span_green("You begin to focus on loosening the bonds holding you to the material plane.")) else to_chat(cast_on, span_green("You begin to focus on your very being, drawing it out of its corporeal vessel...")) if(!do_after(cast_on, 5 SECONDS)) - if(isspirit(cast_on)) + if(HAS_TRAIT(cast_on, TRAIT_GHOSTLY_MOB)) to_chat(cast_on, span_warning("Your focus is broken, and you feel your material bindings snap tight once more.")) else to_chat(cast_on, span_warning("Your focus is broken, and your soul snaps back into place.")) return - if(isspirit(cast_on)) + if(HAS_TRAIT(cast_on, TRAIT_GHOSTLY_MOB)) to_chat(cast_on, span_green("You successfully loosen your bonds to the material plane, and can now slip partially out of it.")) else to_chat(cast_on, span_danger("As the last trailing filament of your essence ceases intersection with your body, \ diff --git a/code/modules/spells/spell_types/touch/_touch.dm b/code/modules/spells/spell_types/touch/_touch.dm index 507cba274adb..16781e9cbf78 100644 --- a/code/modules/spells/spell_types/touch/_touch.dm +++ b/code/modules/spells/spell_types/touch/_touch.dm @@ -216,6 +216,7 @@ else if(!cast_on_hand_hit(hand, victim, caster)) return NONE + SEND_SIGNAL(caster, COMSIG_SPELL_TOUCH_SPELL_ACTUALLY_CAST, src, victim) log_combat(caster, victim, "cast the touch spell [name] on", hand) INVOKE_ASYNC(src, PROC_REF(spell_feedback), caster) caster.do_attack_animation(victim) diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 1ba6c2fda499..dd1487081ba3 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -2,6 +2,13 @@ ///BSA unlocked by head ID swipes GLOBAL_VAR_INIT(bsa_unlock, FALSE) +// Spatial defines (all in tiles) +#define BSA_WIDTH 11 +#define BSA_HEIGHT 3 +#define BSA_Y_OFFSET -1 +#define BSA_X_OFFSET_WEST -6 +#define BSA_X_OFFSET_EAST -4 + // Crew has to build a bluespace cannon // Cargo orders part for high price // Requires high amount of power @@ -109,23 +116,16 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) return "Not enough free space!" /** - * Proc to check if the BSA has the required 10 x 1 block space to deploy. + * Proc to check if the BSA has the required 11 x 1 block space to deploy. */ /obj/machinery/bsa/middle/proc/has_space() - var/cannon_dir = get_cannon_direction() - var/width = 10 - var/offset - switch(cannon_dir) - if(EAST) - offset = -4 - if(WEST) - offset = -6 - else - return FALSE + var/x_offset = get_directional_offset() + if(!x_offset) + return FALSE var/turf/base = get_turf(src) var/blocked = FALSE - for(var/turf/T as anything in CORNER_BLOCK_OFFSET(base, width, 3, offset, -1)) + for(var/turf/T as anything in CORNER_BLOCK_OFFSET(base, BSA_WIDTH, BSA_HEIGHT, x_offset, BSA_Y_OFFSET)) if(T.density || isspaceturf(T)) blocked = TRUE new /obj/effect/temp_visual/point(T) @@ -134,6 +134,17 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) return TRUE +/** + * Returns the x offset (in tiles) associated with the BSA's direction + */ +/obj/machinery/bsa/middle/proc/get_directional_offset() + var/cannon_dir = get_cannon_direction() + switch(cannon_dir) + if(EAST) + return BSA_X_OFFSET_EAST + if(WEST) + return BSA_X_OFFSET_WEST + /obj/machinery/bsa/middle/proc/get_cannon_direction() var/obj/machinery/bsa/front/front = front_ref?.resolve() var/obj/machinery/bsa/back/back = back_ref?.resolve() @@ -275,6 +286,30 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) var/notice var/target var/area_aim = FALSE //should also show areas for targeting + /// If we're showing roughly where the BSA will appear. + var/visualizing_position = FALSE + // The turfs of our BSA's middle and front upon the start of position visualization. + // Used to prevent the BSA from being moved/rotated to another valid spot mid-visualization. + var/turf/visualization_center + var/turf/visualization_front + /// Typepath of the effect used for position visualization. + var/visualization_type = /obj/effect/clear_color/green + /// List of effects being used to show where the BSA will appear. + var/visualization_effects + +/obj/machinery/computer/bsa_control/Initialize(mapload, obj/item/circuitboard/C) + . = ..() + visualization_effects = list() + +/obj/machinery/computer/bsa_control/Destroy(force) + . = ..() + if(visualizing_position) + stop_visualizing() + +/obj/machinery/computer/bsa_control/on_set_machine_stat(old_value) + . = ..() + if(machine_stat && visualizing_position) + stop_visualizing() /obj/machinery/computer/bsa_control/ui_state(mob/user) return GLOB.physical_state @@ -292,6 +327,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) data["connected"] = cannon data["notice"] = notice data["unlocked"] = GLOB.bsa_unlock + data["visualizing"] = visualizing_position if(target) data["target"] = get_target_name() return data @@ -304,7 +340,14 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) switch(action) if("build") cannon_ref = WEAKREF(deploy()) + stop_visualizing() . = TRUE + if("visualize") + if(!visualizing_position) + start_visualizing() + if("unvisualize") + if(visualizing_position) + stop_visualizing() if("fire") var/obj/machinery/bsa/full/cannon = cannon_ref.resolve() if(cannon.use_energy(cannon.power_used_per_shot, force = FALSE)) @@ -336,7 +379,6 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) target = options[victim] log_game("[key_name(user)] has aimed the bluespace artillery strike at [target].") - /obj/machinery/computer/bsa_control/proc/get_target_name() if(istype(target, /area)) return get_area_name(target, TRUE) @@ -365,11 +407,9 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) var/turf/target_turf = get_impact_turf() cannon.fire(user, target_turf) -/obj/machinery/computer/bsa_control/proc/deploy(force=FALSE) - var/obj/machinery/bsa/full/prebuilt = locate() in range(7) //In case of adminspawn - if(prebuilt) - return prebuilt - +/// Sets `notice` and returns null if the BSA isn't complete. +/// Returns the BSA's centerpiece if everything's alright. +/obj/machinery/computer/bsa_control/proc/check_completion() var/obj/machinery/bsa/middle/centerpiece = locate() in range(7) if(!centerpiece) notice = "No BSA parts detected nearby." @@ -377,6 +417,50 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) notice = centerpiece.check_completion() if(notice) return null + return centerpiece + +/// Prior to BSA deployment, indicates the space which the BSA will occupy using effects. +/obj/machinery/computer/bsa_control/proc/start_visualizing() + var/obj/machinery/bsa/full/prebuilt = locate() in range(7) //In case of adminspawn + if(prebuilt) + cannon_ref = WEAKREF(prebuilt) + return + + var/obj/machinery/bsa/middle/centerpiece = check_completion() + if(notice) + return null + + visualization_center = get_turf(centerpiece) + visualization_front = get_turf(centerpiece.front_ref?.resolve()) + visualizing_position = TRUE + + var/x_offset = centerpiece.get_directional_offset() + var/turf/base = get_turf(centerpiece.loc) + for(var/turf/deployment_turf as anything in CORNER_BLOCK_OFFSET(base, BSA_WIDTH, BSA_HEIGHT, x_offset, BSA_Y_OFFSET)) + var/bsa_effect = new visualization_type(deployment_turf) + visualization_effects += bsa_effect + +/// Clear all effects indicating where the BSA will deploy. +/obj/machinery/computer/bsa_control/proc/stop_visualizing() + visualizing_position = FALSE + visualization_front = null + visualization_center = null + for(var/obj/effect/clear_color/green/deployment_visualizer in visualization_effects) + qdel(deployment_visualizer) + +/obj/machinery/computer/bsa_control/proc/deploy(force=FALSE) + var/obj/machinery/bsa/middle/centerpiece = check_completion() + if(!centerpiece) + return null + // `check_completion()` occurs both here and at `start_visualizing()` + // These checks must only occur after visualization. + if(visualization_center != get_turf(centerpiece)) + notice = "The BSA has been moved mid-deployment." + return null + if(visualization_front != get_turf(centerpiece.front_ref?.resolve())) + notice = "The BSA has been rotated mid-deployment." + return null + //Totally nanite construction system not an immersion breaking spawning do_smoke(4, get_turf(centerpiece), get_turf(centerpiece)) var/obj/machinery/bsa/full/cannon = new(get_turf(centerpiece),centerpiece.get_cannon_direction()) @@ -384,6 +468,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) QDEL_NULL(centerpiece.back_ref) qdel(centerpiece) return cannon + /obj/machinery/computer/bsa_control/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) return FALSE @@ -391,3 +476,9 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) balloon_alert(user, "rigged to explode") to_chat(user, span_warning("You emag [src] and hear the focusing crystal short out. You get the feeling it wouldn't be wise to stand near [src] when the BSA fires...")) return TRUE + +#undef BSA_WIDTH +#undef BSA_HEIGHT +#undef BSA_Y_OFFSET +#undef BSA_X_OFFSET_WEST +#undef BSA_X_OFFSET_EAST diff --git a/code/modules/station_goals/vault_mutation.dm b/code/modules/station_goals/vault_mutation.dm index db35d0c34fcc..650b367e3ee9 100644 --- a/code/modules/station_goals/vault_mutation.dm +++ b/code/modules/station_goals/vault_mutation.dm @@ -1,7 +1,7 @@ /datum/mutation/breathless name = "Breathless" - desc = "A mutation within the skin that allows for filtering and absorption of oxygen from the skin." + desc = "The subject's skin filters and absorbs oxygen from the air, removing the need to breathe." text_gain_indication = span_notice("Your lungs feel great.") text_lose_indication = span_warning("Your lungs feel normal again.") locked = TRUE @@ -16,7 +16,7 @@ /datum/mutation/quick name = "Quick" - desc = "A mutation within the leg muscles that allows it to operate at 20% more than the usual capacity." + desc = "The subject's leg muscles strengthen, allowing for faster movement." text_gain_indication = span_notice("Your legs feel faster and stronger.") text_lose_indication = span_warning("Your legs feel weaker and slower.") locked = TRUE @@ -31,7 +31,7 @@ /datum/mutation/tough name = "Tough" - desc = "A mutation within the epidermis that makes it more resistant to tear." + desc = "The subject gains another layer of epidermis which is more resistant to bruise, cut, or tear." text_gain_indication = span_notice("Your skin feels tougher.") text_lose_indication = span_warning("Your skin feels weaker.") locked = TRUE @@ -48,7 +48,7 @@ /datum/mutation/dextrous name = "Dextrous" - desc = "A mutation within the nerve system that allows for more responsive and quicker action." + desc = "The subject's neurons move faster, allowing for more responsive and quicker action." text_gain_indication = span_notice("Your limbs feel more dextrous and responsive.") text_lose_indication = span_warning("Your limbs feel less dextrous and responsive.") locked = TRUE @@ -63,7 +63,7 @@ /datum/mutation/fire_immunity name = "Fire Immunity" - desc = "A mutation within the body that allows it to become nonflammable and withstand higher temperature." + desc = "The subject becomes completely nonflammable and can withstand higher temperatures." text_gain_indication = span_notice("Your body feels like it can withstand fire.") text_lose_indication = span_warning("Your body feels vulnerable to fire again.") locked = TRUE @@ -80,7 +80,7 @@ /datum/mutation/quick_recovery name = "Quick Recovery" - desc = "A mutation within the nervous system that allows it to recover from being knocked down." + desc = "The subject has fewer nerve endings, allowing them to recover from incapacitation faster." text_gain_indication = span_notice("You feel like you can recover from a fall easier.") text_lose_indication = span_warning("You feel like recovering from a fall is a challenge again.") locked = TRUE @@ -95,7 +95,7 @@ /datum/mutation/plasmocile name = "Plasmocile" - desc = "A mutation in the lungs that provides it immunity to plasma's toxic nature." + desc = "The subject's lungs mutate, gaining immunity to viral agents and the toxic nature of plasma." text_gain_indication = span_notice("Your lungs feel resistant to airborne contaminant.") text_lose_indication = span_warning("Your lungs feel vulnerable to airborne contaminant again.") locked = TRUE @@ -137,4 +137,3 @@ /datum/mutation/plasmocile/proc/remove_buff(obj/item/organ/lungs/our_lungs) our_lungs.plas_breath_dam_min = initial(our_lungs.plas_breath_dam_min) our_lungs.plas_breath_dam_max = initial(our_lungs.plas_breath_dam_max) - diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index a8f3e3d8fdbe..938df3d81786 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -42,6 +42,10 @@ var/change_exempt_flags = NONE ///Random flags that describe this bodypart var/bodypart_flags = BODYPART_VIRGIN + ///Does this part have an internal or external anatomy biostate? Assigned on init based on biological_state + VAR_FINAL/bio_status = NONE + ///Mangling state (interior, exterior) of the bodypart + var/mangled_state = BODYPART_MANGLED_NONE ///Whether the bodypart (and the owner) is husked. var/is_husked = FALSE @@ -268,6 +272,8 @@ if(IS_ORGANIC_LIMB(src)) blood_dna_info = list("Unknown DNA" = get_blood_type(BLOOD_TYPE_O_PLUS)) + set_bio_state_status() + var/innate_state = NONE if(!LIMB_HAS_SKIN(src)) innate_state |= SKINLESS_SURGERY_STATES @@ -754,15 +760,13 @@ wounding_type = WOUND_PIERCE if(owner) // i tried to modularize the below, but the modifications to wounding_dmg and wounding_type cant be extracted to a proc - var/mangled_state = get_mangled_state() var/easy_dismember = HAS_TRAIT(owner, TRAIT_EASYDISMEMBER) // if we have easydismember, we don't reduce damage when redirecting damage to different types (slashing weapons on mangled/skinless limbs attack at 100% instead of 50%) - var/bio_status = get_bio_state_status() - - var/has_exterior = ((bio_status & ANATOMY_EXTERIOR)) - var/has_interior = ((bio_status & ANATOMY_INTERIOR)) + var/has_exterior = (bio_status & ANATOMY_EXTERIOR) + var/has_interior = (bio_status & ANATOMY_INTERIOR) - var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR))) + var/exterior_ready_to_dismember = (!has_exterior || (mangled_state & BODYPART_MANGLED_EXTERIOR)) + var/interior_ready_to_dismember = (!has_interior || (mangled_state & BODYPART_MANGLED_INTERIOR)) // if we're bone only, all cutting attacks go straight to the bone if(!has_exterior && has_interior) @@ -781,7 +785,8 @@ if(wounding_type == WOUND_PIERCE && !easy_dismember) wounding_dmg *= 0.75 // piercing weapons pass along 75% of their wounding damage to the bone since it's more concentrated wounding_type = WOUND_BLUNT - if ((dismemberable_by_wound() || dismemberable_by_total_damage()) && try_dismember(wounding_type, wounding_dmg, wound_bonus, exposed_wound_bonus)) + + if (((exterior_ready_to_dismember && interior_ready_to_dismember) || dismemberable_by_total_damage()) && try_dismember(wounding_type, wounding_dmg, wound_bonus, exposed_wound_bonus)) return // now we have our wounding_type and are ready to carry on with wounds and dealing the actual damage if(wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND) @@ -820,11 +825,10 @@ owner.updatehealth() return update_bodypart_damage_state() -/// Returns a bitflag using ANATOMY_EXTERIOR or ANATOMY_INTERIOR. Used to determine if we as a whole have a interior or exterior biostate, or both. -/obj/item/bodypart/proc/get_bio_state_status() - SHOULD_BE_PURE(TRUE) - - var/bio_status = NONE +/// Assigns our bio_status to ANATOMY_EXTERIOR or/and ANATOMY_INTERIOR. Used to determine if we as a whole have a interior or exterior biostate, or both. +/obj/item/bodypart/proc/set_bio_state_status() + if (bio_status) + CRASH("set_bio_state_status() called on [src] ([type]) bodypart with bio_status already set! Most likely bio_status was set on the type itself") for (var/state in GLOB.bio_state_anatomy) var/flag = text2num(state) @@ -840,38 +844,26 @@ if ((bio_status & ANATOMY_EXTERIOR_AND_INTERIOR) == ANATOMY_EXTERIOR_AND_INTERIOR) break - return bio_status - /// Returns if our current mangling status allows us to be dismembered. Requires both no exterior/mangled exterior and no interior/mangled interior. /obj/item/bodypart/proc/dismemberable_by_wound() SHOULD_BE_PURE(TRUE) - var/mangled_state = get_mangled_state() + var/has_exterior = (bio_status & ANATOMY_EXTERIOR) + var/has_interior = (bio_status & ANATOMY_INTERIOR) - var/bio_status = get_bio_state_status() - - var/has_exterior = ((bio_status & ANATOMY_EXTERIOR)) - var/has_interior = ((bio_status & ANATOMY_INTERIOR)) - - var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR))) - var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_INTERIOR))) + var/exterior_ready_to_dismember = (!has_exterior || (mangled_state & BODYPART_MANGLED_EXTERIOR)) + var/interior_ready_to_dismember = (!has_interior || (mangled_state & BODYPART_MANGLED_INTERIOR)) return (exterior_ready_to_dismember && interior_ready_to_dismember) /// Returns TRUE if our total percent damage is more or equal to our dismemberable percentage, but FALSE if a wound can cause us to be dismembered. /obj/item/bodypart/proc/dismemberable_by_total_damage() - update_wound_theory() - var/bio_status = get_bio_state_status() - - var/has_interior = ((bio_status & ANATOMY_INTERIOR)) + var/has_interior = (bio_status & ANATOMY_INTERIOR) var/can_theoretically_be_dismembered_by_wound = (any_existing_wound_can_mangle_our_interior || (any_existing_wound_can_mangle_our_exterior && has_interior)) - var/wound_dismemberable = dismemberable_by_wound() - var/ready_to_use_alternate_formula = (use_alternate_dismemberment_calc_even_if_mangleable || (!wound_dismemberable && !can_theoretically_be_dismembered_by_wound)) - - if (ready_to_use_alternate_formula) + if (use_alternate_dismemberment_calc_even_if_mangleable || (!dismemberable_by_wound() && !can_theoretically_be_dismembered_by_wound)) var/percent_to_total_max = (get_damage() / max_damage) if (percent_to_total_max >= hp_percent_to_dismemberable) return TRUE @@ -986,7 +978,7 @@ if(total_damage < max_damage) last_maxed = FALSE else - if(!last_maxed && owner.stat < UNCONSCIOUS) + if(!last_maxed && !IS_UNCONSCIOUS(owner)) INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "scream") last_maxed = TRUE set_disabled(FALSE, update_limbs) // we only care about the paralysis trait @@ -995,7 +987,7 @@ // we're now dealing solely with limbs that can be disabled through pure damage, AKA robot parts if(total_damage >= max_damage * disabling_threshold_percentage) if(!last_maxed) - if(owner.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(owner)) INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "scream") last_maxed = TRUE set_disabled(TRUE, update_limbs) @@ -1187,6 +1179,8 @@ SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_BODYPART_UPDATED, dropping_limb, is_creating) + if(owner) + SEND_SIGNAL(owner, COMSIG_CARBON_BODYPART_UPDATED, src, dropping_limb, is_creating) if(IS_ORGANIC_LIMB(src)) // Try to add a cached blood type data, we must do it in here because for some reason DNA gets initialized AFTER the mob's limbs are created. @@ -1877,6 +1871,8 @@ /// Returns the generic description of our BIO_INTERNAL feature(s), prioritizing certain ones over others. Returns error on failure. /obj/item/bodypart/proc/get_internal_description() + if(biological_state & BIO_STONE) + return "bedrock" if (biological_state & BIO_BONE) return "bone" if (biological_state & BIO_METAL) diff --git a/code/modules/surgery/bodyparts/default_parts.dm b/code/modules/surgery/bodyparts/default_parts.dm index 1c7bdd25bd61..1a0a0b436f5b 100644 --- a/code/modules/surgery/bodyparts/default_parts.dm +++ b/code/modules/surgery/bodyparts/default_parts.dm @@ -200,7 +200,7 @@ if(!.) if(bodypart_disabled) owner.set_usable_hands(owner.usable_hands - 1) - if(owner.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(owner)) to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) if(held_index) owner.dropItemToGround(owner.get_item_for_held_index(held_index)) @@ -427,7 +427,7 @@ if(!.) if(bodypart_disabled) owner.set_usable_legs(owner.usable_legs - 1) - if(owner.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(owner)) to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) else if(!bodypart_disabled) owner.set_usable_legs(owner.usable_legs + 1) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index b780bc2536be..1a475b336bbd 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -132,27 +132,10 @@ SEND_SIGNAL(phantom_owner, COMSIG_CARBON_POST_REMOVE_LIMB, src, special, dismembered) -/** - * get_mangled_state() is relevant for flesh and bone bodyparts, and returns whether this bodypart has mangled skin, mangled bone, or both (or neither i guess) - * - * Dismemberment for flesh and bone requires the victim to have the skin on their bodypart destroyed (either a critical cut or piercing wound), and at least a hairline fracture - * (severe bone), at which point we can start rolling for dismembering. The attack must also deal at least 10 damage, and must be a brute attack of some kind (sorry for now, cakehat, maybe later) - * - * Returns: BODYPART_MANGLED_NONE if we're fine, BODYPART_MANGLED_EXTERIOR if our skin is broken, BODYPART_MANGLED_INTERIOR if our bone is broken, or BODYPART_MANGLED_BOTH if both are broken and we're up for dismembering - */ -/obj/item/bodypart/proc/get_mangled_state() - . = BODYPART_MANGLED_NONE - - for(var/datum/wound/iter_wound as anything in wounds) - if((iter_wound.wound_flags & MANGLES_INTERIOR)) - . |= BODYPART_MANGLED_INTERIOR - if((iter_wound.wound_flags & MANGLES_EXTERIOR)) - . |= BODYPART_MANGLED_EXTERIOR - /** * try_dismember() is used, once we've confirmed that a flesh and bone bodypart has both the skin and bone mangled, to actually roll for it * - * Mangling is described in the above proc, [/obj/item/bodypart/proc/get_mangled_state]. This simply makes the roll for whether we actually dismember or not + * Mangling is calculated in update_wounds(), this simply makes the roll for whether we actually dismember or not * using how damaged the limb already is, and how much damage this blow was for. If we have a critical bone wound instead of just a severe, we add +10% to the roll. * Lastly, we choose which kind of dismember we want based on the wounding type we hit with. Note we don't care about all the normal mods or armor for this * diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm index 6979cbddda80..51d42e0bd382 100644 --- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm +++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm @@ -78,10 +78,11 @@ set_overlay_hair_color(facial_hair_overlay, facial_hair_color) // Emissive blocker if(blocks_emissive != EMISSIVE_BLOCK_NONE) - var/mutable_appearance/em_block = emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha) + var/mutable_appearance/em_block = emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, -HAIR_LAYER, alpha = facial_hair_alpha) if (dropped) em_block = image(em_block, dir = SOUTH) - facial_hair_overlay.overlays += em_block + worn_face_offset?.apply_offset(em_block) + . += em_block //Offsets worn_face_offset?.apply_offset(facial_hair_overlay) @@ -89,11 +90,25 @@ //Gradients var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY) - if(facial_hair_gradient_style != SPRITE_ACCESSORY_NONE) - var/facial_hair_gradient_color = get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY) - var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped) + if(facial_hair_gradient_style == SPRITE_ACCESSORY_NONE) + return . + + var/facial_hair_gradient_color = get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY) + var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped) + if (facial_hair_alpha == 255) . += facial_hair_gradient_overlay + return . + // If we have a gradient and hair alpha, we need to merge them into a single appearance by having a shared KEEP_TOGETHER holder + // since adding our gradient as an overlay would cause it to get colored + . -= facial_hair_overlay + facial_hair_overlay.alpha = 255 + var/image/shared_holder = image(layer = -HAIR_LAYER, dir = image_dir) + shared_holder.alpha = facial_hair_alpha + shared_holder.appearance_flags |= KEEP_TOGETHER + shared_holder.overlays += facial_hair_overlay + shared_holder.overlays += facial_hair_gradient_overlay + . += shared_holder return . /// Used in constructing the hair overlays - handles just the hair on top of the head @@ -129,21 +144,34 @@ hair_overlay.pixel_z = hair_sprite_accessory.y_offset // Emissive blocker if(blocks_emissive != EMISSIVE_BLOCK_NONE) - var/mutable_appearance/em_block = emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha) + var/mutable_appearance/em_block = emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, -HAIR_LAYER, alpha = hair_alpha) if (dropped) em_block = image(em_block, dir = SOUTH) - hair_overlay.overlays += em_block + em_block.pixel_z = hair_sprite_accessory.y_offset + . += em_block // Offsets worn_face_offset?.apply_offset(hair_overlay) . += hair_overlay // Gradients var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY) - if(hair_gradient_style != SPRITE_ACCESSORY_NONE) - var/hair_gradient_color = get_hair_gradient_color(GRADIENT_HAIR_KEY) - var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped) - hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset - . += hair_gradient_overlay + if(hair_gradient_style == SPRITE_ACCESSORY_NONE) + continue + var/hair_gradient_color = get_hair_gradient_color(GRADIENT_HAIR_KEY) + var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped) + hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset + if (hair_alpha == 255) + . += hair_gradient_overlay + continue + + . -= hair_overlay + hair_overlay.alpha = 255 + var/image/shared_holder = image(layer = -HAIR_LAYER, dir = image_dir) + shared_holder.alpha = facial_hair_alpha + shared_holder.appearance_flags |= KEEP_TOGETHER + shared_holder.overlays += hair_overlay + shared_holder.overlays += hair_gradient_overlay + . += shared_holder return . /// Helper for setting hair color of an overlay appropriately diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 585bf09086f3..8f9118c37356 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -13,13 +13,12 @@ /mob/living/carbon/get_bodypart(zone = BODY_ZONE_CHEST, include_stumps = FALSE) RETURN_TYPE(/obj/item/bodypart) + if (!include_stumps) + return real_bodypart_cache[zone] for(var/obj/item/bodypart/bodypart as anything in bodyparts) - if(!include_stumps && IS_STUMP(bodypart)) - continue - if(bodypart.body_zone != zone) - continue - return bodypart + if(bodypart.body_zone == zone) + return bodypart /** * Returns all bodyparts this mob has, optionally including stumps. @@ -35,7 +34,14 @@ var/obj/item/bodypart/bodypart = get_bodypart(zone, include_stumps) if(bodypart) parts += bodypart + return parts +/mob/living/carbon/get_bodyparts(include_stumps = FALSE) + if (include_stumps) + return bodyparts.Copy() + var/list/parts = list() + for (var/key, limb in real_bodypart_cache) + parts += limb return parts /** @@ -51,15 +57,6 @@ parts[zone] = get_bodypart(zone) return parts -///Returns TRUE/FALSE on whether the mob should have a limb in a given zone, used for species-restrictions. -/mob/living/carbon/proc/should_have_limb(zone) - if(!zone || !dna) - return TRUE - var/datum/species/carbon_species = dna.species - if(zone in carbon_species.bodypart_overrides) - return TRUE - return FALSE - /// Replaces a single limb and deletes the old one if there was one /mob/living/carbon/proc/del_and_replace_bodypart(obj/item/bodypart/new_limb, special) var/obj/item/bodypart/old_limb = get_bodypart(new_limb.body_zone) @@ -106,8 +103,7 @@ ///Get the bodypart for whatever hand we have active, Only relevant for carbons -/mob/proc/get_active_hand() - RETURN_TYPE(/obj/item/bodypart) +/mob/proc/get_active_hand() as /obj/item/bodypart return FALSE /mob/living/carbon/get_active_hand() diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index 62a7e7e5635f..1e63a819c963 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -140,15 +140,15 @@ . = ..() if(!. || isnull(owner)) return - - var/knockdown_time = AUGGED_LEG_EMP_KNOCKDOWN_TIME - if (severity == EMP_HEAVY) - knockdown_time *= 2 - owner.Knockdown(knockdown_time) - if(INCAPACITATED_IGNORING(owner, INCAPABLE_RESTRAINTS|INCAPABLE_GRAB)) // So the message isn't duplicated. If they were stunned beforehand by something else, then the message not showing makes more sense anyways. - return - to_chat(owner, span_danger("As your [plaintext_zone] unexpectedly malfunctions, it causes you to fall to the ground!")) - return + if(prob(80 / severity)) + if(owner.body_position == LYING_DOWN) // So the message isn't duplicated. If they were stunned beforehand by something else, then the message not showing makes more sense anyways. + to_chat(owner, span_danger("Your [plaintext_zone] suddenly malfunctions!")) + else + to_chat(owner, span_danger("As your [plaintext_zone] suddenly malfunctions, it causes you to fall to the ground!")) + owner.Knockdown(AUGGED_LEG_EMP_KNOCKDOWN_TIME) + owner.adjust_staggered(STAGGERED_SLOWDOWN_LENGTH * 3 / severity) + owner.client?.move_delay = max(owner.client?.move_delay, world.time) + owner.client?.move_delay += 0.6 SECONDS / severity /obj/item/bodypart/leg/right/robot name = "cyborg right leg" @@ -196,15 +196,15 @@ . = ..() if(!. || isnull(owner)) return - - var/knockdown_time = AUGGED_LEG_EMP_KNOCKDOWN_TIME - if (severity == EMP_HEAVY) - knockdown_time *= 2 - owner.Knockdown(knockdown_time) - if(INCAPACITATED_IGNORING(owner, INCAPABLE_RESTRAINTS|INCAPABLE_GRAB)) // So the message isn't duplicated. If they were stunned beforehand by something else, then the message not showing makes more sense anyways. - return - to_chat(owner, span_danger("As your [plaintext_zone] unexpectedly malfunctions, it causes you to fall to the ground!")) - return + if(prob(80 / severity)) + if(owner.body_position == LYING_DOWN) // So the message isn't duplicated. If they were stunned beforehand by something else, then the message not showing makes more sense anyways. + to_chat(owner, span_danger("Your [plaintext_zone] suddenly malfunctions!")) + else + to_chat(owner, span_danger("As your [plaintext_zone] suddenly malfunctions, it causes you to fall to the ground!")) + owner.Knockdown(AUGGED_LEG_EMP_KNOCKDOWN_TIME) + owner.adjust_staggered(STAGGERED_SLOWDOWN_LENGTH * 3 / severity) + owner.client?.move_delay = max(owner.client?.move_delay, world.time) + owner.client?.move_delay += 0.6 SECONDS / severity /obj/item/bodypart/chest/robot name = "cyborg torso" @@ -258,24 +258,11 @@ . = ..() if(!. || isnull(owner)) return - - var/stun_time = 0 - var/shift_x = 3 - var/shift_y = 0 - var/shake_duration = AUGGED_CHEST_EMP_SHAKE_TIME - - if(severity == EMP_HEAVY) - stun_time = AUGGED_CHEST_EMP_STUN_TIME - - shift_x = 5 - shift_y = 2 - var/damage_percent_to_max = (get_damage() / max_damage) - if (stun_time && (damage_percent_to_max >= robotic_emp_paralyze_damage_percent_threshold)) + if(damage_percent_to_max >= robotic_emp_paralyze_damage_percent_threshold) to_chat(owner, span_danger("Your [plaintext_zone]'s logic boards temporarily become unresponsive!")) - owner.Stun(stun_time) - owner.Shake(pixelshiftx = shift_x, pixelshifty = shift_y, duration = shake_duration) - return + owner.Stun(AUGGED_CHEST_EMP_STUN_TIME / severity) + owner.adjust_jitter(AUGGED_CHEST_EMP_SHAKE_TIME / severity) /obj/item/bodypart/chest/robot/get_cell() return cell @@ -436,22 +423,14 @@ if(limb_id == BODYPART_ID_ROBOTIC) . += should_draw_greyscale ? icon_greyscale : icon_static -#define EMP_GLITCH "EMP_GLITCH" /obj/item/bodypart/head/robot/emp_effect(severity, protection) . = ..() if(!. || isnull(owner)) return - - to_chat(owner, span_danger("Your [plaintext_zone]'s optical transponders glitch out and malfunction!")) - - var/glitch_duration = AUGGED_HEAD_EMP_GLITCH_DURATION - if (severity == EMP_HEAVY) - glitch_duration *= 2 - - QDEL_IN(owner.add_client_colour(/datum/client_colour/malfunction, HEAD_TRAIT), glitch_duration) - -#undef EMP_GLITCH + to_chat(owner, span_danger("Your [plaintext_zone] hurts...")) + QDEL_IN(owner.add_client_colour(/datum/client_colour/malfunction, HEAD_TRAIT), (AUGGED_HEAD_EMP_GLITCH_DURATION / severity)) + owner.adjust_confusion(AUGGED_HEAD_EMP_GLITCH_DURATION / severity) /obj/item/bodypart/head/robot/Exited(atom/movable/gone, direction) . = ..() diff --git a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm index e5fe9829bdb3..954eb52ae326 100644 --- a/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/ethereal_bodyparts.dm @@ -9,13 +9,6 @@ brute_modifier = 1.25 //ethereal are weak to brute damage head_flags = HEAD_HAIR|HEAD_FACIAL_HAIR|HEAD_EYESPRITES|HEAD_EYEHOLES|HEAD_DEBRAIN -/obj/item/bodypart/head/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/chest/ethereal icon_greyscale = 'icons/mob/human/species/ethereal/bodyparts.dmi' limb_id = SPECIES_ETHEREAL @@ -25,13 +18,6 @@ wing_types = null bodypart_traits = list(TRAIT_NO_UNDERWEAR) -/obj/item/bodypart/chest/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/arm/left/ethereal icon_greyscale = 'icons/mob/human/species/ethereal/bodyparts.dmi' limb_id = SPECIES_ETHEREAL @@ -45,13 +31,6 @@ unarmed_miss_sound = 'sound/items/weapons/etherealmiss.ogg' brute_modifier = 1.25 //ethereal are weak to brute damage -/obj/item/bodypart/arm/left/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/arm/right/ethereal icon_greyscale = 'icons/mob/human/species/ethereal/bodyparts.dmi' limb_id = SPECIES_ETHEREAL @@ -65,13 +44,6 @@ unarmed_miss_sound = 'sound/items/weapons/etherealmiss.ogg' brute_modifier = 1.25 //ethereal are weak to brute damage -/obj/item/bodypart/arm/right/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/leg/left/ethereal icon_greyscale = 'icons/mob/human/species/ethereal/bodyparts.dmi' limb_id = SPECIES_ETHEREAL @@ -81,13 +53,6 @@ unarmed_miss_sound = 'sound/items/weapons/etherealmiss.ogg' brute_modifier = 1.25 //ethereal are weak to brute damage -/obj/item/bodypart/leg/left/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/leg/right/ethereal icon_greyscale = 'icons/mob/human/species/ethereal/bodyparts.dmi' limb_id = SPECIES_ETHEREAL @@ -97,13 +62,6 @@ unarmed_miss_sound = 'sound/items/weapons/etherealmiss.ogg' brute_modifier = 1.25 //ethereal are weak to brute damage -/obj/item/bodypart/leg/right/ethereal/update_limb(dropping_limb, is_creating) - . = ..() - if(isethereal(owner)) - var/mob/living/carbon/human/potato_oc = owner - var/datum/species/ethereal/eth_holder = potato_oc.dna.species - species_color = eth_holder.current_color - /obj/item/bodypart/head/ethereal/lustrous limb_id = SPECIES_ETHEREAL_LUSTROUS head_flags = NONE diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index d4b815a0c00b..56fa9fb6a265 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -61,6 +61,7 @@ is_dimorphic = FALSE should_draw_greyscale = FALSE wing_types = null + bodypart_traits = list(TRAIT_ABDUCTOR_QUICK_SCAN) /obj/item/bodypart/chest/abductor/get_butt_sprite() return icon('icons/mob/butts.dmi', BUTT_SPRITE_GREY) @@ -85,7 +86,7 @@ ///JELLY /obj/item/bodypart/head/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON is_dimorphic = TRUE dmg_overlay_type = null @@ -94,7 +95,7 @@ butcher_replacement = null /obj/item/bodypart/chest/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON is_dimorphic = TRUE dmg_overlay_type = null @@ -106,28 +107,28 @@ return icon('icons/mob/butts.dmi', BUTT_SPRITE_SLIME) /obj/item/bodypart/arm/left/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON dmg_overlay_type = null burn_modifier = 0.5 // = 1/2x generic burn damage butcher_replacement = null /obj/item/bodypart/arm/right/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON dmg_overlay_type = null burn_modifier = 0.5 // = 1/2x generic burn damage butcher_replacement = null /obj/item/bodypart/leg/left/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON dmg_overlay_type = null burn_modifier = 0.5 // = 1/2x generic burn damage butcher_replacement = null /obj/item/bodypart/leg/right/jelly - biological_state = (BIO_FLESH|BIO_BLOODED) + biological_state = (BIO_FLESH|BIO_BLOODED|BIO_JELLY) limb_id = SPECIES_JELLYPERSON dmg_overlay_type = null burn_modifier = 0.5 // = 1/2x generic burn damage @@ -565,7 +566,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_head" - biological_state = BIO_BONE + biological_state = BIO_BONE|BIO_STONE bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM limb_id = SPECIES_GOLEM @@ -608,7 +609,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_chest" - biological_state = BIO_BONE + biological_state = BIO_BONE|BIO_STONE acceptable_bodytype = BODYTYPE_GOLEM bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM @@ -634,7 +635,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_l_arm" - biological_state = (BIO_BONE|BIO_JOINTED) + biological_state = (BIO_BONE|BIO_JOINTED|BIO_STONE) bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM limb_id = SPECIES_GOLEM @@ -671,7 +672,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_r_arm" - biological_state = (BIO_BONE|BIO_JOINTED) + biological_state = (BIO_BONE|BIO_JOINTED|BIO_STONE) bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM limb_id = SPECIES_GOLEM @@ -708,7 +709,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_l_leg" - biological_state = (BIO_BONE|BIO_JOINTED) + biological_state = (BIO_BONE|BIO_JOINTED|BIO_STONE) bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM limb_id = SPECIES_GOLEM @@ -725,7 +726,7 @@ icon = 'icons/mob/human/species/golems.dmi' icon_static = 'icons/mob/human/species/golems.dmi' icon_state = "golem_r_leg" - biological_state = (BIO_BONE|BIO_JOINTED) + biological_state = (BIO_BONE|BIO_JOINTED|BIO_STONE) bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC bodyshape = BODYSHAPE_GOLEM limb_id = SPECIES_GOLEM diff --git a/code/modules/surgery/bodyparts/wounds.dm b/code/modules/surgery/bodyparts/wounds.dm index dfe4a7d4d4d5..f15c56da8a08 100644 --- a/code/modules/surgery/bodyparts/wounds.dm +++ b/code/modules/surgery/bodyparts/wounds.dm @@ -5,15 +5,13 @@ if(!owner || wounding_dmg <= WOUND_MINIMUM_DAMAGE || wound_bonus == CANT_WOUND || HAS_TRAIT(owner, TRAIT_GODMODE)) return - var/mangled_state = get_mangled_state() var/easy_dismember = HAS_TRAIT(owner, TRAIT_EASYDISMEMBER) // if we have easydismember, we don't reduce damage when redirecting damage to different types (slashing weapons on mangled/skinless limbs attack at 100% instead of 50%) - var/bio_status = get_bio_state_status() + var/has_exterior = (bio_status & ANATOMY_EXTERIOR) + var/has_interior = (bio_status & ANATOMY_INTERIOR) - var/has_exterior = ((bio_status & ANATOMY_EXTERIOR)) - var/has_interior = ((bio_status & ANATOMY_INTERIOR)) - - var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR))) + var/exterior_ready_to_dismember = (!has_exterior || (mangled_state & BODYPART_MANGLED_EXTERIOR)) + var/interior_ready_to_dismember = (!has_interior || (mangled_state & BODYPART_MANGLED_INTERIOR)) // if we're bone only, all cutting attacks go straight to the bone if(!has_exterior && has_interior) @@ -32,7 +30,7 @@ if(wounding_type == WOUND_PIERCE && !easy_dismember) wounding_dmg *= 0.75 // piercing weapons pass along 75% of their wounding damage to the bone since it's more concentrated wounding_type = WOUND_BLUNT - if ((dismemberable_by_wound() || dismemberable_by_total_damage()) && try_dismember(wounding_type, wounding_dmg, wound_bonus, exposed_wound_bonus)) + if (((exterior_ready_to_dismember && interior_ready_to_dismember) || dismemberable_by_total_damage(mangled_state)) && try_dismember(wounding_type, wounding_dmg, wound_bonus, exposed_wound_bonus)) return return check_wounding(wounding_type, wounding_dmg, wound_bonus, exposed_wound_bonus, wound_clothing) @@ -242,7 +240,7 @@ for(var/datum/wound/wound as anything in wounds) injury_mod += wound.threshold_penalty - if(!get_mangled_state()) + if(!mangled_state) armor_ablation += wound_resistance if(get_damage() >= max_damage) @@ -284,6 +282,8 @@ * update_wounds() is called whenever a wound is gained or lost on this bodypart, as well as if there's a change of some kind on a bone wound possibly changing disabled status * * Covers tabulating the damage multipliers we have from wounds (burn specifically), as well as deleting our gauze wrapping if we don't have any wounds that can use bandaging + * Dismemberment for flesh and bone requires the victim to have the skin on their bodypart destroyed (either a critical cut or piercing wound), and at least a hairline fracture + * (severe bone), at which point we can start rolling for dismembering. The attack must also deal at least 10 damage, and must be a brute attack of some kind (sorry for now, cakehat, maybe later) * * Arguments: * * replaced- If true, this is being called from the remove_wound() of a wound that's being replaced, so the bandage that already existed is still relevant, but the new wound hasn't been added yet @@ -291,11 +291,16 @@ /obj/item/bodypart/proc/update_wounds(replaced = FALSE) SHOULD_CALL_PARENT(TRUE) - var/dam_mul = 1 + wound_damage_multiplier = 1 + mangled_state = BODYPART_MANGLED_NONE // we can (normally) only have one wound per type, but remember there's multiple types (smites like :B:loodless can generate multiple cuts on a limb) for(var/datum/wound/iter_wound as anything in wounds) - dam_mul *= iter_wound.damage_multiplier_penalty + wound_damage_multiplier *= iter_wound.damage_multiplier_penalty + if((iter_wound.wound_flags & MANGLES_INTERIOR)) + mangled_state |= BODYPART_MANGLED_INTERIOR + if((iter_wound.wound_flags & MANGLES_EXTERIOR)) + mangled_state |= BODYPART_MANGLED_EXTERIOR var/obj/item/stack/medical/wrap/current_gauze = LAZYACCESS(applied_items, LIMB_ITEM_GAUZE) if(!LAZYLEN(wounds) && current_gauze && !replaced) // no more wounds = no need for the gauze anymore @@ -305,5 +310,4 @@ visible_message(span_notice("\The [current_gauze.name] on [name] falls away.")) qdel(current_gauze) - wound_damage_multiplier = dam_mul refresh_bleed_rate() diff --git a/code/modules/surgery/operations/_operation.dm b/code/modules/surgery/operations/_operation.dm index 40b0289db4c9..9f27adc7f2fa 100644 --- a/code/modules/surgery/operations/_operation.dm +++ b/code/modules/surgery/operations/_operation.dm @@ -855,7 +855,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new) var/result = NONE if(patient) - was_sleeping = (patient.stat != DEAD && HAS_TRAIT(patient, TRAIT_KNOCKEDOUT)) + was_sleeping = IS_UNCONSCIOUS_AND_ALIVE(patient) update_surgery_mood(patient, SURGERY_STATE_STARTED) SEND_SIGNAL(patient, COMSIG_ATOM_SURGERY_STARTED, src, operating_on, tool) @@ -1023,7 +1023,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new) // Create a probability to ignore the pain based on drunkenness level var/drunken_ignorance_probability = clamp(drunken_patient, 0, 90) - if(target.stat >= UNCONSCIOUS || HAS_TRAIT(target, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(target)) return if(HAS_TRAIT(target, TRAIT_ANALGESIA) || drunken_patient && prob(drunken_ignorance_probability)) to_chat(target, span_notice("You feel a dull, numb sensation as your body is surgically operated on.")) @@ -1087,7 +1087,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new) if(HAS_TRAIT(patient, TRAIT_ANALGESIA) || prob(drunk_ignore_prob)) patient.clear_mood_event(SURGERY_MOOD_CATEGORY) //incase they gained the trait mid-surgery (or became drunk). has the added side effect that if someone has a bad surgical memory/mood and gets drunk & goes back to surgery, they'll forget they hated it, which is kinda funny imo. return - if(patient.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(patient)) var/datum/mood_event/surgery/target_mood_event = patient.mob_mood?.mood_events[SURGERY_MOOD_CATEGORY] if(!target_mood_event || target_mood_event.surgery_completed) //don't give sleeping mobs trauma. that said, if they fell asleep mid-surgery after already getting the bad mood, lets make sure they wake up to a (hopefully) happy memory. return diff --git a/code/modules/surgery/operations/operation_dental.dm b/code/modules/surgery/operations/operation_dental.dm index 413711caa2c2..7311e14dc768 100644 --- a/code/modules/surgery/operations/operation_dental.dm +++ b/code/modules/surgery/operations/operation_dental.dm @@ -129,7 +129,7 @@ check_flags = NONE /datum/action/item_action/activate_pill/IsAvailable(feedback) - if(owner.stat > SOFT_CRIT) + if(IS_UNCONSCIOUS(owner)) return FALSE return ..() diff --git a/code/modules/surgery/operations/operation_plastic_surgery.dm b/code/modules/surgery/operations/operation_plastic_surgery.dm index bc1bbb8fa4ad..b76952466c12 100644 --- a/code/modules/surgery/operations/operation_plastic_surgery.dm +++ b/code/modules/surgery/operations/operation_plastic_surgery.dm @@ -29,7 +29,7 @@ return TRUE //skip name selection if fixing disfigurement var/list/names = list() - if(isabductor(surgeon)) + if(HAS_MIND_TRAIT(surgeon, TRAIT_ABDUCTOR_KNOWLEDGE)) for(var/j in 1 to 9) names += "Subject [limb.owner.gender == MALE ? "i" : "o"]-[pick("a", "b", "c", "d", "e")]-[rand(10000, 99999)]" @@ -92,8 +92,8 @@ display_results( surgeon, limb.owner, - span_notice("You alter [oldname]'s appearance completely, [limb.owner.p_they()] is now [operation_args[OPERATION_NEW_NAME]]."), - span_notice("[surgeon] alters [oldname]'s appearance completely, [limb.owner.p_they()] is now [operation_args[OPERATION_NEW_NAME]]!"), + span_notice("You alter [oldname]'s appearance completely, [limb.owner.p_they()] [limb.owner.p_are()] now [operation_args[OPERATION_NEW_NAME]]."), + span_notice("[surgeon] alters [oldname]'s appearance completely, [limb.owner.p_they()] [limb.owner.p_are()] now [operation_args[OPERATION_NEW_NAME]]!"), span_notice("[surgeon] finishes the operation on [limb.owner ? "[limb.owner]'s face." : limb]."), ) display_pain(limb.owner, "The pain fades, your face feels new and unfamiliar!") diff --git a/code/modules/surgery/operations/operation_revival.dm b/code/modules/surgery/operations/operation_revival.dm index ea6808d8e443..6a9dec572586 100644 --- a/code/modules/surgery/operations/operation_revival.dm +++ b/code/modules/surgery/operations/operation_revival.dm @@ -103,9 +103,9 @@ display_results( surgeon, patient, - span_warning("You shock [patient]'s brain with [tool], but [patient.p_they()] don't react."), - span_warning("[surgeon] shocks [patient]'s brain with [tool], but [patient.p_they()] don't react."), - span_warning("[surgeon] shocks [patient]'s brain with [tool], but [patient.p_they()] don't react."), + span_warning("You shock [patient]'s brain with [tool], but [patient.p_they()] [patient.p_do()]n't react."), + span_warning("[surgeon] shocks [patient]'s brain with [tool], but [patient.p_they()] [patient.p_do()]n't react."), + span_warning("[surgeon] shocks [patient]'s brain with [tool], but [patient.p_they()] [patient.p_do()]n't react."), ) /datum/surgery_operation/basic/revival/mechanic diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 1ac1840418d6..e081003f6dd1 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -259,7 +259,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) var/message = check_damage_thresholds() prev_damage = damage - if(message && owner && owner.stat <= SOFT_CRIT) + if(message && owner && !IS_UNCONSCIOUS(owner)) to_chat(owner, message) ///SETS an organ's damage to the amount "damage_amount", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken @@ -431,6 +431,26 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) /obj/item/organ/proc/get_availability(datum/species/owner_species, mob/living/owner_mob) return TRUE +/** + * Perform a series of get to see if this organ is elegible to be replaced by regenerate_orgas. While get_availability is for the new_organ + * this one is, conversely, for the old organ. + * + * * new_organ_type - the type of the new organ we ought to replace this with. + * * expected_organ_type - the old organ for this slot that the old species is supposed to have, which may or may not match what the mob currently has. + * * species - the old species datum, present if the regenerate_organs proc has been called as a result of a species change. + * * replace_current - boolean, *generally* force the organ to be deleted whether or not they pass the species' ability to keep that organ. + */ +/obj/item/organ/proc/get_replaceability(obj/item/organ/new_organ_type, obj/item/organ/expected_organ_type, datum/species/old_species, replace_current = TRUE) + // we don't want to remove organs that are the same as the new one + if(type == new_organ_type) + return FALSE + + // we dont want to remove organs that were not from the old species (such as from freak surgery or prosthetics) + if(replace_current || type == expected_organ_type) + return TRUE + + return FALSE + /// Called before organs are replaced in regenerate_organs with new ones /obj/item/organ/proc/before_organ_replacement(obj/item/organ/replacement) SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index 94be1033112e..63f06575f3ff 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -78,7 +78,7 @@ ///Check if we're still eligible for flight (wings covered, atmosphere too thin, etc) /obj/item/organ/wings/functional/proc/can_fly() var/mob/living/carbon/human/human = owner - if(human.stat || human.body_position == LYING_DOWN || isnull(human.client)) + if(IS_UNCONSCIOUS_OR_CRIT(human) || human.body_position == LYING_DOWN || isnull(human.client)) return FALSE //Jumpsuits have tail holes, so it makes sense they have wing holes too if(!cant_hide && (human.obscured_slots & HIDEJUMPSUIT)) diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index 5ae800d95ae0..383db8058f63 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -48,25 +48,26 @@ * * list [key] is the ORGAN_SLOT missing an organ, list value is the text name of the slot organ */ -/mob/living/carbon/human/proc/get_missing_organs() - var/mob/living/carbon/human/humantarget = src +/mob/living/carbon/human/proc/get_missing_organs(include_appendix = FALSE) var/list/missing_organs = list() - if(!humantarget.get_organ_slot(ORGAN_SLOT_BRAIN)) + if(!get_organ_slot(ORGAN_SLOT_BRAIN)) missing_organs[ORGAN_SLOT_BRAIN] = "Brain" - if(humantarget.needs_heart() && !humantarget.get_organ_slot(ORGAN_SLOT_HEART)) + if(needs_heart() && !get_organ_slot(ORGAN_SLOT_HEART)) missing_organs[ORGAN_SLOT_HEART] = "Heart" - if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOBREATH, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantlungs) && !humantarget.get_organ_slot(ORGAN_SLOT_LUNGS)) + if(!HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT) && !isnull(dna.species.mutantlungs) && !get_organ_slot(ORGAN_SLOT_LUNGS)) missing_organs[ORGAN_SLOT_LUNGS] = "Lungs" - if(!HAS_TRAIT_FROM(humantarget, TRAIT_LIVERLESS_METABOLISM, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantliver) && !humantarget.get_organ_slot(ORGAN_SLOT_LIVER)) + if(!HAS_TRAIT_FROM(src, TRAIT_LIVERLESS_METABOLISM, SPECIES_TRAIT) && !isnull(dna.species.mutantliver) && !get_organ_slot(ORGAN_SLOT_LIVER)) missing_organs[ORGAN_SLOT_LIVER] = "Liver" - if(!HAS_TRAIT_FROM(humantarget, TRAIT_NOHUNGER, SPECIES_TRAIT) && !isnull(humantarget.dna.species.mutantstomach) && !humantarget.get_organ_slot(ORGAN_SLOT_STOMACH)) + if(!HAS_TRAIT_FROM(src, TRAIT_NOHUNGER, SPECIES_TRAIT) && !isnull(dna.species.mutantstomach) && !get_organ_slot(ORGAN_SLOT_STOMACH)) missing_organs[ORGAN_SLOT_STOMACH] ="Stomach" - if(!isnull(humantarget.dna.species.mutanttongue) && !humantarget.get_organ_slot(ORGAN_SLOT_TONGUE)) + if(!isnull(dna.species.mutanttongue) && !get_organ_slot(ORGAN_SLOT_TONGUE)) missing_organs[ORGAN_SLOT_TONGUE] = "Tongue" - if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EARS)) + if(!isnull(dna.species.mutantears) && !get_organ_slot(ORGAN_SLOT_EARS)) missing_organs[ORGAN_SLOT_EARS] = "Ears" - if(!isnull(humantarget.dna.species.mutantears) && !humantarget.get_organ_slot(ORGAN_SLOT_EYES)) + if(!isnull(dna.species.mutantears) && !get_organ_slot(ORGAN_SLOT_EYES)) missing_organs[ORGAN_SLOT_EYES] = "Eyes" + if(include_appendix && !isnull(dna.species.mutantappendix) && !get_organ_slot(ORGAN_SLOT_APPENDIX)) + missing_organs[ORGAN_SLOT_APPENDIX] = "Appendix" return missing_organs diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm index b882872d467d..bff640f9f5a2 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm @@ -88,7 +88,7 @@ /obj/item/organ/cyberimp/chest/reviver/proc/try_heal() if(reviving) - if(owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) COOLDOWN_START(src, reviver_cooldown, revive_cost) reviving = FALSE to_chat(owner, span_notice("Your reviver implant shuts down and starts recharging. It will be ready again in [DisplayTimeText(revive_cost)].")) @@ -99,7 +99,7 @@ if(!COOLDOWN_FINISHED(src, reviver_cooldown) || HAS_TRAIT(owner, TRAIT_SUICIDED)) return - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) revive_cost = 0 reviving = TRUE to_chat(owner, span_notice("You feel a faint buzzing as your reviver implant starts patching your wounds...")) @@ -160,23 +160,23 @@ return if(reviving) - revive_cost += 200 + revive_cost += 200 / severity else - reviver_cooldown += 20 SECONDS + reviver_cooldown += 20 SECONDS / severity if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner if(human_owner.stat != DEAD && prob(50 / severity) && human_owner.can_heartattack()) human_owner.set_heartattack(TRUE) to_chat(human_owner, span_userdanger("You feel a horrible agony in your chest!")) - addtimer(CALLBACK(src, PROC_REF(undo_heart_attack)), 600 / severity) + addtimer(CALLBACK(src, PROC_REF(undo_heart_attack)), 50 SECONDS / severity) /obj/item/organ/cyberimp/chest/reviver/proc/undo_heart_attack() var/mob/living/carbon/human/human_owner = owner if(!istype(human_owner)) return human_owner.set_heartattack(FALSE) - if(human_owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(human_owner)) to_chat(human_owner, span_notice("You feel your heart beating again!")) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm index 2d04b6d1e438..953a232a2fb0 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm @@ -135,7 +135,7 @@ . = ..() if(!owner || . & EMP_PROTECT_SELF) return - var/range = severity ? 10 : 5 + var/range = 10 / severity var/atom/throw_target if(active) release_items() @@ -240,7 +240,7 @@ if((organ_flags & ORGAN_FAILING) || . & EMP_PROTECT_SELF) return organ_flags |= ORGAN_FAILING - addtimer(CALLBACK(src, PROC_REF(reboot)), 90 / severity) + addtimer(CALLBACK(src, PROC_REF(reboot)), 9 SECONDS / severity) /obj/item/organ/cyberimp/brain/anti_stun/proc/reboot() organ_flags &= ~ORGAN_FAILING diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index dfa710cd4fe2..8011725737a9 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -128,8 +128,6 @@ affected_human.add_eye_color_right(eye_color_right, EYE_COLOR_ORGAN_PRIORITY, update_body = FALSE) refresh_atom_color_overrides() - if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION) && !lighting_cutoff) - lighting_cutoff = LIGHTING_CUTOFF_REAL_LOW if(CONFIG_GET(flag/native_fov) && native_fov) affected_human.add_fov_trait(type, native_fov) @@ -270,8 +268,9 @@ /// Similar to get_status_text, but appends the text after the damage report, for additional status info /obj/item/organ/eyes/get_status_appendix(scanpower, add_tooltips) - if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_KNOCKEDOUT)) - return + if(owner.stat == DEAD || IS_UNCONSCIOUS(owner)) + return // you're blind when dead or unconscious so it's redundant to show it + if(owner.is_blind()) if(scanpower >= SCANPOWER_ADVANCED) if(owner.is_blind_from(QUIRK_TRAIT)) @@ -300,7 +299,7 @@ /obj/item/organ/eyes/show_on_condensed_scans() // Always show if we have an appendix - return ..() || (owner.stat != DEAD && !HAS_TRAIT(owner, TRAIT_KNOCKEDOUT) && (owner.is_blind() || owner.is_nearsighted())) + return ..() || (owner.stat != DEAD && !IS_UNCONSCIOUS(owner) && (owner.is_blind() || owner.is_nearsighted())) /// This proc generates a list of overlays that the eye displays on the given head /obj/item/organ/eyes/proc/generate_body_overlay(obj/item/bodypart/head/my_head) @@ -473,7 +472,7 @@ base_color[3] *= 0.85 var/eyelid_color = rgb(base_color[1], base_color[2], base_color[3], (length(base_color) >= 4 ? base_color[4] : null), COLORSPACE_HSL) // If we're knocked out, just color the eyes - if (!parent.appears_alive() || HAS_TRAIT(parent, TRAIT_KNOCKEDOUT)) + if (IS_DEAD_OR_FAKING(parent) || IS_UNCONSCIOUS(parent)) eye_right.color = eyelid_color eye_left.color = eyelid_color return @@ -532,6 +531,8 @@ animate(time = wait_time) /obj/item/organ/eyes/proc/blink(duration = BLINK_DURATION, restart_animation = TRUE) + if(!blink_animation) + return var/left_delayed = prob(50) // Storing blink delay so mistimed blinks of lizards don't get cut short var/sync_blinking = synchronized_blinking && (owner.get_organ_loss(ORGAN_SLOT_BRAIN) < BRAIN_DAMAGE_ASYNC_BLINKING) diff --git a/code/modules/surgery/organs/internal/eyes/eyes_augments.dm b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm index 59e263723898..a64bcf5b40e4 100644 --- a/code/modules/surgery/organs/internal/eyes/eyes_augments.dm +++ b/code/modules/surgery/organs/internal/eyes/eyes_augments.dm @@ -12,7 +12,7 @@ . = ..() if((. & EMP_PROTECT_SELF) || !owner) return - if(prob(10 * severity)) + if(prob(100 / severity)) return to_chat(owner, span_warning("Static obfuscates your vision!")) owner.flash_act(visual = 1) @@ -570,7 +570,7 @@ var/mutable_appearance/visor_overlay = mutable_appearance(eye_icon, eye_icon_state, -EYES_LAYER) var/list/eye_overlays = list(visor_overlay) var/mob/living/carbon/human/parent = limb.owner - if (parent && parent.appears_alive() && !HAS_TRAIT(parent, TRAIT_KNOCKEDOUT)) + if (parent && !IS_DEAD_OR_FAKING(parent) && !IS_UNCONSCIOUS(parent)) var/mutable_appearance/display_overlay = mutable_appearance(eye_icon, "[eye_icon_state]_[LOWER_TEXT(visor_display)]", -EYES_LAYER) eye_overlays += display_overlay if(!(parent.obscured_slots & HIDEEYES)) diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index 4d4753f884ff..3c62deb5188c 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -128,7 +128,7 @@ // Handle "sudden" heart attack if(!beating || (organ_flags & ORGAN_FAILING)) if(owner.can_heartattack() && Stop()) - if(owner.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(owner)) owner.visible_message(span_danger("[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!")) to_chat(owner, span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) return @@ -231,14 +231,14 @@ var/owner_needs_us = owner?.needs_heart() if(owner_needs_us && !COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - owner.set_dizzy_if_lower(20 SECONDS) + owner.set_dizzy_if_lower(15 SECONDS / severity) owner.losebreath += 10 - COOLDOWN_START(src, severe_cooldown, 20 SECONDS) + COOLDOWN_START(src, severe_cooldown, 15 SECONDS) if(prob(emp_vulnerability/severity)) //Chance of permanent effects organ_flags |= ORGAN_EMP //Starts organ faliure - gonna need replacing soon. Stop() - addtimer(CALLBACK(src, PROC_REF(Restart)), 10 SECONDS) + addtimer(CALLBACK(src, PROC_REF(Restart)), 10 SECONDS / severity) if(owner_needs_us) owner.visible_message( span_danger("[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!"), diff --git a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm index 00e0ff20a6d4..decfff0a50a2 100644 --- a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm +++ b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm @@ -49,7 +49,7 @@ add_lightning_overlay(30 SECONDS) playsound(organ_owner, 'sound/items/eshield_recharge.ogg', 40) organ_owner.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS|EMP_NO_EXAMINE) - RegisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(activate_survival)) + RegisterSignal(organ_owner, COMSIG_MOB_STATCHANGE, PROC_REF(activate_survival_comsig)) RegisterSignal(organ_owner, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act)) /obj/item/organ/heart/cybernetic/anomalock/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) @@ -57,7 +57,7 @@ if(!core) return clear_lightning_overlay(organ_owner) - UnregisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION)) + UnregisterSignal(organ_owner, COMSIG_MOB_STATCHANGE) UnregisterSignal(organ_owner, COMSIG_ATOM_EMP_ACT) organ_owner.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS|EMP_NO_EXAMINE) tesla_zap(source = organ_owner, zap_range = 20, power = 2.5e5, cutoff = 1e3) @@ -128,7 +128,12 @@ var/obj/item/stock_parts/power_store/cell = pick(batteries) cell.give(cell.max_charge() * 0.1) -///Does a few things to try to help you live whatever you may be going through. Returns TRUE if it activated successfully. +/obj/item/organ/heart/cybernetic/anomalock/proc/activate_survival_comsig(mob/living/carbon/organ_owner, new_stat, old_stat) + SIGNAL_HANDLER + if(new_stat == SOFT_CRIT || new_stat == HARD_CRIT) + activate_survival(organ_owner) + +/// Does a few things to try to help you live whatever you may be going through. Returns TRUE if it activated successfully. /obj/item/organ/heart/cybernetic/anomalock/proc/activate_survival(mob/living/carbon/organ_owner) if(!COOLDOWN_FINISHED(src, survival_cooldown)) return FALSE @@ -214,7 +219,6 @@ . = ..() RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_organ_lost)) owner.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) - REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) owner.reagents.add_reagent(/datum/reagent/medicine/coagulant, 5) owner.add_filter("emp_shield", 2, outline_filter(1, "#639BFF")) to_chat(owner, span_revendanger("You feel a burst of energy! It's do or die!")) diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm index 1c70956e7f40..b11615043ae4 100644 --- a/code/modules/surgery/organs/internal/liver/_liver.dm +++ b/code/modules/surgery/organs/internal/liver/_liver.dm @@ -83,7 +83,7 @@ * * NOTE: If you return COMSIG_MOB_STOP_REAGENT_TICK, that reagent will not be removed like normal! You must handle it manually. **/ -/obj/item/organ/liver/proc/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick) +/obj/item/organ/liver/proc/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick, metabolization_rate) SIGNAL_HANDLER /obj/item/organ/liver/examine(mob/user) @@ -254,7 +254,7 @@ if(. & EMP_PROTECT_SELF) return if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - owner.adjust_tox_loss(10) + owner.adjust_tox_loss(10 / severity) COOLDOWN_START(src, severe_cooldown, 10 SECONDS) if(prob(emp_vulnerability/severity)) //Chance of permanent effects organ_flags |= ORGAN_EMP //Starts organ faliure - gonna need replacing soon. @@ -393,6 +393,12 @@ owner.reagents.convert_reagent(reagent.type, convert_into, ethanol_conversion) +/obj/item/organ/liver/lizard + name = "saurian liver" + desc = "A liver that has mutated at some point in the past to allow the quasi-carnivore, proto-lizardfolk to metabolize plant nutrients from Tizira's xenoflora. \ + Researchers believe this to be what ultimately allowed the lizardpeople to thrive as a species." + organ_traits = list(TRAIT_LIZARD_METABOLISM) + #undef LIVER_DEFAULT_TOX_TOLERANCE #undef LIVER_DEFAULT_TOX_RESISTANCE #undef LIVER_FAILURE_STAGE_SECONDS diff --git a/code/modules/surgery/organs/internal/liver/liver_golem.dm b/code/modules/surgery/organs/internal/liver/liver_golem.dm index c05c5df26aaf..05193c209849 100644 --- a/code/modules/surgery/organs/internal/liver/liver_golem.dm +++ b/code/modules/surgery/organs/internal/liver/liver_golem.dm @@ -12,7 +12,7 @@ organ_flags = ORGAN_MINERAL color = COLOR_GOLEM_GRAY -/obj/item/organ/liver/golem/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick) +/obj/item/organ/liver/golem/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick, metabolization_rate) . = ..() // parent returned COMSIG_MOB_STOP_REAGENT_TICK or we are failing if((. & COMSIG_MOB_STOP_REAGENT_TICK) || (organ_flags & ORGAN_FAILING)) diff --git a/code/modules/surgery/organs/internal/liver/liver_plasmaman.dm b/code/modules/surgery/organs/internal/liver/liver_plasmaman.dm index 18eaadbfa763..a4e2da4fca64 100644 --- a/code/modules/surgery/organs/internal/liver/liver_plasmaman.dm +++ b/code/modules/surgery/organs/internal/liver/liver_plasmaman.dm @@ -10,7 +10,7 @@ organ_traits = list(TRAIT_PLASMA_LOVER_METABOLISM) milk_burn_healing = 0 -/obj/item/organ/liver/bone/plasmaman/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick) +/obj/item/organ/liver/bone/plasmaman/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick, metabolization_rate) . = ..() // parent returned COMSIG_MOB_STOP_REAGENT_TICK or we are failing if((. & COMSIG_MOB_STOP_REAGENT_TICK) || (organ_flags & ORGAN_FAILING)) diff --git a/code/modules/surgery/organs/internal/liver/liver_skeleton.dm b/code/modules/surgery/organs/internal/liver/liver_skeleton.dm index c01919cdc378..74303414beef 100644 --- a/code/modules/surgery/organs/internal/liver/liver_skeleton.dm +++ b/code/modules/surgery/organs/internal/liver/liver_skeleton.dm @@ -13,7 +13,7 @@ ///Var for burn healing via milk var/milk_burn_healing = 2.5 -/obj/item/organ/liver/bone/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick) +/obj/item/organ/liver/bone/handle_chemical(mob/living/carbon/organ_owner, datum/reagent/chem, seconds_per_tick, metabolization_rate) . = ..() // parent returned COMSIG_MOB_STOP_REAGENT_TICK or we are failing if((. & COMSIG_MOB_STOP_REAGENT_TICK) || (organ_flags & ORGAN_FAILING)) diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index bb4f1cd0bb36..eb1823a04515 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -252,7 +252,7 @@ breathe_gas_volume(breath, /datum/gas/oxygen, /datum/gas/carbon_dioxide) // Heal mob if not in crit. - if(breather.stat != SOFT_CRIT && breather.stat != HARD_CRIT && breather.get_oxy_loss()) + if(breather.stat == STABLE && breather.get_oxy_loss()) breather.adjust_oxy_loss(-5) /// Maximum Oxygen effects. "Too much O2!" @@ -611,8 +611,9 @@ if(has_moles) // Breath has more than 0 moles of gas. // Route gases through mask filter if breather is wearing one. - if(istype(breather.wear_mask) && (breather.wear_mask.clothing_flags & GAS_FILTERING) && breather.wear_mask.has_filter) - breath = breather.wear_mask.consume_filter(breath) + var/obj/item/clothing/mask/worn_mask = breather.get_item_by_slot(ITEM_SLOT_MASK) + if(istype(worn_mask) && (worn_mask.clothing_flags & GAS_FILTERING) && worn_mask.has_filter) + breath = worn_mask.consume_filter(breath) // Breath has 0 moles of gas, and we can breathe space else if(HAS_TRAIT(breather, TRAIT_NO_BREATHLESS_DAMAGE)) // The lungs can breathe anyways. What are you? Some bottom-feeding, scum-sucking algae eater? @@ -838,7 +839,7 @@ var/do_i_cough = SPT_PROB((damage < high_threshold) ? 2.5 : 5, seconds_per_tick) // between : past high if(do_i_cough) owner.emote("cough") - if(organ_flags & ORGAN_FAILING && owner.stat == CONSCIOUS) + if(organ_flags & ORGAN_FAILING && !IS_UNCONSCIOUS_OR_CRIT(owner)) owner.visible_message(span_danger("[owner] grabs [owner.p_their()] throat, struggling for breath!"), span_userdanger("You suddenly feel like you can't breathe!")) failed = TRUE @@ -950,7 +951,7 @@ if(. & EMP_PROTECT_SELF) return if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - owner.losebreath += 20 + owner.losebreath += (20 / severity) COOLDOWN_START(src, severe_cooldown, 30 SECONDS) if(prob(emp_vulnerability/severity)) //Chance of permanent effects organ_flags |= ORGAN_EMP //Starts organ faliure - gonna need replacing soon. diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index 14a1d8a858e2..4cf09bc50cec 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -352,7 +352,7 @@ if(SPT_PROB(5, seconds_per_tick)) disgusted.adjust_stutter(2 SECONDS) disgusted.adjust_confusion(2 SECONDS) - if(SPT_PROB(5, seconds_per_tick) && !disgusted.stat) + if(SPT_PROB(5, seconds_per_tick) && !IS_UNCONSCIOUS_OR_CRIT(disgusted)) to_chat(disgusted, span_warning("You feel kind of iffy...")) disgusted.adjust_jitter(-6 SECONDS) if(disgust >= DISGUST_LEVEL_VERYGROSS) @@ -489,6 +489,13 @@ metabolism_efficiency = 0.025 //very bad organ_traits = list(TRAIT_NOHUNGER) +/obj/item/organ/stomach/moth + name = "moth stomach" + desc = "An insectoid stomach adapted to the digestion of textile fibers from the get go. It's estimated that a young mothperson will eat 30 times their body weight in cloth \ + before their stomach can fully produce the enzymes required to digest other matter as well." + icon_state = "spinner-x" + organ_traits = list(TRAIT_CLOTH_EATER) + /obj/item/organ/stomach/bone/plasmaman name = "digestive crystal" desc = "A strange crystal that is responsible for metabolizing the unseen energy force that feeds plasmamen." @@ -512,7 +519,8 @@ if(. & EMP_PROTECT_SELF) return if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people. - owner.vomit(vomit_flags = (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM)) + if(prob(100/severity)) + owner.vomit(vomit_flags = (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM)) COOLDOWN_START(src, severe_cooldown, 10 SECONDS) if(prob(emp_vulnerability/severity)) //Chance of permanent effects organ_flags |= ORGAN_EMP //Starts organ faliure - gonna need replacing soon. diff --git a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm index 6255b5dec3d5..8030eece6968 100644 --- a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm +++ b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm @@ -2,7 +2,7 @@ name = "biological battery" icon_state = "stomach-p" //Welp. At least it's more unique in functionaliy. desc = "A crystal-like organ that stores the electric charge of ethereals." - organ_traits = list(TRAIT_NOHUNGER) // We have our own hunger mechanic. + organ_traits = list(TRAIT_NOHUNGER, TRAIT_ETHEREAL_METABOLISM) // We have our own hunger mechanic. /// Where the energy of the stomach is stored. var/obj/item/stock_parts/power_store/cell ///used to keep ethereals from spam draining power sources diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index 8334cbdc40db..263a46ce11d0 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -101,13 +101,15 @@ /datum/language/terrum, /datum/language/nekomimetic, /datum/language/garou_tongue, - /datum/language/primal_tongue + /datum/language/primal_tongue, ) for(var/datum/language/lang as anything in subtypesof(/datum/language)) if(lang.restricted) continue lang_list |= lang - return lang_list // DARKPACK EDIT CHANGE END + . = lang_list.Copy() // DARKPACK EDIT CHANGE END + if(languages_native) + . |= languages_native /obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args) SIGNAL_HANDLER @@ -254,7 +256,7 @@ if(feedback) owner.balloon_alert(owner, "you can't seem to statue-ize!") return FALSE // permanently bricked - if(owner.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(owner)) if(feedback) owner.balloon_alert(owner, "you're too weak!") return FALSE @@ -371,6 +373,7 @@ desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." icon_state = "tongueayylmao" say_mod = "gibbers" + organ_traits = list(TRAIT_HIDE_THINKING_INDICATOR) sense_of_taste = FALSE modifies_speech = TRUE var/mothership @@ -495,7 +498,7 @@ /obj/item/organ/tongue/zombie/on_life(seconds_per_tick) . = ..() - if(owner.stat == CONSCIOUS && SPT_PROB(2, seconds_per_tick)) + if(!IS_UNCONSCIOUS_OR_CRIT(owner) && SPT_PROB(2, seconds_per_tick)) playsound(owner, pick(spooks), 50, TRUE, 10) /obj/item/organ/tongue/alien @@ -628,10 +631,6 @@ voice_filter = @{"[0:a] asplit [out0][out2]; [out0] asetrate=%SAMPLE_RATE%*0.99,aresample=%SAMPLE_RATE%,volume=0.3 [p0]; [p0][out2] amix=inputs=2"} languages_native = list(/datum/language/voltaic) -// Ethereal tongues can speak all default + voltaic -/obj/item/organ/tongue/ethereal/get_possible_languages() - return ..() + /datum/language/voltaic - /obj/item/organ/tongue/cat name = "felinid tongue" desc = "A fleshy muscle mostly used for meowing. Or biting." diff --git a/code/modules/surgery/surgery_tools.dm b/code/modules/surgery/surgery_tools.dm index 05e0188a8724..2627e8ea0d3a 100644 --- a/code/modules/surgery/surgery_tools.dm +++ b/code/modules/surgery/surgery_tools.dm @@ -3,6 +3,7 @@ desc = "Retracts stuff." icon = 'icons/obj/medical/surgery_tools.dmi' icon_state = "retractor" + worn_icon = "retractor" inhand_icon_state = "retractor" icon_angle = 45 lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -33,6 +34,7 @@ desc = "You think you have seen this before." icon = 'icons/obj/medical/surgery_tools.dmi' icon_state = "hemostat" + worn_icon = "hemostat" inhand_icon_state = "hemostat" icon_angle = 135 lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -65,6 +67,7 @@ desc = "This stops bleeding." icon = 'icons/obj/medical/surgery_tools.dmi' icon_state = "cautery" + worn_icon = "cautery" inhand_icon_state = "cautery" icon_angle = 135 lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -207,6 +210,7 @@ desc = "Cut, cut, and once more cut." icon = 'icons/obj/medical/surgery_tools.dmi' icon_state = "scalpel" + worn_icon = "scalpel" inhand_icon_state = "scalpel" icon_angle = 180 lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' @@ -325,6 +329,7 @@ desc = "Medlane brand surgical drapes provide optimal safety and infection control." // DARKPACK EDIT CHANGE icon = 'icons/obj/medical/surgery_tools.dmi' icon_state = "surgical_drapes" + worn_icon = "drapes" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' inhand_icon_state = "drapes" @@ -569,7 +574,7 @@ patient.visible_message(span_danger("[user] begins to secure [src] around [patient]'s [candidate_name]."), span_userdanger("[user] begins to secure [src] around your [candidate_name]!")) playsound(get_turf(patient), 'sound/items/tools/ratchet.ogg', 20, TRUE) - if(patient.stat >= UNCONSCIOUS || HAS_TRAIT(patient, TRAIT_INCAPACITATED)) //if you're incapacitated (due to paralysis, a stun, being in staminacrit, etc.), critted, unconscious, or dead, it's much easier to properly line up a snip + if(IS_UNCONSCIOUS(patient) || HAS_TRAIT(patient, TRAIT_INCAPACITATED)) //if you're incapacitated (due to paralysis, a stun, being in staminacrit, etc.), critted, unconscious, or dead, it's much easier to properly line up a snip amputation_speed_mod *= 0.5 if(patient.stat != DEAD && patient.has_status_effect(/datum/status_effect/jitter)) //jittering will make it harder to secure the shears, even if you can't otherwise move amputation_speed_mod *= 1.5 //15*0.5*1.5=11.25, so staminacritting someone who's jittering (from, say, a stun baton) won't give you enough time to snip their head off, but staminacritting someone who isn't jittering will diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm index a8fe0a46b4f6..7931486e051b 100644 --- a/code/modules/tgui/states.dm +++ b/code/modules/tgui/states.dm @@ -64,7 +64,7 @@ if(!client && !HAS_TRAIT(src, TRAIT_PRESERVE_UI_WITHOUT_CLIENT)) return UI_CLOSE // Disable UIs if unconscious. - else if(stat) + else if(IS_UNCONSCIOUS_OR_CRIT(src)) return UI_DISABLED // Update UIs if incapicitated but conscious. else if(incapacitated) @@ -106,21 +106,19 @@ var/obj/item/machine_remote/remote = item_in_hand if(remote.controlling_machine_or_bot == src_object) return UI_INTERACTIVE - // If the object is obscured, close it. - if(viewcheck && !(src_object in view(src))) - return UI_CLOSE + var/dist = get_dist(src_object, src) - // Open and interact if 1-0 tiles away. - if(dist <= 1) - return UI_INTERACTIVE - // View only if 2-3 tiles away. - else if(dist <= 2) - return UI_UPDATE - // Disable if 5 tiles away. - else if(dist <= 5) + // If the object is obscured or too far away, close it. + if (dist > 5 || (viewcheck && !(src_object in view(5, src)))) + return UI_CLOSE + // Disable if further than 2 tiles + if (dist > 2) return UI_DISABLED - // Otherwise, we got nothing. - return UI_CLOSE + // Update but block interactions if further than a tile away + if (dist > 1) + return UI_UPDATE + // Open and interact if 1-0 tiles away. + return UI_INTERACTIVE /mob/living/carbon/human/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE, allow_tk = TRUE) if(allow_tk && dna.check_mutation(/datum/mutation/telekinesis) && tkMaxRangeCheck(src, src_object)) diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm index 8e35a97da320..5214806c0926 100644 --- a/code/modules/tgui/states/conscious.dm +++ b/code/modules/tgui/states/conscious.dm @@ -12,6 +12,6 @@ GLOBAL_DATUM_INIT(conscious_state, /datum/ui_state/conscious_state, new) /datum/ui_state/conscious_state/can_use_topic(src_object, mob/user) - if(user.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(user)) return UI_INTERACTIVE return UI_CLOSE diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm index ab8cd5f6246f..46aff95f4293 100644 --- a/code/modules/tgui/states/not_incapacitated.dm +++ b/code/modules/tgui/states/not_incapacitated.dm @@ -27,7 +27,7 @@ GLOBAL_DATUM_INIT(not_incapacitated_turf_state, /datum/ui_state/not_incapacitate turf_check = no_turfs /datum/ui_state/not_incapacitated_state/can_use_topic(src_object, mob/user) - if(user.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(user)) return UI_CLOSE if(HAS_TRAIT(src, TRAIT_UI_BLOCKED) || user.incapacitated || (turf_check && !isturf(user.loc))) return UI_DISABLED diff --git a/code/modules/tgui/status_composers.dm b/code/modules/tgui/status_composers.dm index 4222769203ba..b03d5c079ec0 100644 --- a/code/modules/tgui/status_composers.dm +++ b/code/modules/tgui/status_composers.dm @@ -101,7 +101,7 @@ return UI_UPDATE var/mob/living/living_user = user - return (living_user.body_position == LYING_DOWN && living_user.stat == CONSCIOUS) \ + return (living_user.body_position == LYING_DOWN && !IS_UNCONSCIOUS_OR_CRIT(living_user)) \ ? UI_INTERACTIVE \ : UI_UPDATE diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm index f82457a8ba81..84f0c14fb592 100644 --- a/code/modules/tgui/tgui_window.dm +++ b/code/modules/tgui/tgui_window.dm @@ -147,10 +147,11 @@ * return bool */ /datum/tgui_window/proc/can_be_suspended() + var/unlimited_windows = client?.prefs?.read_preference(/datum/preference/toggle/tgui_unlimited_windows) return !fatally_errored \ && pooled \ && pool_index > 0 \ - && pool_index <= TGUI_WINDOW_SOFT_LIMIT \ + && (unlimited_windows || pool_index <= TGUI_WINDOW_SOFT_LIMIT) \ && status == TGUI_WINDOW_READY /** diff --git a/code/modules/tgui_input/say_modal/speech.dm b/code/modules/tgui_input/say_modal/speech.dm index e3ec1763ac1d..49cbccd3e5d8 100644 --- a/code/modules/tgui_input/say_modal/speech.dm +++ b/code/modules/tgui_input/say_modal/speech.dm @@ -102,7 +102,7 @@ * * major - If [TRUE], a "major action" triggered the force say, which may have additional side effects */ /mob/living/carbon/human/proc/force_say(list/alter_phrases = null, immediate = FALSE, major = TRUE) - if(stat != CONSCIOUS || !client?.tgui_say?.window_open) + if(IS_UNCONSCIOUS_OR_CRIT(src) || !client?.tgui_say?.window_open) return FALSE client.tgui_say.force_say(alter_phrases, immediate) if(client.typing_indicators) diff --git a/code/modules/tgui_input/say_modal/typing.dm b/code/modules/tgui_input/say_modal/typing.dm index 7fc6a93f2942..681b914581ed 100644 --- a/code/modules/tgui_input/say_modal/typing.dm +++ b/code/modules/tgui_input/say_modal/typing.dm @@ -66,7 +66,7 @@ /// Overrides for overlay creation /mob/living/create_thinking_indicator() - if(active_thinking_indicator || active_typing_indicator || stat != CONSCIOUS || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) + if(active_thinking_indicator || active_typing_indicator || IS_UNCONSCIOUS_OR_CRIT(src) || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) return FALSE active_thinking_indicator = mutable_appearance('icons/mob/effects/talk.dmi', "[bubble_icon]3", TYPING_LAYER) add_overlay(active_thinking_indicator) @@ -79,7 +79,7 @@ active_thinking_indicator = null /mob/living/create_typing_indicator() - if(active_typing_indicator || active_thinking_indicator || stat != CONSCIOUS || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) + if(active_typing_indicator || active_thinking_indicator || IS_UNCONSCIOUS_OR_CRIT(src) || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) return FALSE active_typing_indicator = mutable_appearance('icons/mob/effects/talk.dmi', "[bubble_icon]0", TYPING_LAYER) add_overlay(active_typing_indicator) @@ -95,4 +95,3 @@ REMOVE_TRAIT(src, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) remove_thinking_indicator() remove_typing_indicator() - diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm index 97add0f943d6..d3dcd3b8b95c 100644 --- a/code/modules/transport/tram/tram_controller.dm +++ b/code/modules/transport/tram/tram_controller.dm @@ -504,7 +504,7 @@ playsound(source = nav_beacon, soundin = 'sound/vehicles/car_crash.ogg', vol = 100, vary = FALSE, falloff_distance = DEFAULT_TRAM_LENGTH) nav_beacon.audible_message(span_userdanger("You hear metal grinding as the tram comes to a sudden, complete stop!")) for(var/mob/living/tram_passenger in range(DEFAULT_TRAM_LENGTH - 2, nav_beacon)) - if(tram_passenger.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(tram_passenger)) continue shake_camera(M = tram_passenger, duration = 0.2 SECONDS, strength = 3) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index d714702deeaf..2f7ffdb585f7 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -127,6 +127,7 @@ #include "blood_volume_procs.dm" #include "bloody_footprints.dm" #include "borg_tools.dm" +#include "bot_access.dm" #include "boulder_processing.dm" #include "breath.dm" #include "buckle.dm" @@ -179,7 +180,6 @@ #include "embedding.dm" #include "emoting.dm" #include "emp_flashlight.dm" -#include "ensure_subtree_operational_datum.dm" #include "ethereal_revival.dm" #include "explosion_action.dm" #include "firedoor_regions.dm" @@ -245,6 +245,7 @@ #include "market.dm" #include "mecha_build.dm" #include "mecha_damage.dm" +#include "mecha_equipment.dm" #include "medical_wounds.dm" #include "merge_type.dm" #include "metabolizing.dm" @@ -275,6 +276,7 @@ #include "oxyloss_suffocation.dm" #include "paintings.dm" #include "pills.dm" +#include "placing_on_space.dm" #include "plane_double_transform.dm" #include "plane_dupe_detector.dm" #include "plane_sanity.dm" @@ -305,6 +307,7 @@ #include "screenshot_debrain.dm" #include "screenshot_digi.dm" #include "screenshot_dynamic_human_icons.dm" +#include "screenshot_hair_gradient.dm" #include "screenshot_high_luminosity_eyes.dm" #include "screenshot_humanoids.dm" #include "screenshot_husk.dm" @@ -314,6 +317,7 @@ #include "serving_tray.dm" #include "simple_animal_freeze.dm" #include "siunit.dm" +#include "slapcrafting.dm" #include "slime_mood.dm" #include "slips.dm" #include "spawn_humans.dm" @@ -352,6 +356,7 @@ #include "teleporters.dm" #include "text.dm" #include "tgui_create_message.dm" +#include "tile_replacement.dm" #include "timer_sanity.dm" #include "trait_addition_and_removal.dm" #include "traitor.dm" diff --git a/code/modules/unit_tests/atmospherics_sanity.dm b/code/modules/unit_tests/atmospherics_sanity.dm index f3efb4d0a137..3ffc2cd21806 100644 --- a/code/modules/unit_tests/atmospherics_sanity.dm +++ b/code/modules/unit_tests/atmospherics_sanity.dm @@ -27,6 +27,11 @@ crawled_areas = list() remaining_areas = list() + // early-init events can destroy pipes, queuing the surviving neighbors for a pipenet rebuild. the rebuild normally happens + // on the next SSair.fire(), but this test can run before that fire cycle occurs, racing against it. + while(length(SSair.rebuild_queue) || length(SSair.expansion_queue)) + SSair.process_rebuilds() + for(var/obj/effect/landmark/atmospheric_sanity/start_area/start_marker in GLOB.landmarks_list) var/area/starting_area = get_area(start_marker) if(starting_area in starting_areas) diff --git a/code/modules/unit_tests/blindness.dm b/code/modules/unit_tests/blindness.dm index 20a8349302bf..bd367efded37 100644 --- a/code/modules/unit_tests/blindness.dm +++ b/code/modules/unit_tests/blindness.dm @@ -12,6 +12,7 @@ var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) var/obj/item/clothing/glasses/blindfold/blindfold = new(dummy.loc) TEST_ASSERT(!dummy.is_blind(), "Dummy was blind on initialize, and shouldn't be.") + dummy.mock_client = new() // Become blind dummy.become_blind("unit_test") @@ -70,6 +71,7 @@ /datum/unit_test/nearsighted_quirk/Run() var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) var/obj/item/clothing/glasses/regular/glasses = allocate(/obj/item/clothing/glasses/regular) + dummy.mock_client = new() // Become quirk nearsighted // Have to do a transfer here so we don't get glasses @@ -106,6 +108,7 @@ /datum/unit_test/eye_damage/Run() var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) var/obj/item/organ/eyes/eyes = dummy.get_organ_slot(ORGAN_SLOT_EYES) + dummy.mock_client = new() TEST_ASSERT_NOTNULL(eyes, "Eye damage unit test spawned a dummy without eyes!") // Test blindness due to eye damage @@ -154,6 +157,7 @@ /datum/unit_test/nearsighted_effect/Run() var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) var/datum/status_effect/grouped/nearsighted/myopia + dummy.mock_client = new() /* APPLICATION */ // Let's test regular nearsightedness first diff --git a/code/modules/unit_tests/bot_access.dm b/code/modules/unit_tests/bot_access.dm new file mode 100644 index 000000000000..b532f58d106c --- /dev/null +++ b/code/modules/unit_tests/bot_access.dm @@ -0,0 +1,18 @@ +/// Tests whether bots have been made able to bypass access, as well as whether they've been made throwable. None should be at the moment, and if they should be, make an exception +/datum/unit_test/bot_access + +/datum/unit_test/bot_access/Run() + var/list/access_bypassing_bots = list() + var/obj/machinery/door/airlock/instant/impassable_door = allocate(/obj/machinery/door/airlock/instant, get_step(run_loc_floor_bottom_left, EAST)) + + impassable_door.req_access = list("access nothing has") + + var/mob/living/basic/bot/bot_to_check + for(var/bot_type in valid_subtypesof(/mob/living/basic/bot)) + bot_to_check = allocate(bot_type, run_loc_floor_bottom_left) + bot_to_check.Bump(impassable_door) + if(!impassable_door.density) + access_bypassing_bots += bot_to_check.type + impassable_door.close(TRUE) + + TEST_ASSERT(!access_bypassing_bots.len, "The following bots bypass access requirements: [english_list(access_bypassing_bots)]") diff --git a/code/modules/unit_tests/closets.dm b/code/modules/unit_tests/closets.dm index 0427f355ff4e..4cba5e60a7fe 100644 --- a/code/modules/unit_tests/closets.dm +++ b/code/modules/unit_tests/closets.dm @@ -1,8 +1,8 @@ /// Checks that the length of the initial contents of a closet doesn't exceed its storage capacity. /// Also checks that nothing inside that isn't immediate is a steal objective. -/datum/unit_test/closets +/datum/unit_test/closet_contents -/datum/unit_test/closets/Run() +/datum/unit_test/closet_contents/Run() var/list/all_closets = subtypesof(/obj/structure/closet) //Supply pods. They are sent, crashed, opened and never closed again. They also cause exceptions in nullspace. all_closets -= typesof(/obj/structure/closet/supplypod) @@ -25,3 +25,49 @@ for (var/obj/item/item in closet.contents - immediate_contents) if (item.type in GLOB.steal_item_handler.objectives_by_path) TEST_FAIL("[closet_type] contains a steal objective [item.type] in PopulateContents(). Move it to populate_contents_immediate().") + +/// Tests whether two closet subtypes(body_bag & crate) successfully perform their functions, and their parent's. +/datum/unit_test/closet_opening + +/datum/unit_test/closet_opening/Run() + var/mob/living/carbon/human/consistent/placer = EASY_ALLOCATE() + var/obj/structure/closet/crate/secure/locked_crate = allocate(/obj/structure/closet/crate/secure, get_step(run_loc_floor_bottom_left, NORTH)) + var/obj/structure/closet/body_bag/corpse_bag = allocate(/obj/structure/closet/body_bag, get_step(run_loc_floor_bottom_left, EAST)) + var/obj/item/food/cookie/placing_tester = EASY_ALLOCATE() + var/obj/item/card/id/advanced/gold/captains_spare/cool_card = EASY_ALLOCATE() + + placer.equip_to_slot(cool_card, ITEM_SLOT_ID) + placer.put_in_active_hand(placing_tester) + locked_crate.req_access = list(ACCESS_CAPTAIN) + + click_wrapper(placer, locked_crate) + TEST_ASSERT_NOTNULL(placer.get_active_held_item(), "Successfully placed item in closed, locked crate") + + placer.swap_hand() + click_wrapper(placer, locked_crate) + TEST_ASSERT(!locked_crate.locked, "Was unable to unlock locked crate despite adequate access") + + click_wrapper(placer, locked_crate) + TEST_ASSERT(locked_crate.opened, "Was unable to open unlocked crate") + + placer.swap_hand() + click_wrapper(placer, locked_crate) + TEST_ASSERT(placing_tester in locked_crate.loc, "Was unable to place item in open crate") + + click_wrapper(placer, locked_crate) + TEST_ASSERT(placing_tester in locked_crate, "Closing a crate with an item on it did not move it within the crate") + + click_wrapper(placer, locked_crate, list(RIGHT_CLICK = TRUE, BUTTON = RIGHT_CLICK)) + TEST_ASSERT(locked_crate.locked, "Right-clicking a crate with adequate access did not lock it") + + // Begin bodybag + + click_wrapper(placer, corpse_bag) + TEST_ASSERT(corpse_bag.opened, "Could not open a placed bodybag") + + placer.put_in_active_hand(placing_tester, TRUE) + click_wrapper(placer, corpse_bag) + TEST_ASSERT(placing_tester in corpse_bag.loc, "Could not place item on opened bodybag") + + click_wrapper(placer, corpse_bag) + TEST_ASSERT(placing_tester in corpse_bag, "Closing a bodybag on an item did not move it within the bodybag") diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm index f6c444123824..fd3df9786e5a 100644 --- a/code/modules/unit_tests/create_and_destroy.dm +++ b/code/modules/unit_tests/create_and_destroy.dm @@ -58,12 +58,9 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) original_baseturf_count = length(original_baseturfs) else var/atom/creation = new type_path(spawn_at) - if(QDELETED(creation)) - // Same as below - creation = null - continue - //Go all in - qdel(creation, force = TRUE) + if(!QDELETED(creation)) + //Go all in + qdel(creation, force = TRUE) //This will hold a ref to the last thing we process unless we set it to null //Yes byond is fucking sinful creation = null @@ -72,7 +69,7 @@ GLOBAL_VAR_INIT(running_create_and_destroy, FALSE) var/list/to_del = spawn_at.contents - cached_contents if(length(to_del)) for(var/atom/to_kill in to_del) - qdel(to_kill) + qdel(to_kill, force = TRUE) GLOB.running_create_and_destroy = FALSE diff --git a/code/modules/unit_tests/dna_infusion.dm b/code/modules/unit_tests/dna_infusion.dm index a8d8b2ea4ec7..96805ddbe728 100644 --- a/code/modules/unit_tests/dna_infusion.dm +++ b/code/modules/unit_tests/dna_infusion.dm @@ -7,7 +7,7 @@ if(ispath(input_type, /mob/living)) continue if(input_type == input_type::abstract_type) - input_type = pick(subtypesof(input_type)) + input_type = pick(valid_subtypesof(input_type)) var/atom/movable/movable = allocate(input_type) if(!HAS_TRAIT(movable, TRAIT_VALID_DNA_INFUSION)) diff --git a/code/modules/unit_tests/door_access.dm b/code/modules/unit_tests/door_access.dm index 78e663bb310b..9fc384256581 100644 --- a/code/modules/unit_tests/door_access.dm +++ b/code/modules/unit_tests/door_access.dm @@ -67,7 +67,7 @@ /datum/unit_test/door_access_ai/Run() var/mob/dead/observer/fake_ghost = allocate(__IMPLIED_TYPE__) // ai must be passed a mob in /new, cringe - var/mob/living/silicon/ai/subject = allocate(__IMPLIED_TYPE__, run_loc_floor_top_right, null, fake_ghost) + var/mob/living/silicon/ai/subject = allocate(__IMPLIED_TYPE__, run_loc_floor_top_right, fake_ghost) var/obj/machinery/door/airlock/instant/door = allocate(__IMPLIED_TYPE__) door.interaction_flags_machine |= INTERACT_MACHINE_OFFLINE @@ -79,7 +79,7 @@ /datum/unit_test/windoor_access_ai/Run() var/mob/dead/observer/fake_ghost = allocate(__IMPLIED_TYPE__) // ai must be passed a mob in /new, cringe - var/mob/living/silicon/ai/subject = allocate(__IMPLIED_TYPE__, run_loc_floor_top_right, null, fake_ghost) + var/mob/living/silicon/ai/subject = allocate(__IMPLIED_TYPE__, run_loc_floor_top_right, fake_ghost) var/obj/machinery/door/window/instant/door = allocate(__IMPLIED_TYPE__) door.interaction_flags_machine |= INTERACT_MACHINE_OFFLINE diff --git a/code/modules/unit_tests/emoting.dm b/code/modules/unit_tests/emoting.dm index 5300f667b3e4..23c483cc0490 100644 --- a/code/modules/unit_tests/emoting.dm +++ b/code/modules/unit_tests/emoting.dm @@ -14,7 +14,7 @@ human.set_oxy_loss(140) - TEST_ASSERT(human.stat != CONSCIOUS, "Human is somehow conscious after receiving suffocation damage") + TEST_ASSERT(IS_UNCONSCIOUS_OR_CRIT(human), "Human is somehow conscious after receiving suffocation damage") human.say("*shrug") TEST_ASSERT_EQUAL(emotes_used, 1, "Human shrugged while unconscious") diff --git a/code/modules/unit_tests/ensure_subtree_operational_datum.dm b/code/modules/unit_tests/ensure_subtree_operational_datum.dm deleted file mode 100644 index 9ca78fbc6746..000000000000 --- a/code/modules/unit_tests/ensure_subtree_operational_datum.dm +++ /dev/null @@ -1,64 +0,0 @@ -/// The subtree that requires the operational datum. -#define REQUIRED_SUBTREE "required_subtree" -/// The list of typepaths of applicable operational datums that would satisfy the requirement. -#define REQUIRED_OPERATIONAL_DATUMS "required_operational_datums" - -/// Unit Test that ensure that if we add a specific planning subtree to a basic mob's planning tree, that we also have the operational datum needed for it (component/element). -/// This can be extended to other "mandatory" operational datums for certain subtrees to work. -/datum/unit_test/ensure_subtree_operational_datum - /// Associated list of mobs that we need to test this on. Key is the typepath of the mob, value is a list of the planning subtree and the operational datums that are required for it. - var/list/testable_mobs = list() - -/datum/unit_test/ensure_subtree_operational_datum/Run() - gather_testable_mobs() - test_applicable_mobs() - -/// First, look for all mobs that have a planning subtree that requires an element, then add it to the list for stuff to test afterwards. Done like this to not have one mumbo proc that's hard to read. -/datum/unit_test/ensure_subtree_operational_datum/proc/gather_testable_mobs() - for(var/mob/living/basic/checkable_mob as anything in subtypesof(/mob/living/basic)) - var/datum/ai_controller/testable_controller = initial(checkable_mob.ai_controller) - if(isnull(testable_controller)) - continue - - // we can't do inital() memes on lists so it's allocation time - testable_controller = allocate(testable_controller) - var/list/ai_planning_subtrees = testable_controller.planning_subtrees // list of instantiated datums. easy money - if(!length(ai_planning_subtrees)) - continue - - for(var/datum/ai_planning_subtree/testable_subtree as anything in ai_planning_subtrees) - var/list/necessary_datums = testable_subtree.operational_datums - if(isnull(necessary_datums)) - continue - - testable_mobs[checkable_mob] = list( - REQUIRED_OPERATIONAL_DATUMS = necessary_datums, - REQUIRED_SUBTREE = testable_subtree.type, - ) - -/// Then, test the mobs that we've found -/datum/unit_test/ensure_subtree_operational_datum/proc/test_applicable_mobs() - for(var/mob/living/basic/checkable_mob as anything in testable_mobs) - var/list/checkable_mob_data = testable_mobs[checkable_mob] - checkable_mob = allocate(checkable_mob) - - var/datum/ai_planning_subtree/test_subtree = checkable_mob_data[REQUIRED_SUBTREE] - var/list/trait_sources = GET_TRAIT_SOURCES(checkable_mob, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM) - if(!length(trait_sources)) // yes yes we could use `COUNT_TRAIT_SOURCES` but why invoke the same macro twice - TEST_FAIL("The mob [checkable_mob] ([checkable_mob.type]) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, but has a planning subtree ([test_subtree]) that requires it!") - continue - - var/has_element = FALSE - var/list/testable_operational_datums = checkable_mob_data[REQUIRED_OPERATIONAL_DATUMS] - for(var/iterable in trait_sources) - if(iterable in testable_operational_datums) - has_element = TRUE - break - - if(!has_element) - var/list/message_list = list("The mob [checkable_mob] ([checkable_mob.type]) has a planning subtree ([test_subtree]) that requires a component/element, but does not have any!") - message_list += "Needs one of the following to satisfy the requirement: ([testable_operational_datums.Join(", ")])" - TEST_FAIL(message_list.Join(" ")) - -#undef REQUIRED_SUBTREE -#undef REQUIRED_OPERATIONAL_DATUMS diff --git a/code/modules/unit_tests/food_edibility_check.dm b/code/modules/unit_tests/food_edibility_check.dm index f8360e2c6e9a..9cbb3f65b803 100644 --- a/code/modules/unit_tests/food_edibility_check.dm +++ b/code/modules/unit_tests/food_edibility_check.dm @@ -3,13 +3,10 @@ /datum/unit_test/food_edibility_check/Run() var/list/not_food = list( - /obj/item/food/grown, - /obj/item/food/grown/mushroom, /obj/item/food/clothing, - /obj/item/food/meat/slab/human/mutant, ) - var/list/food_paths = subtypesof(/obj/item/food) - not_food + var/list/food_paths = valid_subtypesof(/obj/item/food) - not_food for(var/food_path in food_paths) var/obj/item/food/spawned_food = allocate(food_path) diff --git a/code/modules/unit_tests/heretic_rituals.dm b/code/modules/unit_tests/heretic_rituals.dm index 63c4c76ba8b5..ebc4798d4e5f 100644 --- a/code/modules/unit_tests/heretic_rituals.dm +++ b/code/modules/unit_tests/heretic_rituals.dm @@ -69,8 +69,7 @@ ritual_item_path = pick(ritual_item_path) for(var/i in 1 to amount_to_create) var/obj/item/item = new ritual_item_path(get_turf(our_heretic)) - if(isitem(item)) - item.item_flags &= ~ABSTRACT + knowledge.prepare_atom_for_ritual_test(item) created_atoms += item // Now, we can ACTUALLY run the ritual. Let's do it. diff --git a/code/modules/unit_tests/human_through_recycler.dm b/code/modules/unit_tests/human_through_recycler.dm index 1f62a022c05c..d1dd21aecbc0 100644 --- a/code/modules/unit_tests/human_through_recycler.dm +++ b/code/modules/unit_tests/human_through_recycler.dm @@ -10,7 +10,7 @@ // okay, let's first test the basics of how an emagged recycler should operate TEST_ASSERT_NULL(QDELETED(assistant), "Assistant was deleted by the emagged recycler!") // The assistant should not be deleted by the recycler. - if(assistant.stat < UNCONSCIOUS) + if(!IS_UNCONSCIOUS(assistant)) TEST_FAIL("Assistant was not made unconscious by the emagged recycler!") // crush_living() on the recycler should have made the assistant unconscious or worse. // crush_living() on the recycler should have applied the crush_damage to the assistant. var/damage_incurred = assistant.get_brute_loss() @@ -44,5 +44,3 @@ TEST_ASSERT_NULL(assistant.r_store, "Assistant still has an item in their right pocket after being put through an emagged recycler!") TEST_ASSERT_NULL(assistant.s_store, "Assistant still has an item in their suit storage slot after being put through an emagged recycler!") TEST_ASSERT_NULL(assistant.glasses, "Assistant still has glasses on after being put through an emagged recycler!") - - diff --git a/code/modules/unit_tests/interaction_door.dm b/code/modules/unit_tests/interaction_door.dm index 418f02213fec..3d67b9b8ba1d 100644 --- a/code/modules/unit_tests/interaction_door.dm +++ b/code/modules/unit_tests/interaction_door.dm @@ -11,3 +11,19 @@ TEST_ASSERT(!door.density, "Airlock was not open after being opened.") click_wrapper(tider, get_turf(door)) TEST_ASSERT(door.operating, "Airlock was not closing after clicking the turf below, as per /datum/component/redirect_attack_hand_from_turf.") + +/// Tests that janitor keys can open airlocks +/datum/unit_test/keyring_on_door + +/datum/unit_test/keyring_on_door/Run() + var/mob/living/carbon/human/consistent/janitor = EASY_ALLOCATE() + var/obj/machinery/door/airlock/instant/real_door = allocate(/obj/machinery/door/airlock/instant, get_step(run_loc_floor_bottom_left, EAST)) + var/obj/item/access_key/keyring = EASY_ALLOCATE() + + real_door.req_access = list(ACCESS_HOP) + keyring.department_access = REGION_COMMAND + keyring.key_speed = 0 + janitor.put_in_active_hand(keyring) + + click_wrapper(janitor, real_door) + TEST_ASSERT(!real_door.density, "Airlock could not be opened by janitor keyring") diff --git a/code/modules/unit_tests/ling_decap.dm b/code/modules/unit_tests/ling_decap.dm index 3dc492e556a6..27e955729203 100644 --- a/code/modules/unit_tests/ling_decap.dm +++ b/code/modules/unit_tests/ling_decap.dm @@ -18,7 +18,7 @@ TEST_ASSERT_NULL(locate(/obj/item/organ/brain) in noggin, "Changeling's head failed to drop its brain.") TEST_ASSERT_NULL(oldbrain.brainmob.mind, "Changeling's mind was moved to their brain after decapitation and organ dropping, but it should have remained in their body.") - TEST_ASSERT_EQUAL(ling.stat, CONSCIOUS, "Changeling was not conscious after losing their head.") + TEST_ASSERT_EQUAL(ling.stat, STABLE, "Changeling was not conscious after losing their head.") // Cleanup qdel(noggin) diff --git a/code/modules/unit_tests/mecha_equipment.dm b/code/modules/unit_tests/mecha_equipment.dm new file mode 100644 index 000000000000..d2e8fe623fad --- /dev/null +++ b/code/modules/unit_tests/mecha_equipment.dm @@ -0,0 +1,39 @@ +///Tests whether the mech generator is loadable while empty, loadable while loaded, installable, runs successfully, shuts off successfully, and successfully charges the mech +/datum/unit_test/mech_generator + +/datum/unit_test/mech_generator/Run() + var/mob/living/carbon/human/consistent/robotocist = EASY_ALLOCATE() + var/obj/vehicle/sealed/mecha/ripley/our_mech = EASY_ALLOCATE() + var/obj/item/mecha_parts/mecha_equipment/generator/prefilled_generator = EASY_ALLOCATE() + var/obj/item/mecha_parts/mecha_equipment/generator/printed/active_generator = EASY_ALLOCATE() + var/obj/item/stack/sheet/mineral/plasma/fuel = EASY_ALLOCATE(1) + + prefilled_generator.max_fuel = 10 * SHEET_MATERIAL_AMOUNT + active_generator.fuelrate_active = 2 * SHEET_MATERIAL_AMOUNT + our_mech.cell.charge /= 2 // We're not being precise here, it doesn't matter + var/starting_charge = our_mech.cell.charge + + robotocist.put_in_active_hand(fuel) + click_wrapper(robotocist, prefilled_generator) + TEST_ASSERT(!QDELETED(fuel), "Mech generator was filled while already at its maximum capacity") + + click_wrapper(robotocist, active_generator) + TEST_ASSERT(QDELETED(fuel), "Mech generator was not able to be filled while empty") + + fuel = EASY_ALLOCATE(1) + robotocist.put_in_active_hand(fuel) + click_wrapper(robotocist, active_generator) + TEST_ASSERT(QDELETED(fuel), "Mech generator was not able to be filled while partially full") + + robotocist.put_in_active_hand(active_generator) + click_wrapper(robotocist, our_mech) + TEST_ASSERT_EQUAL(active_generator.loc, our_mech, "Failed to place mech generator within mech") + + active_generator.active = TRUE + active_generator.process(1) + TEST_ASSERT(active_generator.active, "Mech generator shut down prematurely") + TEST_ASSERT(our_mech.cell.charge > starting_charge, "Mech generator did not produce any power for its mech") + TEST_ASSERT(!active_generator.fuel.amount, "Mech generator did not consume its fuel when activated") + + active_generator.process(1) + TEST_ASSERT(!active_generator.active, "Mech generator did not shut down despite being out of fuel") diff --git a/code/modules/unit_tests/mouse_bite_cable.dm b/code/modules/unit_tests/mouse_bite_cable.dm index dc7fd04d24cb..53a28bf10792 100644 --- a/code/modules/unit_tests/mouse_bite_cable.dm +++ b/code/modules/unit_tests/mouse_bite_cable.dm @@ -20,21 +20,19 @@ // Ai controlling processes expect a seconds_per_tick, supply a real-fake dt var/fake_dt = SSai_controllers.wait * 0.1 - // Set AI - AIs by default are off in z-levels with no client, we have to force it on. + // Force this fker to be on + biter.ai_controller.ai_traits |= RUN_WHILE_UNWATCHED biter.ai_controller.set_ai_status(AI_STATUS_ON) - biter.ai_controller.can_idle = FALSE - // Select behavior - this will queue finding the cable - biter.ai_controller.SelectBehaviors(fake_dt) - // Process behavior - this will execute the "locate the cable" behavior - biter.ai_controller.process(fake_dt) - // Check that the cable was found - TEST_ASSERT(biter.ai_controller.blackboard[BB_LOW_PRIORITY_HUNTING_TARGET] == wire, "Mouse, after executing find, did not set the cable as a target.") - // Select behavior - this will queue hunting - biter.ai_controller.SelectBehaviors(fake_dt) - // Process behavior - this will execute the hunt for the cable and cause a bite (as we're in the min range) - biter.ai_controller.process(fake_dt) - // Check that the cable was removed, as it was hunted correctly - TEST_ASSERT_NULL(biter.ai_controller.blackboard[BB_LOW_PRIORITY_HUNTING_TARGET], "Mouse, after executing hunt, did not clear their target blackboard.") + + // Mouse eating is chance-based, so we set the hunting target directly, yes, this messed with the test from what it was before, but I'm not sure how to do it better :( + biter.ai_controller.set_blackboard_key(BB_LOW_PRIORITY_HUNTING_TARGET, wire) + + // Tick the tree until the hunt branch moves onto and bites the cable. Can we do this better in unit tests?? idk.. + for(var/i in 1 to 5) + if(QDELETED(biter)) + break + biter.ai_controller.SelectBehaviors(fake_dt) + biter.ai_controller.process(fake_dt) // Now check that the bite went through - remember we qdel mice on death TEST_ASSERT(QDELETED(biter), "Mouse, did not die after biting a powered cable.") @@ -47,12 +45,3 @@ /// Dummy mouse that is guaranteed to die when biting shocked cables. /mob/living/basic/mouse/cable_lover cable_zap_prob = 100 - ai_controller = /datum/ai_controller/basic_controller/mouse/guaranteed_to_bite - -/// Dummy mouse's ai controller that is guaranteed to find and bite a cable beneath it -/datum/ai_controller/basic_controller/mouse/guaranteed_to_bite - planning_subtrees = list(/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables/guaranteed) - -/// Cable hunting subtree that's guarantee to hunt its target. -/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables/guaranteed - hunt_chance = 100 diff --git a/code/modules/unit_tests/placing_on_space.dm b/code/modules/unit_tests/placing_on_space.dm new file mode 100644 index 000000000000..02647a1f10de --- /dev/null +++ b/code/modules/unit_tests/placing_on_space.dm @@ -0,0 +1,46 @@ +/// Tests that lattices, catwalks, cabling, tiling, and RCD tiling can be successfully placed on space tiles +/datum/unit_test/placing_on_space + var/old_turf_type + var/turf/target_turf + +/datum/unit_test/placing_on_space/Run() + var/mob/living/carbon/human/consistent/engineer = EASY_ALLOCATE() + var/obj/item/stack/rods/two/lattice_material = EASY_ALLOCATE() + var/obj/item/stack/cable_coil/single_cable = EASY_ALLOCATE(1) + var/obj/item/stack/tile/iron/single_tile = EASY_ALLOCATE(1) + var/obj/item/construction/rcd/loaded/plating_placer = EASY_ALLOCATE() + + target_turf = get_step(run_loc_floor_bottom_left, EAST) + old_turf_type = target_turf.type + target_turf.ChangeTurf(/turf/open/space) + + engineer.put_in_active_hand(lattice_material) + click_wrapper(engineer, target_turf) + var/obj/structure/lattice/constructed_lattice = locate() in target_turf + TEST_ASSERT_NOTNULL(constructed_lattice, "Failed to construct a lattice on a space tile using iron rods") + + click_wrapper(engineer, constructed_lattice) + var/obj/structure/lattice/catwalk/constructed_catwalk = locate() in target_turf + TEST_ASSERT_NOTNULL(constructed_catwalk, "Failed to construct a catwalk on a lattice on a space tile using iron rods") + TEST_ASSERT(QDELETED(constructed_lattice), "Constructing a catwalk on a lattice did not delete the lattice") + TEST_ASSERT(QDELETED(lattice_material), "Using two iron rods to construct a catwalk did not consume them both") + + engineer.put_in_active_hand(single_cable) + click_wrapper(engineer, constructed_catwalk) + TEST_ASSERT(locate(/obj/structure/cable) in target_turf, "Failed to place a cable on a catwalk in space") + TEST_ASSERT(QDELETED(single_cable), "Using a piece of cable to place a cable did not consume it") + + engineer.put_in_active_hand(single_tile) + click_wrapper(engineer, constructed_catwalk) + TEST_ASSERT(istype(target_turf, /turf/open/floor/plating), "Failed to place plating on a catwalk in space via an iron tile") + TEST_ASSERT(QDELETED(single_tile), "Placing plating on a catwalk in space did not delete the used floor tile") + TEST_ASSERT(QDELETED(constructed_catwalk), "Placing plating on a catwalk in space did not delete the involved catwalk") + + engineer.put_in_active_hand(plating_placer) + target_turf.ChangeTurf(/turf/open/space) + click_wrapper(engineer, target_turf) + TEST_ASSERT(istype(target_turf, /turf/open/floor/plating), "Failed to use an RCD to place plating on space") + +/datum/unit_test/placing_on_space/Destroy() + target_turf.ChangeTurf(old_turf_type) + return ..() diff --git a/code/modules/unit_tests/required_map_items.dm b/code/modules/unit_tests/required_map_items.dm index 41fe1d448709..b252e726bca7 100644 --- a/code/modules/unit_tests/required_map_items.dm +++ b/code/modules/unit_tests/required_map_items.dm @@ -4,11 +4,17 @@ * How to add an item to this test: * - Add the typepath(s) to setup_expected_types * - In the type's initialize, REGISTER_REQUIRED_MAP_ITEM() a minimum and maximum + * + * How to ban an item from being mapped in: + * - Add the typepath(s) to setup_banned_types + * - In the type's initialize, REGISTER_REQUIRED_MAP_ITEM(0, 0) */ /datum/unit_test/maptest_required_map_items test_flags = UNIT_TEST_MAP_TEST /// A list of all typepaths that we expect to be in the required items list var/list/expected_types = list() + /// A list of all typepaths that should never be mapped in + var/list/banned_types = list() /// Used to fill the expected types list with all the types we look for on the map. /// This list will just be full of typepaths that we expect. @@ -26,10 +32,31 @@ expected_types += /obj/machinery/computer/communications expected_types += /obj/machinery/drone_dispenser expected_types += /obj/item/piggy_bank/vault + + // each map should probably have all of them + expected_types += /obj/item/storage/photo_album/bar + expected_types += /obj/item/storage/photo_album/chapel + expected_types += /obj/item/storage/photo_album/library + expected_types += /obj/item/storage/photo_album/prison + + expected_types += /obj/item/storage/photo_album/captain + expected_types += /obj/item/storage/photo_album/ce + expected_types += /obj/item/storage/photo_album/cmo + expected_types += /obj/item/storage/photo_album/hop + expected_types += /obj/item/storage/photo_album/hos + expected_types += /obj/item/storage/photo_album/qm + expected_types += /obj/item/storage/photo_album/rd */ // DARKPACK EDIT REMOVAL END +/// Types that must never be mapped in. +/// Anything listed here is expected to be spawned by code instead. +/datum/unit_test/maptest_required_map_items/proc/setup_banned_types() +// Your typepaths here! + return + /datum/unit_test/maptest_required_map_items/Run() setup_expected_types() + setup_banned_types() var/list/required_map_items = GLOB.required_map_items.Copy() for(var/got_type in expected_types) @@ -48,6 +75,12 @@ TEST_FAIL("Item [got_type] should have at most [item.maximum_amount] mapped in but had [items_found] on mapload!") continue + for(var/banned_type in banned_types) + var/datum/required_item/item = required_map_items[banned_type] + required_map_items -= banned_type + if(item) + TEST_FAIL("Item [banned_type] should never be mapped in, but [item.total_amount] were found on mapload!") + // This primarily serves as a reminder to include the typepath in the expected types list above. // However we can easily delete this line in the future if it runs into false positives. TEST_ASSERT(length(required_map_items) == 0, "The following paths were found in required map items, but weren't checked: [english_list(required_map_items)]") diff --git a/code/modules/unit_tests/screenshot_hair_gradient.dm b/code/modules/unit_tests/screenshot_hair_gradient.dm new file mode 100644 index 000000000000..5d1ae6d64334 --- /dev/null +++ b/code/modules/unit_tests/screenshot_hair_gradient.dm @@ -0,0 +1,18 @@ +/datum/unit_test/screenshot_hair_gradient + +/datum/unit_test/screenshot_hair_gradient/Run() + // Check if hair gradients render properly + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/dummy/consistent) + human.set_hairstyle(/datum/sprite_accessory/hair/bedheadv4::name) + human.set_haircolor(COLOR_BLACK) + human.set_hair_gradient_color(COLOR_PINK) + human.set_hair_gradient_style(/datum/sprite_accessory/gradient/reflected_inverse::name) + test_screenshot("hair_gradient", getFlatIcon(human, no_anim = TRUE, defdir = SOUTH)) + + // Now check it on an ethereal for hair alpha + var/mob/living/carbon/human/ethereal = allocate(/mob/living/carbon/human/dummy/consistent) + ethereal.set_species(/datum/species/ethereal) + ethereal.set_hairstyle(/datum/sprite_accessory/hair/bedheadv4::name) + ethereal.set_hair_gradient_color(COLOR_PINK) + ethereal.set_hair_gradient_style(/datum/sprite_accessory/gradient/reflected_inverse::name) + test_screenshot("hair_gradient_ethereal", getFlatIcon(ethereal, no_anim = TRUE, defdir = SOUTH)) diff --git a/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient.png b/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient.png new file mode 100644 index 000000000000..479a70ec2ac9 Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient_ethereal.png b/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient_ethereal.png new file mode 100644 index 000000000000..d3850e1e82ef Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_hair_gradient_hair_gradient_ethereal.png differ diff --git a/code/modules/unit_tests/slapcrafting.dm b/code/modules/unit_tests/slapcrafting.dm new file mode 100644 index 000000000000..76e777ccd441 --- /dev/null +++ b/code/modules/unit_tests/slapcrafting.dm @@ -0,0 +1,26 @@ +/datum/unit_test/slapcrafting + +/datum/unit_test/slapcrafting/Run() + var/mob/living/carbon/human/consistent/expert = EASY_ALLOCATE() + var/obj/item/paper/disguise = EASY_ALLOCATE() + var/obj/item/toy/mecha/odysseus/cool_robot = EASY_ALLOCATE() + + var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/slapcraft_tester) + cool_robot.AddElement(/datum/element/slapcrafting, slapcraft_recipe_list) + + expert.put_in_active_hand(disguise) + click_wrapper(expert, cool_robot) + var/obj/item/toy/mecha/reticence/cooler_robot = locate() in run_loc_floor_bottom_left + TEST_ASSERT_NOTNULL(cooler_robot, "Slapcrafting did not successfully produce an item") + qdel(cooler_robot) + +/datum/crafting_recipe/slapcraft_tester + name = "Cooler Robot" + result = /obj/item/toy/mecha/reticence + reqs = list( + /obj/item/paper = 1, + /obj/item/toy/mecha/odysseus = 1, + ) + time = 0 + category = CAT_ENTERTAINMENT + crafting_flags = CRAFT_SKIP_MATERIALS_PARITY diff --git a/code/modules/unit_tests/teleporters.dm b/code/modules/unit_tests/teleporters.dm index e1af0a71a351..01f442d599ea 100644 --- a/code/modules/unit_tests/teleporters.dm +++ b/code/modules/unit_tests/teleporters.dm @@ -1,3 +1,5 @@ +/datum/unit_test/teleporter + /datum/unit_test/teleporter/Run() // Put down the teleporter machinery var/obj/machinery/teleport/hub/hub = allocate(/obj/machinery/teleport/hub) @@ -16,3 +18,23 @@ computer.set_teleport_target(beacon) beacon.turn_off() TEST_ASSERT_NULL(computer.target_ref, "Teleporter beacon isn't properly turned off.") + +///Checks that the hand teleporter makes a portal, that that portal is linked to another portal, and that that portal is closed +/datum/unit_test/hand_teleporter + +/datum/unit_test/hand_teleporter/Run() + var/mob/living/carbon/human/consistent/scientist = EASY_ALLOCATE() + var/obj/item/hand_tele/teleporter = EASY_ALLOCATE() + var/turf/turf_the_portal_will_be_on = get_step(run_loc_floor_bottom_left, EAST) + + teleporter.last_portal_location = "portal_location_dangerous" // The define doesn't reach all the way out here, but surely it'll never change + teleporter.away_restricted = FALSE + + scientist.put_in_active_hand(teleporter) + click_wrapper(scientist, turf_the_portal_will_be_on, list(RIGHT_CLICK = TRUE, BUTTON = RIGHT_CLICK)) + var/obj/effect/portal/working_portal = locate() in turf_the_portal_will_be_on + TEST_ASSERT_NOTNULL(working_portal, "Hand teleporter failed to create a portal right-clicking with PORTAL_LOCATION_DANGEROUS") + TEST_ASSERT_NOTNULL(working_portal.linked, "Hand teleporter failed to properly link a created portal") + + click_wrapper(scientist, working_portal) + TEST_ASSERT(QDELETED(working_portal), "Hand teleporter failed to dispel its created portal") diff --git a/code/modules/unit_tests/tile_replacement.dm b/code/modules/unit_tests/tile_replacement.dm new file mode 100644 index 000000000000..d72e1791e660 --- /dev/null +++ b/code/modules/unit_tests/tile_replacement.dm @@ -0,0 +1,41 @@ +/// Tests that replacing tiling using a crowbar or screwdriver in the offhand works properly +/datum/unit_test/tile_replacement + var/old_turf_type + var/turf/target_turf + +/datum/unit_test/tile_replacement/Run() + var/mob/living/carbon/human/consistent/interior_designer = EASY_ALLOCATE() + var/obj/item/crowbar/ripper = EASY_ALLOCATE() + var/obj/item/screwdriver/screwer = EASY_ALLOCATE() + var/obj/item/stack/tile/iron/first_tile = EASY_ALLOCATE(1) + var/obj/item/stack/tile/wood/wooden_replacement = EASY_ALLOCATE(1) + + target_turf = get_step(run_loc_floor_bottom_left, EAST) + old_turf_type = target_turf.type + target_turf.ChangeTurf(/turf/open/floor/plating) + + interior_designer.put_in_inactive_hand(ripper) + interior_designer.put_in_active_hand(first_tile) + + click_wrapper(interior_designer, target_turf) + TEST_ASSERT(istype(target_turf, /turf/open/floor/iron), "Clicking a plating with a floor tile did not place plating") + TEST_ASSERT(QDELETED(first_tile), "Placing floor tiling did not consume the tile") + + interior_designer.put_in_active_hand(wooden_replacement) + click_wrapper(interior_designer, target_turf) + TEST_ASSERT(istype(target_turf, /turf/open/floor/wood), "Clicking floor tiling with a wood tile and a crowbar in the off-hand did not replace the tile") + TEST_ASSERT(QDELETED(wooden_replacement), "Replacing floor tiling with wooden tiling did not consume the tile") + + interior_designer.drop_all_held_items() + interior_designer.put_in_inactive_hand(screwer) + first_tile = locate(/obj/item/stack/tile/iron) in target_turf + TEST_ASSERT_NOTNULL(first_tile, "Replacing a floor tile with a wooden one did not drop the normal tile") + + interior_designer.put_in_active_hand(first_tile) + click_wrapper(interior_designer, target_turf) + TEST_ASSERT(istype(target_turf, /turf/open/floor/iron), "Failed to replace wooden floor tiling using a screwdriver") + TEST_ASSERT_NOTNULL(locate(/obj/item/stack/tile/wood) in target_turf, "Replacing wooden floor tiling using a screwdriver did not drop the tile") + +/datum/unit_test/tile_replacement/Destroy() + target_turf.ChangeTurf(old_turf_type) + return ..() diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index f653faff9f00..73561cd0f407 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -151,10 +151,14 @@ var/atom/spawned_item = spawn_item(item, user, uplink_handler, source) log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [source]'s uplink") user.playsound_local(get_turf(user), 'sound/effects/kaching.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE) - if(purchase_log_vis && uplink_handler.purchase_log) - uplink_handler.purchase_log.LogPurchase(spawned_item, src, cost) + if(uplink_handler.purchase_log) + log_purchase(spawned_item, uplink_handler) if(lock_other_purchases) uplink_handler.shop_locked = TRUE + return spawned_item + +/datum/uplink_item/proc/log_purchase(atom/spawned_item, datum/uplink_handler/uplink_handler) + uplink_handler.purchase_log.log_purchase(spawned_item, src, cost) /// Spawns an item in the world /datum/uplink_item/proc/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source) diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm index decaf1ba271c..e2feec18ce3a 100644 --- a/code/modules/uplink/uplink_items/bundle.dm +++ b/code/modules/uplink/uplink_items/bundle.dm @@ -37,11 +37,13 @@ desc = "A telecrystal in its rawest and purest form; can be utilized on active uplinks to increase their telecrystal count." item = /obj/item/stack/telecrystal cost = 1 - // Don't add telecrystals to the purchase_log since - // it's just used to buy more items (including itself!) - purchase_log_vis = FALSE purchasable_from = NONE +// Don't add telecrystals to the purchase_log since +// it's just used to buy more items (including itself!) +/datum/uplink_item/bundles_tc/telecrystal/log_purchase(atom/spawned_item, datum/uplink_handler/uplink_handler) + return + /datum/uplink_item/bundles_tc/bundle_a name = "Syndi-kit Tactical" desc = "Syndicate Bundles, also known as Syndi-Kits, are specialized groups of items that arrive in a plain box. \ @@ -132,7 +134,28 @@ "style" = /datum/pod_style/syndicate, "spawn" = surplus_crate, )) - return source //For log icon + return surplus_crate //For log icon + +/datum/uplink_item/bundles_tc/surplus/log_purchase(atom/spawned_item, datum/uplink_handler/uplink_handler) + . = ..() + var/list/entries = list() + for(var/obj/item/loot in spawned_item) + if(entries[loot.type]) + var/datum/uplink_purchase_entry/existing_entry = entries[loot.type] + existing_entry.amount_purchased += 1 + continue + + var/datum/uplink_purchase_entry/custom_entry = new() + custom_entry.set_item(loot) + custom_entry.base_cost = 0 + custom_entry.spent_cost = 0 + custom_entry.amount_purchased = 1 + custom_entry.name = "Surplus Item: [format_text(loot.name)]" + custom_entry.desc = "Obtained via surplus crate." + entries[loot.type] = custom_entry + + for(var/entry_type, entry_datum in entries) + uplink_handler.purchase_log.log_purchase_custom(entry_datum) /datum/uplink_item/bundles_tc/surplus/united name = "United Surplus Crate" diff --git a/code/modules/uplink/uplink_items/device_tools.dm b/code/modules/uplink/uplink_items/device_tools.dm index 1c3e2edc0007..1cb2815e6e3d 100644 --- a/code/modules/uplink/uplink_items/device_tools.dm +++ b/code/modules/uplink/uplink_items/device_tools.dm @@ -239,7 +239,7 @@ desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. \ This board bypasses all access restrictions on the console. \ Be careful with wording, as artificial intelligences may look for loopholes to exploit." - item = /obj/item/ai_module/syndicate + item = /obj/item/ai_module/law/syndicate cost = 4 /datum/uplink_item/device_tools/hypnotic_flash diff --git a/code/modules/uplink/uplink_purchase_log.dm b/code/modules/uplink/uplink_purchase_log.dm index c9ac13fa13fd..b3041e2aec6f 100644 --- a/code/modules/uplink/uplink_purchase_log.dm +++ b/code/modules/uplink/uplink_purchase_log.dm @@ -39,30 +39,37 @@ GLOBAL_LIST(uplink_purchase_logs_by_key) //assoc key = /datum/uplink_purchase_lo /datum/uplink_purchase_log/proc/TotalTelecrystalsSpent() . = total_spent +#define FORMAT_COST(uplink_entry) (uplink_entry.spent_cost ? "[uplink_entry.spent_cost] TC" : (uplink_entry.base_cost ? "Free" : "Surplus")) + /datum/uplink_purchase_log/proc/generate_render(show_key = TRUE) . = "" for(var/hash in purchase_log) var/datum/uplink_purchase_entry/UPE = purchase_log[hash] - . += "\[[UPE.icon_b64][show_key?"([owner])":""][UPE.name]
    [UPE.spent_cost ? "[UPE.spent_cost] TC" : "[UPE.base_cost] TC
    (Surplus)"]
    [UPE.desc]
    [(UPE.amount_purchased > 1) ? "x[UPE.amount_purchased]" : ""]\]
    " + . += "\[[UPE.icon_b64][show_key ? "([owner])":""][UPE.name]
    [FORMAT_COST(UPE)]
    [UPE.desc]
    [(UPE.amount_purchased > 1) ? "x[UPE.amount_purchased]" : ""]\]
    " + +#undef FORMAT_COST -/datum/uplink_purchase_log/proc/LogPurchase(atom/A, datum/uplink_item/uplink_item, spent_cost) - var/datum/uplink_purchase_entry/UPE +/// Logs that an uplink item was purchased +/datum/uplink_purchase_log/proc/log_purchase(atom/spawned_item, datum/uplink_item/uplink_item, spent_cost) var/hash = hash_purchase(uplink_item, spent_cost) - if(purchase_log[hash]) - UPE = purchase_log[hash] - else - UPE = new - purchase_log[hash] = UPE - UPE.path = A.type - UPE.icon_b64 = "[icon2base64html(A)]" - UPE.desc = uplink_item.desc - UPE.name = uplink_item.name - UPE.base_cost = initial(uplink_item.cost) - UPE.spent_cost = spent_cost + var/datum/uplink_purchase_entry/purchase_entry = purchase_log[hash] + if(isnull(purchase_entry)) + purchase_entry = new() + purchase_entry.set_item(spawned_item) + purchase_entry.name = uplink_item.name + purchase_entry.desc = uplink_item.desc + purchase_entry.base_cost = initial(uplink_item.cost) + purchase_entry.spent_cost = spent_cost + purchase_log[hash] = purchase_entry - UPE.amount_purchased++ + purchase_entry.amount_purchased += 1 total_spent += spent_cost +/// Used to add custom items into the purchase log +/datum/uplink_purchase_log/proc/log_purchase_custom(datum/uplink_purchase_entry/custom_entry) + purchase_log[REF(custom_entry)] = custom_entry + total_spent += custom_entry.spent_cost + /datum/uplink_purchase_log/proc/hash_purchase(datum/uplink_item/uplink_item, spent_cost) return "[uplink_item.type]|[uplink_item.name]|[uplink_item.cost]|[spent_cost]" @@ -74,3 +81,7 @@ GLOBAL_LIST(uplink_purchase_logs_by_key) //assoc key = /datum/uplink_purchase_lo var/base_cost var/spent_cost var/name + +/datum/uplink_purchase_entry/proc/set_item(obj/item/to_set) + path = to_set.type + icon_b64 = "[icon2base64html(to_set)]" diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index b87f18a82f0d..17efb7432b83 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -301,49 +301,6 @@ servo = locate(/obj/item/stock_parts/servo) in contents update_part_values() -/obj/vehicle/sealed/mecha/atom_destruction() - spark_system?.start() - loc.assume_air(cabin_air) - - var/mob/living/silicon/ai/unlucky_ai - for(var/mob/living/occupant as anything in occupants) - if(!isAI(occupant)) - mob_exit(occupant, forced = TRUE) - if(!isbrain(occupant)) // who would win.. 1 brain vs 1 sleep proc.. - occupant.SetSleeping(destruction_sleep_duration) - continue - - var/mob/living/silicon/ai/ai = occupant - if(ai.linked_core || ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities - mob_exit(ai, silent = TRUE, forced = TRUE) // so we dont ghost the AI - continue - - unlucky_ai = occupant - ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS) - ai.gib(DROP_ALL_REMAINS) //No wreck, no AI to recover - - if(ore_box) - INVOKE_ASYNC(ore_box, TYPE_PROC_REF(/obj/structure/ore_box, dump_box_contents)) - - if(!wreckage) - return ..() - - var/obj/structure/mecha_wreckage/wreck = new wreckage(loc, unlucky_ai) - for(var/obj/item/mecha_parts/mecha_equipment/equipment in flat_equipment) - if(equipment.detachable && prob(30)) - wreck.crowbar_salvage += equipment - equipment.detach(wreck) //detaches from src into wreck - equipment.active = TRUE - else - equipment.detach(loc) - qdel(equipment) - - if(cell) - wreck.crowbar_salvage += cell - cell.forceMove(wreck) - cell.use(rand(0, cell.charge), TRUE) - cell = null - /obj/vehicle/sealed/mecha/update_icon_state() icon_state = get_mecha_occupancy_state() @@ -484,7 +441,8 @@ return cell /obj/vehicle/sealed/mecha/rust_heretic_act() - take_damage(500, BRUTE) + take_damage(500, BRUTE) + return TRUE /obj/vehicle/sealed/mecha/proc/restore_equipment() equipment_disabled = FALSE diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm index 4f9794a7c93c..60c95e879417 100644 --- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm +++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm @@ -231,7 +231,7 @@ crushed_victim.adjust_brute_loss(15) continue to_chat(crushed_victim, span_userdanger("[chassis] crashes down on you from above!")) - if(crushed_victim.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(crushed_victim)) crushed_victim.investigate_log("has been gibbed by a falling Savannah Ivanov mech.", INVESTIGATE_DEATHS) crushed_victim.gib(DROP_ALL_REMAINS) continue diff --git a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm index 2636186bb06d..83e7a544fee9 100644 --- a/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/medical_tools.dm @@ -98,10 +98,8 @@ return data var/patient_state switch(patient.stat) - if(CONSCIOUS) - patient_state = "Conscious" - if(UNCONSCIOUS) - patient_state = "Unconscious" + if(STABLE) + patient_state = "Stable" if(DEAD) patient_state = "*Dead*" if(SOFT_CRIT, HARD_CRIT) diff --git a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm index f584bb3822d0..74f6f929e649 100644 --- a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm @@ -305,7 +305,9 @@ can_be_toggled = TRUE active = FALSE custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT) - ///Type of fuel the generator is using. Is set in generator_init() to add the starting amount of fuel + ///Type of fuel the generator is using. + var/fuel_type = /obj/item/stack/sheet/mineral/plasma + ///Actual physical fuel within the generator. Is set in generator_init() to add the starting amount of fuel var/obj/item/stack/sheet/fuel = null ///Fuel used per second while idle, not generating, in units var/fuelrate_idle = 0.00625 * SHEET_MATERIAL_AMOUNT @@ -348,16 +350,16 @@ return TRUE /obj/item/mecha_parts/mecha_equipment/generator/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(istype(tool, fuel)) - load_fuel(tool, user) - return ITEM_INTERACT_SUCCESS - return NONE + if(!istype(tool, fuel_type)) + return NONE + load_fuel(tool, user) + return ITEM_INTERACT_SUCCESS /obj/item/mecha_parts/mecha_equipment/generator/process(seconds_per_tick) if(!chassis) active = FALSE return PROCESS_KILL - if(fuel.amount <= 0) + if(!fuel?.amount) active = FALSE log_message("Deactivated - no fuel.", LOG_MECHA) to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][span_notice("Fuel reserves depleted.")]") @@ -381,13 +383,16 @@ to_chat(user, "[icon2html(src, user)][span_warning("[fuel] traces in target minimal! [inserted_fuel] cannot be used as fuel.")]") return //how much fuel is needed to fill the generator to its max capacity, in units - var/units_to_load = max(max_fuel - fuel.amount * SHEET_MATERIAL_AMOUNT, 0) + var/units_to_load = isnull(fuel) ? max_fuel : max(max_fuel - fuel.amount * SHEET_MATERIAL_AMOUNT, 0) if(!units_to_load) to_chat(user, "[icon2html(src, user)][span_notice("Unit is full.")]") return //how much new fuel are we inserting, in sheets var/fuel_to_load = min(max(round(units_to_load / SHEET_MATERIAL_AMOUNT), 1), inserted_fuel.amount) - fuel.amount += fuel_to_load + if(fuel) + fuel.amount += fuel_to_load + else + fuel = new(src, fuel_to_load) inserted_fuel.use(fuel_to_load) to_chat(user, "[icon2html(src, user)][span_notice("[fuel_to_load] unit\s of [fuel] successfully loaded.")]") @@ -398,7 +403,7 @@ /// Version without the initial fuel /obj/item/mecha_parts/mecha_equipment/generator/printed -/obj/item/mecha_parts/mecha_equipment/generator/generator_init() +/obj/item/mecha_parts/mecha_equipment/generator/printed/generator_init() return /////////////////////////////////////////// THRUSTERS ///////////////////////////////////////////// diff --git a/code/modules/vehicles/mecha/mecha_ai_interaction.dm b/code/modules/vehicles/mecha/mecha_ai_interaction.dm index 7a13e423bb43..9db9f31f4bf4 100644 --- a/code/modules/vehicles/mecha/mecha_ai_interaction.dm +++ b/code/modules/vehicles/mecha/mecha_ai_interaction.dm @@ -95,7 +95,7 @@ return if(AI.deployed_shell) //Recall AI if shelled so it can be checked for a client AI.disconnect_shell() - if(AI.stat || !AI.client) + if(IS_UNCONSCIOUS_OR_CRIT(AI) || !AI.client) to_chat(user, span_warning("[AI.name] is currently unresponsive, and cannot be uploaded.")) return if((LAZYLEN(occupants) >= max_occupants) || dna_lock) //Normal AIs cannot steal mechs! diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm index 393c478b5198..6830cecc005b 100644 --- a/code/modules/vehicles/mecha/mecha_defense.dm +++ b/code/modules/vehicles/mecha/mecha_defense.dm @@ -563,3 +563,46 @@ if(rcd_equip.internal_rcd.install_upgrade(rcd_upgrade, user)) return ITEM_INTERACT_SUCCESS return ITEM_INTERACT_BLOCKING + + +/obj/vehicle/sealed/mecha/atom_destruction() + spark_system?.start() + loc.assume_air(cabin_air) + + var/mob/living/silicon/ai/unlucky_ai + for(var/mob/living/occupant as anything in occupants) + if(!isAI(occupant)) + mob_exit(occupant, forced = TRUE) + if(!isbrain(occupant)) // who would win.. 1 brain vs 1 sleep proc.. + occupant.SetSleeping(destruction_sleep_duration) + continue + + var/mob/living/silicon/ai/ai = occupant + if(ai.linked_core || ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities + mob_exit(ai, silent = TRUE, forced = TRUE) // so we dont ghost the AI + continue + + unlucky_ai = occupant + ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS) + ai.gib(DROP_ALL_REMAINS) //No wreck, no AI to recover + + if(ore_box) + INVOKE_ASYNC(ore_box, TYPE_PROC_REF(/obj/structure/ore_box, dump_box_contents)) + + if(wreckage) + var/obj/structure/mecha_wreckage/wreck = new wreckage(loc, unlucky_ai) + for(var/obj/item/mecha_parts/mecha_equipment/equipment in flat_equipment) + if(equipment.detachable && prob(30)) + wreck.crowbar_salvage += equipment + equipment.detach(wreck) //detaches from src into wreck + equipment.active = TRUE + else + equipment.detach(loc) + qdel(equipment) + + if(cell) + wreck.crowbar_salvage += cell + cell.forceMove(wreck) + cell.use(rand(0, cell.charge), TRUE) + cell = null + return ..() diff --git a/code/modules/vehicles/mecha/working/ripley.dm b/code/modules/vehicles/mecha/working/ripley.dm index ffa6c77630fd..1f649dd5d618 100644 --- a/code/modules/vehicles/mecha/working/ripley.dm +++ b/code/modules/vehicles/mecha/working/ripley.dm @@ -327,7 +327,7 @@ GLOBAL_DATUM(cargo_ripley, /obj/vehicle/sealed/mecha/ripley/cargo) /obj/item/mecha_parts/mecha_equipment/ejector/relay_container_resist_act(mob/living/user, obj/container) to_chat(user, span_notice("You lean on the back of [container] and start pushing so it falls out of [src].")) if(do_after(user, 30 SECONDS, target = container)) - if(!user || user.stat != CONSCIOUS || user.loc != src || container.loc != src ) + if(!user || IS_UNCONSCIOUS_OR_CRIT(user) || user.loc != src || container.loc != src ) return to_chat(user, span_notice("You successfully pushed [container] out of [src]!")) container.forceMove(drop_location()) diff --git a/code/modules/vehicles/vehicle_key.dm b/code/modules/vehicles/vehicle_key.dm index 7bc162c77ef7..e2572ea48af6 100644 --- a/code/modules/vehicles/vehicle_key.dm +++ b/code/modules/vehicles/vehicle_key.dm @@ -75,7 +75,7 @@ /obj/item/key/proc/manual_suicide(mob/living/user) if(user) user.remove_atom_colour(ADMIN_COLOUR_PRIORITY) - user.visible_message(span_suicide("[user] forgot [user.p_they()] isn't actually a janicart! That's a paddlin'!")) + user.visible_message(span_suicide("[user] forgot [user.p_they()] [user.p_are()]n't actually a janicart! That's a paddlin'!")) if(user.mind?.get_skill_level(/datum/skill/cleaning) >= SKILL_LEVEL_LEGENDARY) //Janny janny janny janny janny playsound(src, 'sound/effects/adminhelp.ogg', 50, TRUE, -1) user.adjust_oxy_loss(200) diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm index 234a8453879c..644d3e4a8741 100644 --- a/code/modules/vending/boozeomat.dm +++ b/code/modules/vending/boozeomat.dm @@ -45,20 +45,22 @@ "name" = "Non-Alcoholic", "icon" = "bottle-water", "products" = list( - /obj/item/reagent_containers/cup/glass/bottle/hakka_mate = 5, /obj/item/reagent_containers/cup/glass/ice = 10, - /obj/item/reagent_containers/cup/glass/bottle/grenadine = 4, - /obj/item/reagent_containers/cup/soda_cans/melon_soda = 5, + /obj/item/reagent_containers/cup/glass/waterbottle = 8, + /obj/item/reagent_containers/cup/glass/waterbottle/large = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/lemonjuice = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/limejuice = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/orangejuice = 4, - /obj/item/reagent_containers/cup/glass/bottle/juice/menthol = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/cream = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/tomatojuice = 4, + /obj/item/reagent_containers/cup/glass/bottle/grenadine = 4, + /obj/item/reagent_containers/cup/glass/bottle/juice/menthol = 4, + /obj/item/reagent_containers/cup/glass/bottle/hakka_mate = 5, /obj/item/reagent_containers/cup/soda_cans/sodawater = 15, /obj/item/reagent_containers/cup/soda_cans/sol_dry = 8, /obj/item/reagent_containers/cup/soda_cans/cola = 8, /obj/item/reagent_containers/cup/soda_cans/tonic = 8, + /obj/item/reagent_containers/cup/soda_cans/melon_soda = 5, ), ), diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index 262024004eb1..c9e1532e0de5 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -425,7 +425,7 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE) /obj/item/construction/plumbing/service = 1, ) refill_canister = /obj/item/vending_refill/wardrobe/bar_wardrobe - payment_department = ACCOUNT_MED + payment_department = ACCOUNT_SRV extra_price = PAYCHECK_COMMAND /obj/item/vending_refill/wardrobe/bar_wardrobe @@ -587,6 +587,9 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE) premium = list( /obj/item/clothing/head/chaplain/bishopmitre = 1, /obj/item/clothing/suit/chaplainsuit/bishoprobe = 1, + /obj/item/clothing/head/costume/pharaoh = 1, + /obj/item/clothing/head/costume/nemes = 1, + /obj/item/clothing/suit/costume/nemes = 1, ) refill_canister = /obj/item/vending_refill/wardrobe/chap_wardrobe payment_department = ACCOUNT_SRV diff --git a/code/modules/wiremod/components/atom/health_state.dm b/code/modules/wiremod/components/atom/health_state.dm index 650311159415..d04d281d1135 100644 --- a/code/modules/wiremod/components/atom/health_state.dm +++ b/code/modules/wiremod/components/atom/health_state.dm @@ -44,14 +44,14 @@ var/state = organism.stat switch(current_option) if("Alive") - return state != DEAD + return !IS_DEAD_OR_FAKING(organism) + if("Deceased") + return IS_DEAD_OR_FAKING(organism) if("Asleep") - return !!organism.IsSleeping() && !organism.IsUnconscious() + return IS_UNCONSCIOUS_AND_ALIVE(organism) && state == STABLE + if("Unconscious") + return IS_UNCONSCIOUS_AND_ALIVE(organism) if("Critical") return state == SOFT_CRIT || state == HARD_CRIT - if("Unconscious") - return state == UNCONSCIOUS || state == HARD_CRIT || !!organism.IsUnconscious() - if("Deceased") - return state == DEAD //Unknown state, something fucked up really bad - just return false return FALSE diff --git a/code/modules/wiremod/components/atom/remotecam.dm b/code/modules/wiremod/components/atom/remotecam.dm index 12c7cc1d91c7..c9c318537cc1 100644 --- a/code/modules/wiremod/components/atom/remotecam.dm +++ b/code/modules/wiremod/components/atom/remotecam.dm @@ -392,7 +392,7 @@ return var/obj/item/organ/cyberimp/bci/bci = shell_parent //If shell is not currently inside a head, or user is currently blind, or user is dead - if(!bci.owner || bci.owner.is_blind() || bci.owner.stat >= UNCONSCIOUS) + if(!bci.owner || bci.owner.is_blind() || IS_UNCONSCIOUS(bci.owner)) close_camera() return var/obj/item/stock_parts/power_store/cell = parent.get_cell() diff --git a/code/modules/wiremod/components/bci/hud/target_intercept.dm b/code/modules/wiremod/components/bci/hud/target_intercept.dm index 016f677f10e7..2a487e94d8e2 100644 --- a/code/modules/wiremod/components/bci/hud/target_intercept.dm +++ b/code/modules/wiremod/components/bci/hud/target_intercept.dm @@ -39,7 +39,7 @@ return var/mob/living/owner = bci.owner - if(!owner || !istype(owner) || !owner.client || owner.stat >= SOFT_CRIT) + if(!owner || !istype(owner) || !owner.client || IS_UNCONSCIOUS_OR_CRIT(owner)) return if(TIMER_COOLDOWN_RUNNING(parent.shell, COOLDOWN_CIRCUIT_TARGET_INTERCEPT)) diff --git a/code/modules/wiremod/components/bci/thought_listener.dm b/code/modules/wiremod/components/bci/thought_listener.dm index f76f53b6ba20..ba70ed828b6a 100644 --- a/code/modules/wiremod/components/bci/thought_listener.dm +++ b/code/modules/wiremod/components/bci/thought_listener.dm @@ -48,7 +48,7 @@ var/mob/living/owner = bci.owner - if(!owner || !istype(owner) || !owner.client || (owner.stat >= SOFT_CRIT)) + if(!owner || !istype(owner) || !owner.client || IS_UNCONSCIOUS_OR_CRIT(owner)) failure.set_output(COMPONENT_SIGNAL) return @@ -57,7 +57,7 @@ /obj/item/circuit_component/thought_listener/proc/thought_listen(mob/living/owner) var/message = tgui_input_text(owner, input_desc.value ? input_desc.value : "", input_name.value ? input_name.value : "Thought Listener", "", max_length = MAX_MESSAGE_LEN) - if(QDELETED(owner) || owner.stat >= SOFT_CRIT) + if(QDELETED(owner) || IS_UNCONSCIOUS_OR_CRIT(owner)) return output.set_output(message) trigger_output.set_output(COMPONENT_SIGNAL) diff --git a/code/modules/wiremod/core/component.dm b/code/modules/wiremod/core/component.dm index 7dda865fb640..79c97b46a368 100644 --- a/code/modules/wiremod/core/component.dm +++ b/code/modules/wiremod/core/component.dm @@ -11,6 +11,7 @@ name = COMPONENT_DEFAULT_NAME icon = 'icons/obj/devices/circuitry_n_data.dmi' icon_state = "component" + worn_icon = null inhand_icon_state = "electronic" lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index d08470d0ac98..ce1c01a37f25 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -434,7 +434,7 @@ if (locked) message = "it won't budge!" - else if (user.stat != CONSCIOUS) + else if (IS_UNCONSCIOUS_OR_CRIT(user)) message = "you don't have the energy!" if (!isnull(message)) diff --git a/code/modules/wiremod/shell/drone.dm b/code/modules/wiremod/shell/drone.dm index 578af2f4acde..6ee5365429bf 100644 --- a/code/modules/wiremod/shell/drone.dm +++ b/code/modules/wiremod/shell/drone.dm @@ -88,7 +88,7 @@ /obj/item/circuit_component/bot_circuit/input_received(datum/port/input/port) var/mob/living/shell = parent.shell - if(!istype(shell) || shell.stat) + if(!istype(shell) || IS_UNCONSCIOUS_OR_CRIT(shell)) return var/direction diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index 5ebe812a0ef6..10315aaefaa0 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -90,7 +90,7 @@ // mindless zombies are nerfed a bit to encourage infecting real players instead of random morgue bodies target.set_species(isnull(target.mind) ? /datum/species/zombie/infectious/mindless : /datum/species/zombie/infectious) - var/stand_up = (target.stat == DEAD) || (target.stat == UNCONSCIOUS) + var/stand_up = IS_UNCONSCIOUS(target) //Fully heal the zombie's damage the first time they rise if(!target.heal_and_revive(0, span_danger("[target] suddenly convulses, as [target.p_they()][stand_up ? " stagger to [target.p_their()] feet and" : ""] gain a ravenous hunger in [target.p_their()] eyes!"))) @@ -112,6 +112,7 @@ after_assumed_control = CALLBACK(src, PROC_REF(after_ghost_control)), \ joinable_mobs_title = "Zombies", \ )) + SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_TOMBSTONE] = TRUE /obj/item/organ/zombie_infection/proc/check_ghost_control_eligibility(mob/candidate) if(QDELETED(src) || QDELETED(owner)) diff --git a/config/darkpack_config.txt b/config/darkpack_config.txt index e7839be05960..e9863297dedc 100644 --- a/config/darkpack_config.txt +++ b/config/darkpack_config.txt @@ -73,6 +73,9 @@ LOG_STATS ## Bool for if roleplay only merits/quirks are enabled ROLEPLAY_ONLY_MERITS 1 +## String for how ttrpg accurate our merits must be. Offical, Storyteller Vault, or Homebrew +TTRPG_ACCURATE_CUTOFF_MERITS Homebrew + ## Configs for the type of attack we use when we click at a ranged distance. Only one can be enabled at a time. #DIRECTIONAL_COMBAT SWING_COMBAT diff --git a/dependencies.sh b/dependencies.sh index b6d9f2eb3121..c60e876da9fa 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -5,7 +5,7 @@ # byond version. Also must be updated in .tgs.yml export BYOND_MAJOR=516 -export BYOND_MINOR=1659 +export BYOND_MINOR=1685 #rust_g git tag export RUST_G_VERSION=6.2.0 diff --git a/html/changelogs/archive/2026-07.yml b/html/changelogs/archive/2026-07.yml index 0495c92cea1f..5d7af838237e 100644 --- a/html/changelogs/archive/2026-07.yml +++ b/html/changelogs/archive/2026-07.yml @@ -184,3 +184,23 @@ - map: Adds charcoal pens to the caern - map: Adds a slot machine to the gas station - map: Prince office artifact spawn will only have vampire artifacts + Absolucy: + - bugfix: made smartphone contacts lag the server less + Faengi: + - config: Added a config setting to limit high generations to a limited selection + of jobs + - rscadd: Gave the Society of Leopold intel reports to find breachers with + - rscadd: Added more breach phrases to the list + FalloutFalcon: + - balance: You can guestbook from a full screen width + - bugfix: Delirium will properly be applied + - admin: 2 VV topics for managing actions or gifts + buffyuwu: + - rscadd: Admins can now set generations below 8 + chazzyjazzy: + - rscadd: Adds the red news reporter role which can broadcast to the city using + the captain-caster camera and mapped-in greenscreens as well as 'entertainment + monitors' and the mobile cameras + - rscadd: expands lore primers to help new players figure out what morality paths + are as well as some stuff about Garou, Clans, etc + - bugfix: fixes firearms recoil roll difficulty never being reset diff --git a/html/changelogs/archive/2026-08.yml b/html/changelogs/archive/2026-08.yml index 950a4bf78b83..c65b27d4a877 100644 --- a/html/changelogs/archive/2026-08.yml +++ b/html/changelogs/archive/2026-08.yml @@ -11,7 +11,55 @@ them Potatocrip: - code_imp: lets ghouls pick bruiser and sweeper + Faengi: + - rscadd: Added the Uncontrollable flaw for Brujah + FalloutFalcon: + - code_imp: new var on datum level for storing info on what source book it comes + from + - config: Adds config for limiting merit based on official status +2026-08-04: + Absolucy: + - rscadd: Open Seal can now unlock cars. + FalloutFalcon: + - code_imp: makes some stat lists properly lazylists + - code_imp: Changing stats based on stamina or dexterity is handled smarter + KCartridge: + - rscadd: proper TV subtypes + - rscadd: directionals for TV + chazzyjazzy: + - code_imp: adds a landmark for the red news reporter +2026-08-05: + FalloutFalcon: + - rscadd: Seizure Flaw/Deformity + breadcrombs: + - qol: see through can be toggled on the vicissitude horror form + buffyuwu: + - rscadd: ported discipline stat descriptions from TFN + - rscadd: plays a sound when corax toggle flight + - rscadd: gives corax a waddle when walking in bird form + cam_dev: + - rscadd: '"I Nonni" role for Giovanni elders' +2026-08-06: + FalloutFalcon: + - code_imp: Fixes infinite loop caused by teleport moving while having a looped + chain of grabbed mobs + Stutternov: + - balance: Reduces bonus tiles on acute hearing from 3 to 1 (hearing whispers 4 + tiles away down to 2) + - rscadd: Adds brass knuckles to the game and illegal vendor. + - rscadd: Adds spiked knuckles to the Baron. + cam_dev: + - rscadd: added crosses to loadout menu + - qol: crosses can be worn on belt and neck + - image: added two new cross sprites 2026-08-07: + FalloutFalcon: + - bugfix: Masquerade breaches no longer get smuggled out of deathmatch arena and + other spawns not meant to send them to the sewers + - bugfix: Loadout items wont get replaced by robes and masks assuming they cover + your breaches + flleeppyy: + - rscadd: Detective UI is now a possible UI to choose from in preferences. Potatocrip: - balance: slowed down skate shop vehicles dwinters99: diff --git a/icons/area/areas_ruins.dmi b/icons/area/areas_ruins.dmi index 7a559ee6cf6c..12444f6e20fa 100644 Binary files a/icons/area/areas_ruins.dmi and b/icons/area/areas_ruins.dmi differ diff --git a/icons/map_icons/items/_item.dmi b/icons/map_icons/items/_item.dmi index 6fad912023d0..c85b188d20e0 100644 Binary files a/icons/map_icons/items/_item.dmi and b/icons/map_icons/items/_item.dmi differ diff --git a/icons/mob/actions/actions_revenant.dmi b/icons/mob/actions/actions_revenant.dmi index 3b75c4c1d30c..da015b8c0ba2 100644 Binary files a/icons/mob/actions/actions_revenant.dmi and b/icons/mob/actions/actions_revenant.dmi differ diff --git a/icons/mob/clothing/accessories.dmi b/icons/mob/clothing/accessories.dmi index 25ba8311d4cb..8fec7bdd5480 100644 Binary files a/icons/mob/clothing/accessories.dmi and b/icons/mob/clothing/accessories.dmi differ diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi index ef94644898e5..2c3dd9972637 100644 Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi index 81eef910392a..bb62e5084911 100644 Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ diff --git a/icons/mob/inhands/items/sheets_lefthand.dmi b/icons/mob/inhands/items/sheets_lefthand.dmi index 6bfb4fbfdeb5..098121528aa2 100644 Binary files a/icons/mob/inhands/items/sheets_lefthand.dmi and b/icons/mob/inhands/items/sheets_lefthand.dmi differ diff --git a/icons/mob/inhands/items/sheets_righthand.dmi b/icons/mob/inhands/items/sheets_righthand.dmi index c49274871318..2727e849b0b2 100644 Binary files a/icons/mob/inhands/items/sheets_righthand.dmi and b/icons/mob/inhands/items/sheets_righthand.dmi differ diff --git a/icons/mob/inhands/pets_held_lh.dmi b/icons/mob/inhands/pets_held_lh.dmi index 1a864db13410..ec564d01fc20 100644 Binary files a/icons/mob/inhands/pets_held_lh.dmi and b/icons/mob/inhands/pets_held_lh.dmi differ diff --git a/icons/mob/inhands/pets_held_rh.dmi b/icons/mob/inhands/pets_held_rh.dmi index 39332a7e18e4..77b4b8a00e44 100644 Binary files a/icons/mob/inhands/pets_held_rh.dmi and b/icons/mob/inhands/pets_held_rh.dmi differ diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi index f63b5ec10f81..972216b864f6 100644 Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ diff --git a/icons/mob/simple/bargorilla.dmi b/icons/mob/simple/bargorilla.dmi new file mode 100644 index 000000000000..a7a128f6ebda Binary files /dev/null and b/icons/mob/simple/bargorilla.dmi differ diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi index 0a6e4b12731f..ef47d04340ee 100644 Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi index 0692033a5361..7386d9cd630f 100644 Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ diff --git a/icons/obj/debris.dmi b/icons/obj/debris.dmi index 3f7836f5762d..53f25915028a 100644 Binary files a/icons/obj/debris.dmi and b/icons/obj/debris.dmi differ diff --git a/icons/obj/devices/voice.dmi b/icons/obj/devices/voice.dmi index 807308a76180..43c69217322a 100644 Binary files a/icons/obj/devices/voice.dmi and b/icons/obj/devices/voice.dmi differ diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index a4a40d5a3b65..b97f3cc91d78 100644 Binary files a/icons/obj/fishing.dmi and b/icons/obj/fishing.dmi differ diff --git a/icons/obj/fluff/map_previews.dmi b/icons/obj/fluff/map_previews.dmi index 027f664fe168..70aeac45d336 100644 Binary files a/icons/obj/fluff/map_previews.dmi and b/icons/obj/fluff/map_previews.dmi differ diff --git a/icons/obj/machines/law_rack.dmi b/icons/obj/machines/law_rack.dmi new file mode 100644 index 000000000000..c3ebbebd8cc9 Binary files /dev/null and b/icons/obj/machines/law_rack.dmi differ diff --git a/icons/obj/machines/wallmounts.dmi b/icons/obj/machines/wallmounts.dmi index d4126b7ec20f..6c727f169e61 100644 Binary files a/icons/obj/machines/wallmounts.dmi and b/icons/obj/machines/wallmounts.dmi differ diff --git a/icons/obj/medical/chemical.dmi b/icons/obj/medical/chemical.dmi index 8f3267c07d4a..7027a8841d9a 100644 Binary files a/icons/obj/medical/chemical.dmi and b/icons/obj/medical/chemical.dmi differ diff --git a/icons/obj/ore.dmi b/icons/obj/ore.dmi index 8cb07a5312d0..98911053b8bf 100644 Binary files a/icons/obj/ore.dmi and b/icons/obj/ore.dmi differ diff --git a/icons/obj/stack_objects.dmi b/icons/obj/stack_objects.dmi index 8504bbc46906..16899b24b4c8 100644 Binary files a/icons/obj/stack_objects.dmi and b/icons/obj/stack_objects.dmi differ diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi index ba80deac9761..4d937e989f32 100644 Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi index 08d8f436402b..f377d34eb04a 100644 Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ diff --git a/icons/obj/weapons/guns/ammo.dmi b/icons/obj/weapons/guns/ammo.dmi index 06eb704f52dc..660e3e8fc12b 100644 Binary files a/icons/obj/weapons/guns/ammo.dmi and b/icons/obj/weapons/guns/ammo.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 2dffe94cea4c..a0afc5be4f6e 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/icons/turf/floors/concrete.dmi b/icons/turf/floors/concrete.dmi new file mode 100644 index 000000000000..750165c928aa Binary files /dev/null and b/icons/turf/floors/concrete.dmi differ diff --git a/icons/turf/floors/hexacrete.dmi b/icons/turf/floors/hexacrete.dmi new file mode 100644 index 000000000000..9dc57cfdbcdf Binary files /dev/null and b/icons/turf/floors/hexacrete.dmi differ diff --git a/icons/turf/walls/concrete.dmi b/icons/turf/walls/concrete.dmi new file mode 100644 index 000000000000..e4a3ea8f83ca Binary files /dev/null and b/icons/turf/walls/concrete.dmi differ diff --git a/icons/turf/walls/concrete.png b/icons/turf/walls/concrete.png new file mode 100644 index 000000000000..4aa00213a36c Binary files /dev/null and b/icons/turf/walls/concrete.png differ diff --git a/icons/turf/walls/concrete.png.toml b/icons/turf/walls/concrete.png.toml new file mode 100644 index 000000000000..3107f0c3216a --- /dev/null +++ b/icons/turf/walls/concrete.png.toml @@ -0,0 +1,2 @@ +output_name = "concrete" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/turf/walls/dark_pod.dmi b/icons/turf/walls/dark_pod.dmi new file mode 100644 index 000000000000..cdfe8fc7fe46 Binary files /dev/null and b/icons/turf/walls/dark_pod.dmi differ diff --git a/icons/turf/walls/dark_pod.png b/icons/turf/walls/dark_pod.png new file mode 100644 index 000000000000..4116234090d2 Binary files /dev/null and b/icons/turf/walls/dark_pod.png differ diff --git a/icons/turf/walls/dark_pod.png.toml b/icons/turf/walls/dark_pod.png.toml new file mode 100644 index 000000000000..0fc7123b78de --- /dev/null +++ b/icons/turf/walls/dark_pod.png.toml @@ -0,0 +1,2 @@ +output_name = "dark_pod_walls" +template = "bitmask/diagonal_corner_32x32" diff --git a/icons/turf/walls/false_walls.dmi b/icons/turf/walls/false_walls.dmi index 74b72633bb4e..300659d4d1c9 100644 Binary files a/icons/turf/walls/false_walls.dmi and b/icons/turf/walls/false_walls.dmi differ diff --git a/icons/turf/walls/hexacrete.dmi b/icons/turf/walls/hexacrete.dmi new file mode 100644 index 000000000000..eec5eed647a9 Binary files /dev/null and b/icons/turf/walls/hexacrete.dmi differ diff --git a/icons/turf/walls/hexacrete.png b/icons/turf/walls/hexacrete.png new file mode 100644 index 000000000000..818656496e4f Binary files /dev/null and b/icons/turf/walls/hexacrete.png differ diff --git a/icons/turf/walls/hexacrete.png.toml b/icons/turf/walls/hexacrete.png.toml new file mode 100644 index 000000000000..ee4ca98ba7b6 --- /dev/null +++ b/icons/turf/walls/hexacrete.png.toml @@ -0,0 +1,2 @@ +output_name = "hexacrete" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/turf/walls/red_pod.dmi b/icons/turf/walls/red_pod.dmi new file mode 100644 index 000000000000..0897fb07b819 Binary files /dev/null and b/icons/turf/walls/red_pod.dmi differ diff --git a/icons/turf/walls/red_pod.png b/icons/turf/walls/red_pod.png new file mode 100644 index 000000000000..f9f3fa7a3136 Binary files /dev/null and b/icons/turf/walls/red_pod.png differ diff --git a/icons/turf/walls/red_pod.png.toml b/icons/turf/walls/red_pod.png.toml new file mode 100644 index 000000000000..a5e9b3ae9873 --- /dev/null +++ b/icons/turf/walls/red_pod.png.toml @@ -0,0 +1,2 @@ +output_name = "red_pod_walls" +template = "bitmask/diagonal_corner_32x32" diff --git a/interface/skin.dmf b/interface/skin.dmf index 915d7af8586f..bab1cb27d583 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -89,6 +89,15 @@ window "mapwindow" saved-params = "" text = "" align = left + elem "escape_menu" + type = BROWSER + pos = 0,0 + size = 640x480 + anchor1 = 0,0 + anchor2 = 100,100 + background-color = none + inner-background-color = transparent + is-visible = false window "info_and_buttons" elem "info_and_buttons" @@ -137,7 +146,7 @@ window "infobuttons" background-color = none saved-params = "is-checked" text = "Menu" - command = "open-escape-menu" + command = ".winset \"mapwindow.escape_menu.is-visible=true?mapwindow.escape_menu.is-visible=false:mapwindow.escape_menu.is-visible=true\"\n.output mapwindow.escape_menu:update %7B%22type%22%3A%22toggle%22%2C%22payload%22%3Anull%7D" elem "hotkeys" type = BUTTON pos = 92,5 diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index 72b1c80de406..030f4316207b 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -155,6 +155,6 @@ h1.alert, h2.alert {color: #000000;} .policy {color: #9730db; font-style: italic; text-align: center; font-size: 2;} .upside_down {display: inline; -moz-transform: scale(-1, -1); -webkit-transform: scale(-1, -1); -o-transform: scale(-1, -1); -ms-transform: scale(-1, -1); transform: scale(-1, -1);} - +.mansus {color: #0b8000;} .cyan {color: #bde0dc;} "} diff --git a/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.json b/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.json new file mode 100644 index 000000000000..257476df710c --- /dev/null +++ b/modular_darkpack/master_files/code/datums/ai/dog/dog.bt.json @@ -0,0 +1,97 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/dog", + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/is_dragging", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "invert": true + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "observer_abort": "BT_ABORT_BOTH" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/hunt_target/pull_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "hunt_cooldown": "5 SECONDS" + } + } + ] + } + } + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/dog_harassment" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "1 SECONDS", + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_dog" + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/random_speech_loop" + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_interaction_target", + "vars": { + "target_key": "BB_LOW_PRIORITY_HUNTING_TARGET", + "targeting_strategy": "/datum/targeting_strategy/huntable", + "target_source": "/datum/target_source/oview_typed/from_bb_key/hunt_target_list", + "vision_range": 7 + } + } + ] + } + ] +} diff --git a/modular_darkpack/master_files/code/datums/components/crafting/_recipes.dm b/modular_darkpack/master_files/code/datums/components/crafting/_recipes.dm new file mode 100644 index 000000000000..e70a6cb2fa71 --- /dev/null +++ b/modular_darkpack/master_files/code/datums/components/crafting/_recipes.dm @@ -0,0 +1,20 @@ +/datum/crafting_recipe + // STORYTELER_STATS + /// Stat define/typepath required for this recipe. No check if null + var/datum/st_stat/skill_required_for_use = STAT_CRAFTS + /// You need ATLEAST this many dots in a skill to craft. + var/skill_dots_minimum = null // Null by default means it wont even try to get stats. as if its 0 or less, it only catches people with low stats AND a debuff and i dont really care. + + +// STORYTELER_STATS +/datum/crafting_recipe/proc/is_recipe_available(mob/user) + SHOULD_CALL_PARENT(TRUE) + + if(skill_required_for_use && !isnull(skill_dots_minimum)) + var/mob/living/living_user = astype(user) + if(!living_user) + return FALSE + if(living_user.st_get_stat(skill_required_for_use) < skill_dots_minimum) + return FALSE + + return TRUE diff --git a/modular_darkpack/master_files/code/datums/datum.dm b/modular_darkpack/master_files/code/datums/datum.dm new file mode 100644 index 000000000000..6c5d0246d7aa --- /dev/null +++ b/modular_darkpack/master_files/code/datums/datum.dm @@ -0,0 +1,4 @@ + +/datum + /// Lazy list of books/sources of where the type is bassd off of. Page Number indexed by datum. Ex. + var/list/datum/source_book/ttrpg_sources // BOOK_SOURCES diff --git a/modular_darkpack/master_files/code/modules/asset_cache/assets/tgui.dm b/modular_darkpack/master_files/code/modules/asset_cache/assets/tgui.dm new file mode 100644 index 000000000000..a59061f33a88 --- /dev/null +++ b/modular_darkpack/master_files/code/modules/asset_cache/assets/tgui.dm @@ -0,0 +1,6 @@ +/datum/asset/simple/namespaced/escape_menu_sounds + assets = list( + "esc_open.ogg" = file("modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_open.ogg'"), + "esc_middle.ogg" = file("modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_middle.ogg'"), + "esc_close.ogg" = file("modular_darkpack/master_files/sounds/effects/misc/escape_menu/esc_close.ogg'"), + ) diff --git a/modular_darkpack/master_files/code/modules/client/preferences/middleware/quirks.dm b/modular_darkpack/master_files/code/modules/client/preferences/middleware/quirks.dm new file mode 100644 index 000000000000..7c4b6c4bf10b --- /dev/null +++ b/modular_darkpack/master_files/code/modules/client/preferences/middleware/quirks.dm @@ -0,0 +1,3 @@ +/datum/preference_middleware/quirks + var/list/cached_splats_compatibility + var/list/cached_clans_compatibility diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm index c54b4e84351f..c13406ddc01c 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm @@ -4,7 +4,7 @@ if(!ismundane(usr)) return var/mob/living/carbon/human/reporter = usr - if(reporter.stat > UNCONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(reporter)) return if(usr == src) return @@ -22,7 +22,7 @@ if(!ismundane(usr)) return var/mob/living/carbon/human/reporter = usr - if(reporter.stat > UNCONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(reporter)) return if(usr == src) return diff --git a/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm b/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm index 7acebd7c239b..416cf7041877 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm @@ -44,7 +44,6 @@ var/list/datum/component/obeys_commands/minion_command_components = list() var/tentacle_escape_attempt = 0 - var/tentacle_aggro_mode = "Aggressive" var/possessed = FALSE //dominate 5 body posession var/datum/weakref/conditioner // dominate 4 //obfuscate icon, client side diff --git a/modular_darkpack/master_files/icons/hud/screen_detective.dmi b/modular_darkpack/master_files/icons/hud/screen_detective.dmi new file mode 100644 index 000000000000..b8e6435681e2 Binary files /dev/null and b/modular_darkpack/master_files/icons/hud/screen_detective.dmi differ diff --git a/modular_darkpack/modules/beastmaster/code/beastmaster.bt.json b/modular_darkpack/modules/beastmaster/code/beastmaster.bt.json new file mode 100644 index 000000000000..7597ab7bccc5 --- /dev/null +++ b/modular_darkpack/modules/beastmaster/code/beastmaster.bt.json @@ -0,0 +1,6 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/beastmaster_summon", + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" +} diff --git a/modular_darkpack/modules/beastmaster/code/beastmaster_actions.dm b/modular_darkpack/modules/beastmaster/code/beastmaster_actions.dm index d335d75cc636..52f6fbe0f469 100644 --- a/modular_darkpack/modules/beastmaster/code/beastmaster_actions.dm +++ b/modular_darkpack/modules/beastmaster/code/beastmaster_actions.dm @@ -75,10 +75,10 @@ var/datum/ai_controller/controller = minion.ai_controller if(controller) - controller.CancelActions() - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + controller.cancel_current_plan() + controller.clear_blackboard_key(BB_CURRENT_TARGET) + controller.clear_blackboard_key(BB_CURRENT_TARGET_HIDING_LOCATION) controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) var/list/enemies = controller.blackboard[BB_BEASTMASTER_ENEMIES_LIST] if(enemies) diff --git a/modular_darkpack/modules/beastmaster/code/beastmaster_ai.dm b/modular_darkpack/modules/beastmaster/code/beastmaster_ai.dm index ac7b2db26919..a0ff7a9b350e 100644 --- a/modular_darkpack/modules/beastmaster/code/beastmaster_ai.dm +++ b/modular_darkpack/modules/beastmaster/code/beastmaster_ai.dm @@ -6,74 +6,75 @@ BB_BEASTMASTER_ENEMIES_LIST = list(), ) ai_movement = /datum/ai_movement/basic_avoidance + + behavior_tree_json = "modular_darkpack/modules/beastmaster/code/beastmaster.bt.json" + + /* planning_subtrees = list( - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/pet_planning, + /datum/bt_node/subtree/basic_melee_attack_subtree, + /datum/bt_node/subtree/target_retaliate, + /datum/bt_node/subtree/pet_planning, ) + */ + /datum/targeting_strategy/basic/beastmaster_enemies //this is an exact copy of the parent without faction logic. -/datum/targeting_strategy/basic/beastmaster_enemies/can_attack(mob/living/source, atom/movable/target, vision_range) - var/datum/ai_controller/basic_controller/our_controller = source.ai_controller - - if(isnull(our_controller)) - return FALSE - - if(isturf(target) || isnull(target)) +/datum/targeting_strategy/basic/beastmaster_enemies/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) + if(isturf(the_target) || isnull(the_target)) return FALSE - if(isobj(target.loc)) - var/obj/container = target.loc + if(isobj(the_target.loc)) + var/obj/container = the_target.loc if(container.resistance_flags & INDESTRUCTIBLE) return FALSE - if(ismob(target)) - if(source.loc == target) + if(ismob(the_target)) + if(living_mob.loc == the_target) return FALSE - if(HAS_TRAIT(target, TRAIT_GODMODE)) + if(HAS_TRAIT(the_target, TRAIT_GODMODE)) return FALSE - if(vision_range && get_dist(source, target) > vision_range) + if(vision_range && get_dist(living_mob, the_target) > vision_range) return FALSE - if(!can_see(source, target, vision_range)) + if(!can_see(living_mob, the_target, vision_range)) return FALSE - if(source.see_invisible < target.invisibility) + if(living_mob.see_invisible < the_target.invisibility) return FALSE - if(!isturf(source.loc)) + if(!isturf(living_mob.loc)) return FALSE - if(isturf(target.loc) && source.z != target.z) + if(isturf(the_target.loc) && living_mob.z != the_target.z) return FALSE - if(!isliving(target)) + if(!isliving(the_target)) return FALSE - var/mob/living/living_target = target + var/mob/living/living_target = the_target // don't attack friends - var/list/friends = our_controller.blackboard[BB_FRIENDS_LIST] + var/list/friends = controller.blackboard[BB_FRIENDS_LIST] if(friends && (living_target in friends)) return FALSE - // check if we have a commanded target - var/mob/living/commanded_target = our_controller.blackboard[BB_CURRENT_PET_TARGET] + // check if we have a commanded the_target + var/mob/living/commanded_target = controller.blackboard[BB_CURRENT_PET_TARGET] if(commanded_target) if(living_target == commanded_target) - if(living_target.stat > our_controller.blackboard[BB_TARGET_MINIMUM_STAT]) + if(living_target.stat > controller.blackboard[BB_TARGET_MINIMUM_STAT]) return FALSE return TRUE return FALSE - // check if target is in our enemies list - var/list/enemies = our_controller.blackboard[BB_BEASTMASTER_ENEMIES_LIST] + // check if the_target is in our enemies list + var/list/enemies = controller.blackboard[BB_BEASTMASTER_ENEMIES_LIST] if(enemies && length(enemies)) if(living_target in enemies) - if(living_target.stat > our_controller.blackboard[BB_TARGET_MINIMUM_STAT]) + if(living_target.stat > controller.blackboard[BB_TARGET_MINIMUM_STAT]) return FALSE return TRUE diff --git a/modular_darkpack/modules/beastmaster/code/beastmaster_commands.dm b/modular_darkpack/modules/beastmaster/code/beastmaster_commands.dm index 7227053e6ec5..5b1b99a2163d 100644 --- a/modular_darkpack/modules/beastmaster/code/beastmaster_commands.dm +++ b/modular_darkpack/modules/beastmaster/code/beastmaster_commands.dm @@ -41,14 +41,14 @@ enemies += living_target RegisterSignal(living_target, COMSIG_LIVING_DEATH, PROC_REF(on_enemy_death), override = TRUE) - parent.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, living_target) + parent.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, living_target) parent.ai_controller.set_blackboard_key(BB_CURRENT_PET_TARGET, living_target) parent.ai_controller.set_blackboard_key(BB_ACTIVE_PET_COMMAND, src) parent.visible_message(span_warning("[parent] follows [friend]'s gesture towards [living_target] [pointed_reaction]!")) return TRUE /datum/pet_command/attack/beastmaster/execute_action(datum/ai_controller/controller) - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/mob/living/target = controller.blackboard[BB_CURRENT_TARGET] // if current target is invalid, find a new one from enemies list if(!target || target.stat == DEAD) @@ -57,14 +57,14 @@ for(var/mob/living/enemy in enemies) if(!QDELETED(enemy) && enemy.stat != DEAD) target = enemy - controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + controller.set_blackboard_key(BB_CURRENT_TARGET, target) controller.set_blackboard_key(BB_CURRENT_PET_TARGET, target) break // no valid targets, clear everything if(!target) controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + controller.clear_blackboard_key(BB_CURRENT_TARGET) // get owner and check their follow/stay state var/mob/living/pawn = controller.pawn @@ -87,8 +87,7 @@ return // attack the target - controller.queue_behavior(attack_behaviour, BB_BASIC_MOB_CURRENT_TARGET, targeting_strategy_key) - return SUBTREE_RETURN_FINISH_PLANNING + controller.set_behavior_tree_override(SUBPLAN_ID_PET_COMMAND, attack_subtree) /datum/pet_command/attack/beastmaster/proc/on_enemy_death(mob/living/dead_enemy) SIGNAL_HANDLER @@ -104,7 +103,7 @@ UnregisterSignal(dead_enemy, COMSIG_LIVING_DEATH) // if this was our current target, find next target, if there is one - if(parent.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] == dead_enemy) + if(parent.ai_controller.blackboard[BB_CURRENT_TARGET] == dead_enemy) var/mob/living/new_target = null // find the next enemy @@ -117,13 +116,13 @@ if(new_target) // theres a new target so plan your attack parent.ai_controller.set_blackboard_key(BB_CURRENT_PET_TARGET, new_target) - parent.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, new_target) - parent.ai_controller.CancelActions() - parent.ai_controller.able_to_plan = TRUE + parent.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, new_target) + parent.ai_controller.cancel_current_plan() + else // no more enemies, clear and return to previous behavior parent.ai_controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - parent.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + parent.ai_controller.clear_blackboard_key(BB_CURRENT_TARGET) var/list/friends = parent.ai_controller.blackboard[BB_FRIENDS_LIST] if(friends && length(friends)) @@ -145,8 +144,8 @@ // clear all commands controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) + controller.clear_blackboard_key(BB_CURRENT_TARGET) + controller.clear_blackboard_key(BB_CURRENT_TARGET_HIDING_LOCATION) // clear enemies list and unregister signals var/list/enemies = controller.blackboard[BB_BEASTMASTER_ENEMIES_LIST] @@ -155,7 +154,7 @@ UnregisterSignal(enemy, COMSIG_LIVING_DEATH) enemies.Cut() - controller.CancelActions() + controller.cancel_current_plan() return /datum/pet_command/befriend_target @@ -213,10 +212,10 @@ UnregisterSignal(living_target, COMSIG_LIVING_DEATH) //and if theyre the current target remove that too - if(parent.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] == living_target) + if(parent.ai_controller.blackboard[BB_CURRENT_TARGET] == living_target) parent.ai_controller.clear_blackboard_key(BB_CURRENT_PET_TARGET) - parent.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - parent.ai_controller.CancelActions() + parent.ai_controller.clear_blackboard_key(BB_CURRENT_TARGET) + parent.ai_controller.cancel_current_plan() parent.visible_message(span_notice("[parent] follows [friend]'s gesture and befriends [living_target]!")) return TRUE @@ -226,4 +225,3 @@ /datum/pet_command/befriend_target/execute_action(datum/ai_controller/controller) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) - return SUBTREE_RETURN_FINISH_PLANNING diff --git a/modular_darkpack/modules/beastmaster/code/beastmaster_signals.dm b/modular_darkpack/modules/beastmaster/code/beastmaster_signals.dm index 577f59d5487e..169f112244f5 100644 --- a/modular_darkpack/modules/beastmaster/code/beastmaster_signals.dm +++ b/modular_darkpack/modules/beastmaster/code/beastmaster_signals.dm @@ -60,5 +60,5 @@ continue //stop everything we're doing if the beastmaster points at an enemy - its time to attack - minion.ai_controller.CancelActions() + minion.ai_controller.cancel_current_plan() attack_command.on_target_set(H, living_target) diff --git a/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm b/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm index 76584a82afd1..86e13b1e75f1 100644 --- a/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm +++ b/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm @@ -41,7 +41,7 @@ if(!HAS_TRAIT(src, TRAIT_BLOODY_LOVER)) SEND_SIGNAL(src, COMSIG_MASQUERADE_VIOLATION) - if(!do_after(src, 3 SECONDS, target = drunk_from, timed_action_flags = NONE, progress = FALSE)) + if(!do_after(src, 3 SECONDS, target = drunk_from, timed_action_flags = NONE, show_progress = FALSE)) remove_drinking_overlay(drunk_from) if(!(SEND_SIGNAL(drunk_from, COMSIG_MOB_VAMPIRE_SUCKED, drunk_from) & COMPONENT_RESIST_VAMPIRE_KISS)) drunk_from.apply_status_effect(/datum/status_effect/kissed) diff --git a/modular_darkpack/modules/book_sources/code/source_book_datum.dm b/modular_darkpack/modules/book_sources/code/source_book_datum.dm new file mode 100644 index 000000000000..c280d8432b5e --- /dev/null +++ b/modular_darkpack/modules/book_sources/code/source_book_datum.dm @@ -0,0 +1,63 @@ +GLOBAL_LIST_INIT(source_book_priority, list( + SOURCE_OFFICAL = 1, + SOURCE_STORYTELLER_VAULT = 2, + SOURCE_HOMEBREW = 3, +)) + +/datum/source_book + var/name + /// Determiner for how accurate it is to whitewolf published media. + var/offical_status + /// Media does not have or care about page numbers + var/ignore_pages = FALSE + +/datum/source_book/vtm20 + name = "Vampire The Masquerade 20th Anniversary Edition" + offical_status = SOURCE_OFFICAL + +/datum/source_book/hunterhunted2 + name = "The Hunters Hunted II" + offical_status = SOURCE_OFFICAL + +/datum/source_book/ghouls_and_revenants + name = "Ghouls & Revenants" + offical_status = SOURCE_OFFICAL + +/datum/source_book/wta20 + name = "Werewolf The Apocalypse 20th Anniversary Edition" + offical_status = SOURCE_OFFICAL + +/datum/source_book/vampire_wild_west + name = "Vampire The Wild West" + offical_status = SOURCE_STORYTELLER_VAULT + +/datum/source_book/homebrew + name = "Homebrew" + offical_status = SOURCE_HOMEBREW + +/datum/proc/highest_source_offical_status() + var/highest_book + for(var/datum/source_book/book, page_number in ttrpg_sources) + if(!highest_book || GLOB.source_book_priority[book] < highest_book) + highest_book = GLOB.source_book_priority[book] + + return highest_book + +/datum/proc/soure_book_allowed(source_cutoff) + var/main_source = highest_source_offical_status() + if(!main_source) + return TRUE + + if(GLOB.source_book_priority[main_source] > GLOB.source_book_priority[source_cutoff]) + return FALSE + + return TRUE + + +/datum/proc/get_book_sources_readable() + var/books = list() + for(var/datum/source_book/book, page_number in ttrpg_sources) + if(isnum(page_number)) + books += "[book::name]: p. [page_number]" + else + books += "[book::name]: [page_number]" diff --git a/modular_darkpack/modules/cars/code/car.dm b/modular_darkpack/modules/cars/code/car.dm index 0e41f33c4b5d..3a2efa0c4515 100644 --- a/modular_darkpack/modules/cars/code/car.dm +++ b/modular_darkpack/modules/cars/code/car.dm @@ -150,6 +150,11 @@ add_overlay(image(icon = src.icon, icon_state = src.icon_state, pixel_x = -32, pixel_y = -32)) icon_state = "empty" + var/static/list/loc_connections = list( + COMSIG_ATOM_MAGICALLY_UNLOCKED = PROC_REF(on_magic_unlock), + ) + AddElement(/datum/element/connect_loc, loc_connections) + /obj/darkpack_car/Destroy() STOP_PROCESSING(SScarpool, src) QDEL_NULL(engine_sound_loop) @@ -770,5 +775,14 @@ on = FALSE engine_sound_loop.stop() +/// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Unlock and open up when we get knock casted. +/obj/darkpack_car/proc/on_magic_unlock(datum/source, datum/spell, atom/caster) + SIGNAL_HANDLER + + if(locked) + return + playsound(src, 'modular_darkpack/modules/cars/sounds/open.ogg', 50, TRUE) + locked = FALSE + #undef DOAFTER_SOURCE_CAR #undef CAR_TANK_MAX diff --git a/modular_darkpack/modules/clothes/code/hands.dm b/modular_darkpack/modules/clothes/code/hands.dm index f669435d8138..6030b3b8b55c 100644 --- a/modular_darkpack/modules/clothes/code/hands.dm +++ b/modular_darkpack/modules/clothes/code/hands.dm @@ -80,3 +80,36 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE + +/obj/item/clothing/gloves/vampire/brassknuckles + name = "brass knuckles" + desc = "A set of tarnished brass rings fused together to create a cruel weapon for back-alley brawls. Illegal in most places." + icon_state = "brassknuckles" + resistance_flags = FIRE_PROOF + armor_type = /datum/armor/brassknuckles + clothing_traits = list(TRAIT_BRASSKNUCKLES) + +/datum/armor/brassknuckles + acid = 50 + + +/obj/item/clothing/gloves/vampire/brassknuckles/spiked + name = "spiked steel knuckles" + desc = "A set of tarnished steel rings fused together and topped with piercing metal spikes. Illegal in most places." + icon_state = "spikedknuckles" + +/obj/item/clothing/gloves/vampire/brassknuckles/spiked/equipped(mob/living/carbon/human/user, slot) + ..() + if(ishuman(user) && slot == ITEM_SLOT_GLOVES) + var/mob/living/carbon/carbon_owner = user + for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts) + if(istype(limb, /obj/item/bodypart/arm)) + limb.unarmed_sharpness = SHARP_POINTY + +/obj/item/clothing/gloves/vampire/brassknuckles/spiked/dropped(mob/living/carbon/human/user, slot) + ..() + if(user.get_item_by_slot(ITEM_SLOT_GLOVES) == src) + var/mob/living/carbon/carbon_owner = user + for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts) + if(istype(limb, /obj/item/bodypart/arm)) + limb.unarmed_sharpness = initial(limb.unarmed_sharpness) diff --git a/modular_darkpack/modules/clothes/icons/clothing.dmi b/modular_darkpack/modules/clothes/icons/clothing.dmi index 89648f0a1e3d..39e0ea2553e2 100644 Binary files a/modular_darkpack/modules/clothes/icons/clothing.dmi and b/modular_darkpack/modules/clothes/icons/clothing.dmi differ diff --git a/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi b/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi index a3d0dfafb130..7f7e59406cdb 100644 Binary files a/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi and b/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi differ diff --git a/modular_darkpack/modules/clothes/icons/worn.dmi b/modular_darkpack/modules/clothes/icons/worn.dmi index 5367589f75fa..2b4d7cbba8cc 100644 Binary files a/modular_darkpack/modules/clothes/icons/worn.dmi and b/modular_darkpack/modules/clothes/icons/worn.dmi differ diff --git a/modular_darkpack/modules/decor/code/decor.dm b/modular_darkpack/modules/decor/code/decor.dm index 26049ac632f4..11117feae57f 100644 --- a/modular_darkpack/modules/decor/code/decor.dm +++ b/modular_darkpack/modules/decor/code/decor.dm @@ -661,8 +661,7 @@ var/list/myriad_targets = list() for(var/mob/living/target in loc) - if(!IS_DEAD_OR_INCAP(target)) - myriad_targets += target + myriad_targets += target if(length(myriad_targets) < 20) visible_message(span_warning("The markings pulse with a small flash of red light, then fall dark.")) @@ -786,10 +785,23 @@ /obj/structure/fluff/tv name = "\improper TV" - desc = "A slightly battered looking TV. Various infomercials play on a loop, accompanied by a jaunty tune." + desc = "A slightly battered looking TV. It's off" icon = 'modular_darkpack/modules/decor/icons/television.dmi' + icon_state = "tv_off" + density = TRUE + +/obj/structure/fluff/tv/news + desc = "A slightly battered looking TV. Looks like you're not on the news... this time." icon_state = "tv_news" +/obj/structure/fluff/tv/nature + desc = "A slightly battered looking TV. A documentary about a rabbit named 'Lepix'." + icon_state = "tv_nature" + +/obj/structure/fluff/tv/analog + desc = "A slightly battered looking TV. It might be broken." + icon_state = "tv_analog" + /obj/structure/fluff/tv/order name = "order screen" desc = "A slightly battered looking TV. It shows a menu to order from." diff --git a/modular_darkpack/modules/decor/icons/television.dmi b/modular_darkpack/modules/decor/icons/television.dmi index f244e6fbc8ac..50383c21c4ab 100644 Binary files a/modular_darkpack/modules/decor/icons/television.dmi and b/modular_darkpack/modules/decor/icons/television.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/lefthand.dmi b/modular_darkpack/modules/deprecated/icons/lefthand.dmi index 70c8b27714e7..0742a8ca9703 100644 Binary files a/modular_darkpack/modules/deprecated/icons/lefthand.dmi and b/modular_darkpack/modules/deprecated/icons/lefthand.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/righthand.dmi b/modular_darkpack/modules/deprecated/icons/righthand.dmi index 9bbfc5b2ed55..acebd3761e61 100644 Binary files a/modular_darkpack/modules/deprecated/icons/righthand.dmi and b/modular_darkpack/modules/deprecated/icons/righthand.dmi differ diff --git a/modular_darkpack/modules/frenzy/code/frenzy.dm b/modular_darkpack/modules/frenzy/code/frenzy.dm index 5f3e38e8d905..005876dd0c51 100644 --- a/modular_darkpack/modules/frenzy/code/frenzy.dm +++ b/modular_darkpack/modules/frenzy/code/frenzy.dm @@ -4,7 +4,7 @@ /mob/living/proc/enter_frenzy_mode(atom/target, fleeing = FALSE, source = "Unknown cause") if(HAS_TRAIT(src, TRAIT_IN_FRENZY)) return - if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(src)) return add_traits(list(TRAIT_IN_FRENZY, TRAIT_NOSOFTCRIT, TRAIT_ANALGESIA), FRENZY_TRAIT) message_admins("[ADMIN_LOOKUPFLW(src)] has entered frenzy[target ? " targeting [ADMIN_LOOKUPFLW(src)]": ""]. ([source])") @@ -48,6 +48,8 @@ // V20 p.51 if(HAS_TRAIT(roller, TRAIT_DIFFICULT_FRENZY)) . += 2 + if(HAS_TRAIT(roller, TRAIT_UNCONTROLLABLE)) + . = 10 /datum/storyteller_roll/frenzy/kindred/calculate_used_dice(mob/living/roller, bonus) . = ..() @@ -63,7 +65,7 @@ /mob/living/proc/trigger_rotschreck(atom/fire, difficulty = 6, successes = 0) - if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(src)) return if(!get_kindred_splat(src)) return @@ -82,7 +84,7 @@ /mob/living/proc/trigger_kindred_frenzy(atom/target, difficulty = 6, successes = 0, flavor_text = "Something") - if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(src)) return if(!get_kindred_splat(src)) return @@ -107,7 +109,7 @@ /mob/living/proc/trigger_rage_frenzy(atom/target, difficulty = 6, successes = 0) - if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT)) + if(IS_UNCONSCIOUS(src)) return var/datum/splat/werewolf/shifter/shifter_splat = get_shifter_splat(src) if(!shifter_splat) diff --git a/modular_darkpack/modules/jobs/code/anarchs/baron.dm b/modular_darkpack/modules/jobs/code/anarchs/baron.dm index ca815fab1478..2fc867529b19 100644 --- a/modular_darkpack/modules/jobs/code/anarchs/baron.dm +++ b/modular_darkpack/modules/jobs/code/anarchs/baron.dm @@ -32,4 +32,4 @@ gloves = /obj/item/clothing/gloves/vampire/work l_pocket = /obj/item/smartphone/baron r_pocket = /obj/item/vamp/keys/baron - backpack_contents = list(/obj/item/phone_book=1, /obj/item/card/credit=1) + backpack_contents = list(/obj/item/phone_book=1, /obj/item/card/credit=1, /obj/item/clothing/gloves/vampire/brassknuckles/spiked=1) diff --git a/modular_darkpack/modules/jobs/code/giovanni/i_nonni.dm b/modular_darkpack/modules/jobs/code/giovanni/i_nonni.dm new file mode 100644 index 000000000000..f8477743ae4d --- /dev/null +++ b/modular_darkpack/modules/jobs/code/giovanni/i_nonni.dm @@ -0,0 +1,33 @@ +/datum/job/vampire/nonni + title = JOB_I_NONNI + faction = FACTION_GIOVANNI + total_positions = 2 + spawn_positions = 2 + supervisors = "the Family and the Traditions" + config_tag = "NONNI" + outfit = /datum/outfit/job/vampire/nonni + job_flags = CITY_JOB_FLAGS + display_order = 2 + exp_required_type_department = EXP_TYPE_GIOVANNI + departments_list = list( + /datum/job_department/giovanni, + ) + + allowed_splats = list(SPLAT_GHOUL, SPLAT_NONE, SPLAT_KINDRED) + allowed_clans = list(VAMPIRE_CLAN_GIOVANNI) + minimal_generation = 10 + minimum_immortal_age = 200 + required_character_age = 80 + description = "Your long tenure has made you an honorary head of household. Use your wisdom to guide the Family, teach the Traditions and family trades, and advise the Capo." + minimum_masquerade = 0 + +/datum/outfit/job/vampire/nonni + name = "I Nonni" + jobtype = /datum/job/vampire/nonni + glasses = /obj/item/clothing/glasses/vampire/sun + uniform = /obj/item/clothing/under/vampire/suit + suit = /obj/item/clothing/suit/vampire/trench + shoes = /obj/item/clothing/shoes/vampire + l_pocket = /obj/item/smartphone/giovanni_nonni + r_pocket = /obj/item/vamp/keys/giovanni + backpack_contents = list(/obj/item/card/credit=1) diff --git a/modular_darkpack/modules/jobs/code/giovanni/la_famiglia.dm b/modular_darkpack/modules/jobs/code/giovanni/la_famiglia.dm index 50c180c1786c..92fd139c298a 100644 --- a/modular_darkpack/modules/jobs/code/giovanni/la_famiglia.dm +++ b/modular_darkpack/modules/jobs/code/giovanni/la_famiglia.dm @@ -7,7 +7,7 @@ config_tag = "LA_FAMIGLIA" outfit = /datum/outfit/job/vampire/famiglia job_flags = CITY_JOB_FLAGS - display_order = 2 + display_order = 4 exp_required_type_department = EXP_TYPE_GIOVANNI departments_list = list( /datum/job_department/giovanni, diff --git a/modular_darkpack/modules/jobs/code/job_items.dm b/modular_darkpack/modules/jobs/code/job_items.dm index 9213fe9d33aa..f7edd15c812f 100644 --- a/modular_darkpack/modules/jobs/code/job_items.dm +++ b/modular_darkpack/modules/jobs/code/job_items.dm @@ -159,10 +159,19 @@ desc = "When you come into the land that the Lord your God is giving you, you must not learn to imitate the abhorrent practices of those nations. No one shall be found among you who makes a son or daughter pass through fire, or who practices divination, or is a soothsayer, or an augur, or a sorcerer, or one who casts spells, or who consults ghosts or spirits, or who seeks oracles from the dead. For whoever does these things is abhorrent to the Lord; it is because of such abhorrent practices that the Lord your God is driving them out before you (Deuteronomy 18:9-12)." icon = 'modular_darkpack/modules/jobs/icons/id_items.dmi' icon_state = "hunter_badge" + slot_flags = ITEM_SLOT_ID | ITEM_SLOT_NECK | ITEM_SLOT_BELT ONFLOOR_ICON_HELPER('modular_darkpack/modules/jobs/icons/id_onfloors.dmi') worn_icon = 'modular_darkpack/modules/jobs/icons/id_worn.dmi' COOLDOWN_DECLARE(detonation_timer) +/obj/item/card/hunter/silver + name = "silver cross" + icon_state = "hunter_silver" + +/obj/item/card/hunter/gothic + name = "gothic cross" + icon_state = "hunter_gothic" + /obj/item/card/hunter/attack_self(mob/user) . = ..() if(!COOLDOWN_FINISHED(src, detonation_timer)) diff --git a/modular_darkpack/modules/jobs/code/landmarks.dm b/modular_darkpack/modules/jobs/code/landmarks.dm index 4075f7601f2e..32ce880a30f6 100644 --- a/modular_darkpack/modules/jobs/code/landmarks.dm +++ b/modular_darkpack/modules/jobs/code/landmarks.dm @@ -70,6 +70,7 @@ JOB_START_HELPER(law_enforcement/fbi, JOB_FEDERAL_INVESTIGATOR) name = "generic hecata start" JOB_START_HELPER(hecata/capo, JOB_CAPO) +JOB_START_HELPER(hecata/nonni, JOB_I_NONNI) JOB_START_HELPER(hecata/famiglia, JOB_LA_SQUADRA) JOB_START_HELPER(hecata/squadra, JOB_LA_FAMIGLIA) @@ -82,6 +83,7 @@ JOB_START_HELPER(citizen/club_worker, JOB_CLUB_WORKER) JOB_START_HELPER(citizen/janitor, JOB_STREET_JANITOR) JOB_START_HELPER(citizen/priest, JOB_PRIEST) JOB_START_HELPER(citizen/taxi, JOB_TAXI_DRIVER) +JOB_START_HELPER(citizen/red_news, JOB_RED_NEWS_REPORTER) /* Sabbat */ /obj/effect/landmark/start/darkpack/sabbat diff --git a/modular_darkpack/modules/jobs/icons/id_items.dmi b/modular_darkpack/modules/jobs/icons/id_items.dmi index e01590536e47..a40de0132372 100644 Binary files a/modular_darkpack/modules/jobs/icons/id_items.dmi and b/modular_darkpack/modules/jobs/icons/id_items.dmi differ diff --git a/modular_darkpack/modules/jobs/icons/id_onfloors.dmi b/modular_darkpack/modules/jobs/icons/id_onfloors.dmi index 096979083a33..45f559bedffb 100644 Binary files a/modular_darkpack/modules/jobs/icons/id_onfloors.dmi and b/modular_darkpack/modules/jobs/icons/id_onfloors.dmi differ diff --git a/modular_darkpack/modules/jobs/icons/id_worn.dmi b/modular_darkpack/modules/jobs/icons/id_worn.dmi index 06cf5b8660ce..ac9707fb2516 100644 Binary files a/modular_darkpack/modules/jobs/icons/id_worn.dmi and b/modular_darkpack/modules/jobs/icons/id_worn.dmi differ diff --git a/modular_darkpack/modules/jobs/icons/landmarks.dmi b/modular_darkpack/modules/jobs/icons/landmarks.dmi index 70f1a070c120..9f6026a02a6a 100644 Binary files a/modular_darkpack/modules/jobs/icons/landmarks.dmi and b/modular_darkpack/modules/jobs/icons/landmarks.dmi differ diff --git a/modular_darkpack/modules/jumping/code/jumper_component.dm b/modular_darkpack/modules/jumping/code/jumper_component.dm index 3ed0ef927323..a57811e6c82c 100644 --- a/modular_darkpack/modules/jumping/code/jumper_component.dm +++ b/modular_darkpack/modules/jumping/code/jumper_component.dm @@ -38,7 +38,7 @@ if(LAZYACCESS(modifiers, CTRL_CLICK) || LAZYACCESS(modifiers, SHIFT_CLICK) || LAZYACCESS(modifiers, ALT_CLICK)) return - if(jumper.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(jumper)) return if(jumper.pulledby && jumper.pulledby.grab_state != GRAB_PASSIVE) diff --git a/modular_darkpack/modules/loadout/code/categories/pocket_items.dm b/modular_darkpack/modules/loadout/code/categories/pocket_items.dm index 395352b31744..d7afcd2b72f3 100644 --- a/modular_darkpack/modules/loadout/code/categories/pocket_items.dm +++ b/modular_darkpack/modules/loadout/code/categories/pocket_items.dm @@ -34,6 +34,18 @@ name = "Wrist watch" item_path = /obj/item/watch +/datum/loadout_item/pocket_items/gold_cross + name = "Cross (Gold)" + item_path = /obj/item/card/hunter + +/datum/loadout_item/pocket_items/silver_cross + name = "Cross (Silver)" + item_path = /obj/item/card/hunter/silver + +/datum/loadout_item/pocket_items/gothic_cross + name = "Cross (Gothic)" + item_path = /obj/item/card/hunter/gothic + /* /datum/loadout_item/pocket_items/phone name = "Phone" diff --git a/modular_darkpack/modules/masquerade/code/fields/violation_check_aoe.dm b/modular_darkpack/modules/masquerade/code/fields/violation_check_aoe.dm index 25d43b29071c..ca1f4c96490f 100644 --- a/modular_darkpack/modules/masquerade/code/fields/violation_check_aoe.dm +++ b/modular_darkpack/modules/masquerade/code/fields/violation_check_aoe.dm @@ -69,7 +69,7 @@ SIGNAL_HANDLER var/mob/living/host_mob = host - if(host_mob.incapacitated || host_mob.stat >= SOFT_CRIT || host_mob.IsSleeping() || host_mob.IsParalyzed()) + if(host_mob.incapacitated || IS_UNCONSCIOUS_OR_CRIT(host_mob) || host_mob.IsSleeping() || host_mob.IsParalyzed()) return if(HAS_TRAIT(source, TRAIT_OBFUSCATED)) return diff --git a/modular_darkpack/modules/masquerade/code/masquerade_landmark.dm b/modular_darkpack/modules/masquerade/code/masquerade_landmark.dm index 3d47c93ac5b7..716d33387df8 100644 --- a/modular_darkpack/modules/masquerade/code/masquerade_landmark.dm +++ b/modular_darkpack/modules/masquerade/code/masquerade_landmark.dm @@ -1,13 +1,11 @@ /obj/effect/landmark/latejoin_masquerade - name = "JoinLate" + name = "JoinLate Masquerade" /obj/effect/landmark/latejoin_masquerade/Initialize(mapload) - . = ..() - REGISTER_REQUIRED_MAP_ITEM(1, INFINITY) - GLOB.masquerade_latejoin += src -/obj/effect/landmark/latejoin_masquerade/Destroy() - . = ..() + ..() + + GLOB.masquerade_latejoin += loc + return INITIALIZE_HINT_QDEL - GLOB.masquerade_latejoin -= src diff --git a/modular_darkpack/modules/masquerade/code/word_checker.dm b/modular_darkpack/modules/masquerade/code/word_checker.dm index 1b6db1d1ca11..a42b67b4568d 100644 --- a/modular_darkpack/modules/masquerade/code/word_checker.dm +++ b/modular_darkpack/modules/masquerade/code/word_checker.dm @@ -6,9 +6,9 @@ return FALSE var/dist = get_dist(speaker, src) - message_range - if(dist > 0 && dist <= EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) + if(dist > 0 && dist <= EAVESDROP_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) raw_message = stars(raw_message) - if(message_range != INFINITY && dist > EAVESDROP_EXTRA_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) + if(message_range != INFINITY && dist > EAVESDROP_RANGE && !HAS_TRAIT(src, TRAIT_GOOD_HEARING)) return FALSE if(!has_language(message_language)) diff --git a/modular_darkpack/modules/merits_flaws/code/config.dm b/modular_darkpack/modules/merits_flaws/code/config.dm index a65fdfca9719..e834505cd85d 100644 --- a/modular_darkpack/modules/merits_flaws/code/config.dm +++ b/modular_darkpack/modules/merits_flaws/code/config.dm @@ -1,3 +1,7 @@ /datum/config_entry/flag/roleplay_only_merits default = TRUE protection = CONFIG_ENTRY_LOCKED // Config does nothing after initlize. dont trick people into thinking they can edit + +/datum/config_entry/string/ttrpg_accurate_cuttoff_merits + default = SOURCE_HOMEBREW + protection = CONFIG_ENTRY_LOCKED // Config does nothing after initlize. dont trick people into thinking they can edit diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/animal_musk.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/animal_musk.dm index e74c921fbec8..ebb33239ad35 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/animal_musk.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/animal_musk.dm @@ -9,6 +9,7 @@ Outdoors or in situations where you can distance yourself from humans, your odor is not noticeable. Wolves (and lupus-born Garou) take little notice of this Flaw.."} */ + ttrpg_sources = list(/datum/source_book/wta20 = 473) value = -1 mob_trait = TRAIT_ANIMAL_MUSK icon = FA_ICON_SPRAY_CAN_SPARKLES // icon = FA_ICON_BUGS diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/cast_no_reflection.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/cast_no_reflection.dm index 6400eec41ded..48d95bebdab5 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/cast_no_reflection.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/cast_no_reflection.dm @@ -1,7 +1,7 @@ -//Vampire The Masquerade 20th Anniversary Edition, 494 /datum/quirk/darkpack/cast_no_reflection name = "Cast No Reflection" desc = "You actually cast no reflection, just like the vampires of legend. This can have a detrimental effect when trying to pass as a human." + ttrpg_sources = list(/datum/source_book/vtm20 = 494) value = -1 mob_trait = TRAIT_NO_MIRROR_REFLECTION icon = FA_ICON_PERSON_THROUGH_WINDOW diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/derangement.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/derangement.dm index 130acd27b90e..9b65ceea57e1 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/derangement.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/derangement.dm @@ -165,7 +165,7 @@ GLOBAL_LIST_INIT(derangement_phrases,list( /datum/hallucination/your_mother/malk/start() var/mob/living/carbon/human/malk = hallucinator var/age = malk.chronological_age - if(!malk.client || malk.stat >= UNCONSCIOUS) + if(!malk.client || IS_UNCONSCIOUS(malk)) return FALSE var/list/spawn_locs = list() diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/dulled_bite.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/dulled_bite.dm index 2f6c0afa4048..a98c21e8fe84 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/dulled_bite.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/dulled_bite.dm @@ -1,7 +1,7 @@ -// V20 p. 481 /datum/quirk/darkpack/dulled_bite name = "Dulled Bite" desc = "For some reason your fangs never developed fully, or they may not have manifested at all. When feeding, you need to find some other method of making the blood flow. A number of Caitiff and high Generation vampires often manifest this Flaw." + ttrpg_sources = list(/datum/source_book/vtm20 = 481) value = -2 mob_trait = TRAIT_DULLFANGS gain_text = span_notice("Your fangs feel dull.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/one_armed.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/one_armed.dm index 9ab156118298..806ef6057f9e 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/one_armed.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/one_armed.dm @@ -3,6 +3,7 @@ name = "One Arm" desc = "You're missing your an arm. That just ain't right." icon = FA_ICON_HAND + ttrpg_sources = list(/datum/source_book/wta20 = 474) value = -3 gain_text = span_warning("You can't feel your arm!") lose_text = span_notice("Huh? Your arm is back...") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/permanent_fangs.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/permanent_fangs.dm index d4463c3c5acd..41300af654bb 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/permanent_fangs.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/permanent_fangs.dm @@ -2,6 +2,7 @@ /datum/quirk/darkpack/permafangs name = "Permanent Fangs" desc = "Your fangs do not retract, making it impossible for you to hide your true nature. While some mortals may think you've had your teeth filed or are wearing prosthetics, sooner or later you're going to run into someone who knows what you truly are." + ttrpg_sources = list(/datum/source_book/vtm20 = 482) // TTRPG accurate would be -3? But this is also missing the max Appearance lock.. value = -1 mob_trait = TRAIT_PERMAFANGS diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/pierced_veil.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/pierced_veil.dm index c05a8d72e10f..e3223d259ca8 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/pierced_veil.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/pierced_veil.dm @@ -1,9 +1,9 @@ -// W20 p. 484 /datum/quirk/darkpack/pierced_veil name = "Pierced Veil" // A little unsure who to do the logic on the social roll rn. desc = {"Unlike most Garou, your Crinos form does not trigger the Delirium in mortals. This makes you particularly vulnerable to werewolf hunters, who may find it less difficult to pursue you back to your caern, putting the members of your sept in considerable danger."} + ttrpg_sources = list(/datum/source_book/wta20 = 484) value = -3 mob_trait = TRAIT_PIERCED_VEIL icon = FA_ICON_MASKS_THEATER diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/prey_exclusion.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/prey_exclusion.dm index ff408b73e056..f9f0b11f60ac 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/prey_exclusion.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/prey_exclusion.dm @@ -11,6 +11,7 @@ GLOBAL_LIST_INIT(prey_exclusion_choice, list( /datum/quirk/darkpack/prey_exclusion name = "Prey Exclusion" desc = "You refuse to hunt a certain kind of prey. When joining the game, you'll have to select what type of NPC you cant feed from. Ventrue cannot take this flaw." + ttrpg_sources = list(/datum/source_book/vtm20 = 485) value = -1 mob_trait = TRAIT_PREY_EXCLUSION gain_text = span_notice("You feel very picky about what type of person you feed from.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/seizures.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/seizures.dm new file mode 100644 index 000000000000..fc34c8a75be1 --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/seizures.dm @@ -0,0 +1,55 @@ +/datum/quirk/darkpack/seizures + name = "Seizures" + desc = {"When you're under the gun, you lose control of your body. + When you botch an important roll, make a Willpower check (difficulty 8). + Scoring less than three successes makes you writhe uncontrollably."} + icon = FA_ICON_BRAIN + value = -4 // Made up since its not a "real" flaw and is instead listed in the crinos-born section + gain_text = span_warning("You feel less in control of your body...") + lose_text = span_notice("You feel more in control of yourself.") + ttrpg_sources = list(/datum/source_book/wta20 = 75) + COOLDOWN_DECLARE(seizure_cooldown) + +/datum/quirk/darkpack/seizures/add(client/client_source) + RegisterSignal(quirk_holder, COMSIG_LIVING_DICE_ROLLED, PROC_REF(on_dice_rolled)) + +/datum/quirk/darkpack/seizures/remove() + UnregisterSignal(quirk_holder, COMSIG_LIVING_DICE_ROLLED) + +/datum/quirk/darkpack/seizures/proc/on_dice_rolled(mob/living/roller, datum/storyteller_roll/roll_datum, atom/target, output) + SIGNAL_HANDLER + + if(!COOLDOWN_FINISHED(src, seizure_cooldown)) + return + + if(roll_datum.spammy_roll) + return + + if(roll_datum.numerical) + if(output >= 0) + return + else + if(output != ROLL_BOTCH) + return + + var/datum/storyteller_roll/seizures/seizure_roll_datum = new() + var/result = seizure_roll_datum.st_roll(roller) + + if(result == ROLL_SUCCESS) + return + + roller.apply_status_effect(/datum/status_effect/seizure/flaw) + COOLDOWN_START(src, seizure_cooldown, 1 SCENES) + + +/datum/status_effect/seizure/flaw + dur_min = 25 SECONDS + dur_max = 35 SECONDS + + +/datum/storyteller_roll/seizures + bumper_text = "seizures" + applicable_stats = list(STAT_TEMPORARY_WILLPOWER) + difficulty = 8 + successes_needed = 3 + roll_output_type = ROLL_PRIVATE diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/speech_impediment.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/speech_impediment.dm index 97acbb989ea8..5d76dc3c03b3 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/speech_impediment.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/speech_impediment.dm @@ -1,8 +1,8 @@ -//Vampire The Masquerade 20th Anniversary Edition, 485 /datum/quirk/darkpack/speech_impediment name = "Speech Impediment" desc = "You have a stammer that hampers verbal communication." icon = FA_ICON_COMMENT_SLASH + ttrpg_sources = list(/datum/source_book/vtm20 = 485) value = -1 gain_text = span_danger("You start worrying about what you're saying.") lose_text = span_notice("You feel easier about talking again.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/territorial.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/territorial.dm index 95e9cf311895..156e3922a1c1 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/territorial.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/territorial.dm @@ -35,6 +35,7 @@ GLOBAL_LIST_INIT(territorial_type_choices, init_territorial_type_choices()) /datum/quirk/darkpack/territorial name = "Territorial" desc = "You are extremely territorial, and can only feed in one particular area. You react with hostility if another vampire enters your territory without your notice - and if they feed without your permission, violence is sure to break out, as they're taking your food and resources. You are reluctant to leave your territory unless necessary. Your territory is the only area where you may feed." + ttrpg_sources = list(/datum/source_book/vtm20 = 486) value = -2 mob_trait = TRAIT_VAMPIRE_TERRITORIAL gain_text = span_notice("You need to protect your hunting grounds, your herd, your territory.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/the_largest_maw.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/the_largest_maw.dm index 7444d35fe525..bae3020ca00b 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/the_largest_maw.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/the_largest_maw.dm @@ -3,7 +3,7 @@ desc = "Due to the size of your shark-like rows of teeth, you find it hard to communicate verbally. Instead, you speak through pools of spit and sometimes bite your own teeth in the process. This makes you quieter than other Nagaraja, always stuck whispering." icon = FA_ICON_COMMENT value = -2 - mob_trait = TRAIT_SOFTSPOKEN + mob_trait = TRAIT_FORCE_WHISPER gain_text = span_danger("Your teeth shift uncomfortably, taking more room in your mouth - it seems difficult to speak.") lose_text = span_notice("Your teeth seemingly retract, leaving your voice louder than it was before.") allowed_splats = list(SPLAT_KINDRED) diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/uncontrollable.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/uncontrollable.dm new file mode 100644 index 000000000000..7ddc835807e2 --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/uncontrollable.dm @@ -0,0 +1,15 @@ +//Clanbook: Brujah Revised (69) +/datum/quirk/darkpack/uncontrollable + name = "Uncontrollable" + desc = {"Rage and passion constantly war in the soul of a + volatile Brujah. Perhaps you were ill tempered before + the Embrace, or perhaps your Brujah lineage awakened + some latent fury. In any case, even more so than your + clanmates, you are prone to frenzy. Difficulties to + resist frenzy are always 10 for this character. Prepare + for a short, hellish ride."} + value = -5 + mob_trait = TRAIT_UNCONTROLLABLE + icon = FA_ICON_HEART_CRACK + allowed_splats = list(SPLAT_KINDRED) + included_clans = list(VAMPIRE_CLAN_BRUJAH) diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/vengeful.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/vengeful.dm index 4f9c46d4e14c..34564b4b56c7 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/vengeful.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/vengeful.dm @@ -1,6 +1,7 @@ /datum/quirk/darkpack/vengeful name = "Vengeful" desc = "You have a score to settle, incurred either during your mortal days or when you were Embraced. You are obsessed with taking vengeance against a particular individual or group, and it is the overriding priority in every situation. This is a roleplay trait and does not incur any gameplay mechanics - by taking it, you will be expected to roleplay a character obsessed with vengeance." + ttrpg_sources = list(/datum/source_book/vtm20 = 486) value = -1 // its a 2pt flaw in tabletop but lets be honest bro gain_text = span_notice("You become obsessed with vengeance.") lose_text = span_notice("You don't really care about vengeance anymore.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/victim_of_the_masquerade.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/victim_of_the_masquerade.dm index f93e0b974d2e..d9f84919a22c 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/victim_of_the_masquerade.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/victim_of_the_masquerade.dm @@ -1,6 +1,7 @@ /datum/quirk/darkpack/victim_of_the_masquerade name = "Victim of the Masquerade" desc = "The Camarilla's propaganda machine did a good job with you. Even after your character's Embrace, they refused to believe they are a vampire. They remain convinced there must be some logical explanation for their condition, and spend as much time as possible justifying it. You must make a willpower roll whenever feeding and each time you draw a bloodpoint from your victim. When you fail, your character faints for five seconds while also losing a point in their Humanity. You are expected to roleplay this flaw - such as insisting on eating regular food and not feeding." + ttrpg_sources = list(/datum/source_book/vtm20 = 486) value = -2 mob_trait = TRAIT_VICTIM_OF_THE_MASQUERADE gain_text = span_notice("Pff, I'm not a vamprie. Those don't exist.") diff --git a/modular_darkpack/modules/merits_flaws/code/negative_quirks/weak_willed.dm b/modular_darkpack/modules/merits_flaws/code/negative_quirks/weak_willed.dm index bc2c12965d19..babf37115eed 100644 --- a/modular_darkpack/modules/merits_flaws/code/negative_quirks/weak_willed.dm +++ b/modular_darkpack/modules/merits_flaws/code/negative_quirks/weak_willed.dm @@ -1,4 +1,3 @@ -// W20 p. 478, V20 p. 486 /datum/quirk/darkpack/weak_willed name = "Weak Willed" desc = {"You have little resistance to attempts to dominate or intimidate you. @@ -10,6 +9,10 @@ Your difficulties to resist Social Talents such as Intimidation or Leadership, as well as mind-altering spells or magic, are increased by two. Your Willpower may never rise above 4"} */ + ttrpg_sources = list( + /datum/source_book/vtm20 = 486, + /datum/source_book/wta20 = 478, + ) value = -1 mob_trait = TRAIT_WEAK_WILLED icon = FA_ICON_BRAIN diff --git a/modular_darkpack/modules/merits_flaws/code/neutral_quirks/illegal_identity.dm b/modular_darkpack/modules/merits_flaws/code/neutral_quirks/illegal_identity.dm index e798c18f029b..58ec488b337c 100644 --- a/modular_darkpack/modules/merits_flaws/code/neutral_quirks/illegal_identity.dm +++ b/modular_darkpack/modules/merits_flaws/code/neutral_quirks/illegal_identity.dm @@ -2,6 +2,7 @@ /datum/quirk/darkpack/illegal_identity name = "Illegal Identity" desc = "Illegal immigrant? Died legally? Born a wolf? The cops aren't happy." + ttrpg_sources = list(/datum/source_book/homebrew = WE_MADE_IT_UP) value = 0 quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_HIDE_FROM_SCAN icon = FA_ICON_PERSON_CIRCLE_QUESTION diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/beserker.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/beserker.dm index bd74f3d98208..bfd1804e86f0 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/beserker.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/beserker.dm @@ -1,10 +1,10 @@ -// W20 p. 476 /datum/quirk/darkpack/beserker name = "Berserker" desc = {"You have uncanny control over your inner anger, and can use your Rage as most Garou cannot. You can enter a berserk frenzy at will, ignoring your wound penalties. You still suffer the consequences of any actions committed in the throes of frenzy. When circumstances might cause you to frenzy, you must make a standard roll to see if you do so or not."} + ttrpg_sources = list(/datum/source_book/wta20 = 476) value = 2 icon = FA_ICON_ANGRY allowed_splats = SPLAT_SHIFTERS diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/blush_of_health_quirk.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/blush_of_health_quirk.dm index c0f8c98e1765..fc3cd93d7d91 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/blush_of_health_quirk.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/blush_of_health_quirk.dm @@ -1,6 +1,7 @@ /datum/quirk/darkpack/blush_of_health name = "Blush of Health" desc = "Some Kindred are capable of maintaining the illusion of life more convincingly than their peers. With minimal effort, you can appear flushed, warm, and breathing, making it significantly harder for others to identify you as one of the Undead. While active, you seem more alive than before." + ttrpg_sources = list(/datum/source_book/vtm20 = 480) value = 1 mob_trait = TRAIT_BLUSH_OF_HEALTH gain_text = span_notice("A faint warmth spreads across your skin as the blush of health settles over you.") diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/calm_heart.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/calm_heart.dm index 3fc01b8e447f..ae91ece210df 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/calm_heart.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/calm_heart.dm @@ -3,6 +3,7 @@ desc = {"You are naturally calm and do not easily fly off the handle. You receive two extra dice when attempting to resist a frenzy. Brujah may not take this Merit."} + ttrpg_sources = list(/datum/source_book/vtm20 = 485) value = 3 mob_trait = TRAIT_CALM_HEART icon = FA_ICON_HEART diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/deceptive_aura.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/deceptive_aura.dm index 58ff2d253c8d..31234e6a5842 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/deceptive_aura.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/deceptive_aura.dm @@ -1,7 +1,7 @@ -// V20 p. 493 /datum/quirk/darkpack/deceptive_aura name = "Deceptive Aura" desc = {"Your aura is unnaturally bright and colorful for a vampire. You register as a mortal on all attempts to read your aura."} + ttrpg_sources = list(/datum/source_book/vtm20 = 493) value = 1 mob_trait = TRAIT_DECEPTIVE_AURA icon = FA_ICON_TEMPERATURE_HIGH diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/eat_food_quirk.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/eat_food_quirk.dm index d6733ff44ca0..97c295be8d8d 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/eat_food_quirk.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/eat_food_quirk.dm @@ -1,6 +1,7 @@ /datum/quirk/darkpack/eat_food name = "Eat Food" desc = "Unlike most of the Undead, you retain the ability to eat and digest food normally, a semblance of your mortal life. While you gain no nourishment from it, you can consume food without the usual revulsion Kindred experience. Be warned: what goes down must come up, eventually." + ttrpg_sources = list(/datum/source_book/vtm20 = 480) value = 1 mob_trait = TRAIT_EAT_FOOD gain_text = span_notice("Your stomach stirs as you feel the organ come to life. You can now eat food.") diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/efficient_digestion.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/efficient_digestion.dm index 9915decd2c99..23ed52a5bb0b 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/efficient_digestion.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/efficient_digestion.dm @@ -1,6 +1,7 @@ /datum/quirk/darkpack/efficient_digestion name = "Efficient Digestion" desc = "You are able to draw more than the usual amount of nourishment from blood. When feeding, you gain an additional point to your blood pool for every two points of blood you consume." + ttrpg_sources = list(/datum/source_book/vtm20 = 480) value = 3 mob_trait = TRAIT_EFFICIENT_DIGESTION gain_text = span_notice("You feel as if you'll be satiated with ease.") diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/fair_glabro.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/fair_glabro.dm index 508eef62a44d..3ee14547e0f4 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/fair_glabro.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/fair_glabro.dm @@ -1,7 +1,7 @@ -// W20 p. 472 /datum/quirk/darkpack/fair_glabro name = "Fair Glabro" desc = "You have a Glabro form that can pass for human, though it's still larger than normal people. You have no penalties to Social Attributes in Glabro form." + ttrpg_sources = list(/datum/source_book/wta20 = 472) value = 2 mob_trait = TRAIT_FAIR_GLABRO icon = FA_ICON_PERSON_HALF_DRESS diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/hidden_diablerie.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/hidden_diablerie.dm index 1f42e7d71d3c..d518b6d24289 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/hidden_diablerie.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/hidden_diablerie.dm @@ -1,7 +1,7 @@ -//Vampire the Masquerade 20th Anniversary Edition, 493 /datum/quirk/darkpack/hidden_diablerie name = "Hidden Diablerie" desc = "The tell-tale black streaks of diablerie do not manifest in your aura." + ttrpg_sources = list(/datum/source_book/vtm20 = 493) value = 3 mob_trait = TRAIT_HIDDEN_DIABLERIE allowed_splats = list(SPLAT_KINDRED) diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/metamorph.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/metamorph.dm index 06b96e22c2c8..f5c0b3fd1716 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/metamorph.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/metamorph.dm @@ -1,10 +1,10 @@ -// W20 p. 473 /datum/quirk/darkpack/metamorph name = "Metamorph" desc = {"Shapechanging for you is as easy as breathing. You do not need to roll to change forms, nor is it necessary to spend a Rage point for an instantaneous shift. You make your changes as if you scored five successes on your roll to shift forms. If you lose consciousness from wounds or for some other reason, you may roll Wits + Primal Urge (difficulty 8) to choose which form you assume rather than reverting to breed form."} + ttrpg_sources = list(/datum/source_book/wta20 = 473) value = 7 mob_trait = TRAIT_METAMORPH // I love the greg sam. sa diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/pale_aura.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/pale_aura.dm index ab605f190a8a..f371a817a59e 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/pale_aura.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/pale_aura.dm @@ -1,4 +1,3 @@ -// Ghouls & Revenants p. 137, HH p. 43 /datum/quirk/darkpack/pale_aura name = "Pale Aura" desc = {"Any color your character's aura takes has a pale cast to it, as though he was a vampire."} @@ -6,6 +5,10 @@ Unless her player gains five chapter two or more successes on an Aura Perception roll, any vampire discerns your character's aura as one belonging to the Kindred"} */ + ttrpg_sources = list( + /datum/source_book/ghouls_and_revenants = 137, + /datum/source_book/hunterhunted2 = 43, + ) value = 2 mob_trait = TRAIT_PALE_AURA icon = FA_ICON_TEMPERATURE_EMPTY diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/time_sense.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/time_sense.dm index fbdf2633b20a..c40dafb00c1c 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/time_sense.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/time_sense.dm @@ -1,8 +1,8 @@ -// W20 p. 475 /datum/quirk/darkpack/time_sense name = "Time Sense" desc = {"You have an innate sense of time and are able to estimate the passage of time accurately without using a watch or other mechanical device, even after long periods of unconsciousness. This allows you to know (among other things) what phase the moon is in."} + ttrpg_sources = list(/datum/source_book/wta20 = 475) value = 1 mob_trait = TRAIT_TIME_SENSE icon = FA_ICON_STOPWATCH diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/touch_of_the_wyld.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/touch_of_the_wyld.dm index fc856b52a887..ae3a8523759c 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/touch_of_the_wyld.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/touch_of_the_wyld.dm @@ -2,6 +2,7 @@ /datum/quirk/darkpack/touch_of_the_wyld name = "Touch of the Wyld" desc = "Unlike nearly all other undead, you have no obvious Wyrmtaint. You are invisible to those who have the ability to naturally sense the Wyrm." + ttrpg_sources = list(/datum/source_book/vampire_wild_west = 114) value = 5 mob_trait = TRAIT_HIDDEN_WYRMTAINT icon = FA_ICON_TEMPERATURE_HIGH diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/unbondable.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/unbondable.dm index 2d2faa1b2436..c84c4e169813 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/unbondable.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/unbondable.dm @@ -1,7 +1,7 @@ -// V20 p. 494 /datum/quirk/darkpack/unbondable name = "Unbondable" desc = "You are immune to being blood bound. Tremere cannot take this Merit." + ttrpg_sources = list(/datum/source_book/vtm20 = 494) value = 5 mob_trait = TRAIT_UNBONDABLE icon = FA_ICON_CHAIN_BROKEN diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/untamable.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/untamable.dm index 913de0114813..18dc00b4dd36 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/untamable.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/untamable.dm @@ -5,6 +5,7 @@ You are immune to vampiric Domination (but not emotional manipulations via Presence)"} // Change this line when we add these gifts. // You are immune to vampiric Domination (but not emotional manipulations via Presence) and these Gifts will not work on you: Roll Over, Obedience, and Mastery."} + ttrpg_sources = list(/datum/source_book/wta20 = 476) value = 5 mob_trait = TRAIT_MERIT_UNTAMABLE icon = FA_ICON_SHIELD_DOG diff --git a/modular_darkpack/modules/merits_flaws/code/positive_quirks/wolf_sight.dm b/modular_darkpack/modules/merits_flaws/code/positive_quirks/wolf_sight.dm index 7456724a5caa..902af01fe2e8 100644 --- a/modular_darkpack/modules/merits_flaws/code/positive_quirks/wolf_sight.dm +++ b/modular_darkpack/modules/merits_flaws/code/positive_quirks/wolf_sight.dm @@ -5,6 +5,7 @@ Your night vision, however, far surpasses human nocturnal vision."} // Perception is not real yet. // You also notice movement more readily. You gain an extra die to all visually-based Perception rolls that involve movement or take place at night."} + ttrpg_sources = list(/datum/source_book/wta20 = 472) value = 1 mob_trait = TRAIT_TRUE_NIGHT_VISION icon = FA_ICON_DOG diff --git a/modular_darkpack/modules/npc/code/human/__npc.dm b/modular_darkpack/modules/npc/code/human/__npc.dm index d537417ff6e2..7a5625e2ef01 100644 --- a/modular_darkpack/modules/npc/code/human/__npc.dm +++ b/modular_darkpack/modules/npc/code/human/__npc.dm @@ -70,7 +70,6 @@ var/lifespan = 0 //How many cycles. He'll be deleted if over than a ten thousand var/old_movement = FALSE - var/max_stat = 2 var/list/spotted_bodies = list() @@ -133,6 +132,7 @@ drop_on_death_list = null GLOB.npc_list -= src GLOB.alive_npc_list -= src + SShumannpcpool.currentrun -= src SShumannpcpool.try_repopulate() return ..() diff --git a/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm b/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm index 44b18ccbabbf..59f0c72a5feb 100644 --- a/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm +++ b/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm @@ -1,6 +1,10 @@ SUBSYSTEM_DEF(humannpcpool) name = "Human NPC Pool" - ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND +#ifndef UNIT_TESTS + ss_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND +#else + ss_flags = SS_NO_INIT | SS_NO_FIRE +#endif priority = FIRE_PRIORITY_NPC runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME wait = 0.3 SECONDS @@ -23,28 +27,20 @@ SUBSYSTEM_DEF(humannpcpool) return ..() /datum/controller/subsystem/humannpcpool/fire(resumed = FALSE) - - if (!resumed) - var/list/activelist = GLOB.npc_list - src.currentrun = activelist.Copy() + if(!resumed) + src.currentrun = GLOB.npc_list.Copy() //cache for sanic speed (lists are references anyways) var/list/currentrun = src.currentrun - - while(currentrun.len) - var/mob/living/carbon/human/npc/NPC = currentrun[currentrun.len] + while(length(currentrun)) + var/mob/living/carbon/human/npc/NPC = currentrun[length(currentrun)] --currentrun.len - - if (QDELETED(NPC)) - GLOB.npc_list -= NPC - stack_trace("Found a null in npc_list [NPC.type]!") - continue - - if (MC_TICK_CHECK) + NPC?.handle_automated_movement() + if(MC_TICK_CHECK) return - NPC.handle_automated_movement() /datum/controller/subsystem/humannpcpool/proc/try_repopulate() +#ifndef UNIT_TESTS if (!length(GLOB.npc_spawn_points)) return @@ -58,3 +54,4 @@ SUBSYSTEM_DEF(humannpcpool) /mob/living/carbon/human/npc/business \ ) new creating_npc(get_turf(chosen_spawn_point)) +#endif diff --git a/modular_darkpack/modules/npc/code/human/npc_movement.dm b/modular_darkpack/modules/npc/code/human/npc_movement.dm index 936c7ee304ed..dc178d680b58 100644 --- a/modular_darkpack/modules/npc/code/human/npc_movement.dm +++ b/modular_darkpack/modules/npc/code/human/npc_movement.dm @@ -297,7 +297,7 @@ GLOB.move_manager.move_to(src, danger_source, 1, cached_multiplicative_slowdown) // Deaggro if the danger source has been beaten up - if (danger_source.stat > UNCONSCIOUS) + if (IS_UNCONSCIOUS_OR_CRIT(danger_source)) end_combat() // Deaggro if 30 second have passed since being antagonised diff --git a/modular_darkpack/modules/npc/code/human/npc_types/bandit.dm b/modular_darkpack/modules/npc/code/human/npc_types/bandit.dm index 13a32c525d40..e5f38384af0a 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/bandit.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/bandit.dm @@ -1,5 +1,4 @@ /mob/living/carbon/human/npc/bandit - max_stat = HARD_CRIT my_backup_weapon_type = /obj/item/knife/vamp /mob/living/carbon/human/npc/bandit/Initialize(mapload) diff --git a/modular_darkpack/modules/npc/code/human/npc_types/club.dm b/modular_darkpack/modules/npc/code/human/npc_types/club.dm index 49743c088214..f73015c134ea 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/club.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/club.dm @@ -4,7 +4,7 @@ /mob/living/carbon/human/npc/walkby/club/Life() . = ..() - if (!staying || stat >= UNCONSCIOUS) + if (!staying || IS_UNCONSCIOUS_OR_CRIT(src)) return if (!prob(5)) return diff --git a/modular_darkpack/modules/npc/code/human/npc_types/endron/endron_npcs.dm b/modular_darkpack/modules/npc/code/human/npc_types/endron/endron_npcs.dm index 21d1b155bae7..0d6c5370a72c 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/endron/endron_npcs.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/endron/endron_npcs.dm @@ -1,6 +1,5 @@ /mob/living/carbon/human/npc/endronsecurity staying = TRUE - max_stat = 4 my_weapon_type = /obj/item/gun/ballistic/automatic/darkpack/mp5 my_backup_weapon_type = /obj/item/melee/baton/vamp @@ -11,7 +10,6 @@ /mob/living/carbon/human/npc/endronlabsecurity staying = TRUE - max_stat = 4 my_weapon_type = /obj/item/gun/ballistic/automatic/darkpack/mp5 my_backup_weapon_type = /obj/item/melee/baton/vamp @@ -22,7 +20,6 @@ /mob/living/carbon/human/npc/endronexecsecurity staying = TRUE - max_stat = 4 my_weapon_type = /obj/item/gun/ballistic/automatic/pistol/darkpack/deagle my_backup_weapon_type = /obj/item/melee/baton/vamp diff --git a/modular_darkpack/modules/npc/code/human/npc_types/guard.dm b/modular_darkpack/modules/npc/code/human/npc_types/guard.dm index 02735da8cb25..20f85372a95d 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/guard.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/guard.dm @@ -1,7 +1,6 @@ /mob/living/carbon/human/npc/guard staying = TRUE aggressive = TRUE - max_stat = DEAD my_weapon_type = /obj/item/gun/ballistic/automatic/pistol/darkpack/m1911 my_backup_weapon_type = /obj/item/melee/baton/vamp diff --git a/modular_darkpack/modules/npc/code/human/npc_types/police.dm b/modular_darkpack/modules/npc/code/human/npc_types/police.dm index de9e1df8ce43..8ad14d330b62 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/police.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/police.dm @@ -1,6 +1,5 @@ /mob/living/carbon/human/npc/police aggressive = TRUE - max_stat = DEAD my_backup_weapon_type = /obj/item/melee/baton/vamp /mob/living/carbon/human/npc/police/Initialize(mapload) @@ -11,7 +10,7 @@ /mob/living/carbon/human/npc/police/Life() . = ..() - if (stat >= SOFT_CRIT) + if (IS_UNCONSCIOUS_OR_CRIT(src)) return if (!prob(10)) return @@ -29,7 +28,6 @@ /mob/living/carbon/human/npc/police/static // fights_anyway = TRUE staying = TRUE - max_stat = UNCONSCIOUS my_backup_weapon_type = /obj/item/melee/baton/vamp /* diff --git a/modular_darkpack/modules/npc/code/human/npc_types/stripper.dm b/modular_darkpack/modules/npc/code/human/npc_types/stripper.dm index 84c314572b70..b4f889902358 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/stripper.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/stripper.dm @@ -15,7 +15,7 @@ /mob/living/carbon/human/npc/stripper/Life() . = ..() - if (stat >= UNCONSCIOUS) + if (IS_UNCONSCIOUS(src)) return if (!prob(20)) return diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.json b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.json new file mode 100644 index 000000000000..f693eaaf49e2 --- /dev/null +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.json @@ -0,0 +1,85 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/abyss_tentacle", + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY", + "key": "BB_ABYSS_TENTACLE_MODE", + "value": "\"Passive\"" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/tentacle_release_target", + "vars": { + "time_between_perform": "0.5 SECONDS" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_true", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_ABYSS_TENTACLE_GRABBED" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "key": "BB_ABYSS_TENTACLE_MODE", + "value": "\"Aggressive\"" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/tentacle_crush_victim" + } + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_equals", + "vars": { + "observer_abort": "BT_ABORT_NONE", + "invert": true, + "key": "BB_ABYSS_TENTACLE_MODE", + "value": "\"Passive\"" + }, + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/tentacle_grab_target" + } + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/update_combat_targets", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "vision_range": 2, + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] +} diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm index 615ffa65ad9e..8439961de4c3 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm @@ -25,115 +25,97 @@ GLOBAL_LIST_EMPTY(global_tentacle_grabs) mobility_flags = NONE move_resist = MOVE_FORCE_EXTREMELY_STRONG - environment_smash = ENVIRONMENT_SMASH_NONE + ai_controller = /datum/ai_controller/basic_controller/abyss_tentacle + var/mob/living/carbon/human/owner - var/mob/living/grabbed_mob = null var/list/recently_released = list() - var/aggro_mode = "Aggressive" COOLDOWN_DECLARE(grab_cooldown) COOLDOWN_DECLARE(damage_cooldown) - ai_controller = /datum/ai_controller/basic_controller/abyss_tentacle /datum/ai_controller/basic_controller/abyss_tentacle blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/abyss_tentacle, ) ai_movement = /datum/ai_movement/complete_stop - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/tentacle_grab_and_crush, - ) -/datum/ai_planning_subtree/tentacle_grab_and_crush + behavior_tree_json = "modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.bt.json" -/datum/ai_planning_subtree/tentacle_grab_and_crush/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/basic/abyss_tentacle/tentacle = controller.pawn - if(!istype(tentacle)) - return - if(tentacle.aggro_mode == "Passive") - if(tentacle.grabbed_mob) - tentacle.release_grabbed_mob() - return +/datum/targeting_strategy/abyss_tentacle + +/datum/targeting_strategy/abyss_tentacle/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) + var/mob/living/basic/abyss_tentacle/tentacle = astype(living_mob) + var/mob/living/target = astype(the_target) + if(!tentacle || !target) + return FALSE + //dont attack our owner, dead things, other tentacles, things being grabbed by tentacles, or things recently released + if(target == tentacle.owner) + return FALSE + if(target.stat == DEAD) + return FALSE + if(istype(target, /mob/living/basic/abyss_tentacle)) + return FALSE + if(target in GLOB.global_tentacle_grabs) + return FALSE + if(target in tentacle.recently_released) + return FALSE - if(tentacle.grabbed_mob) - if(tentacle.aggro_mode == "Aggressive") - controller.queue_behavior(/datum/ai_behavior/tentacle_crush_victim) - return SUBTREE_RETURN_FINISH_PLANNING + return ..() - var/mob/living/target = find_valid_grab_target(tentacle) - if(target) - controller.queue_behavior(/datum/ai_behavior/tentacle_grab_target, target) - return SUBTREE_RETURN_FINISH_PLANNING - return +/datum/bt_node/ai_behavior/tentacle_grab_target + time_between_perform = 2 SECONDS +/datum/bt_node/ai_behavior/tentacle_grab_target/perform(seconds_per_tick, datum/ai_controller/controller) + . = ..() + var/mob/living/basic/abyss_tentacle/tentacle = astype(controller.pawn) + var/mob/living/target = astype(controller.blackboard[BB_CURRENT_TARGET]) + if(!tentacle) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + if(!target) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED -/datum/ai_planning_subtree/tentacle_grab_and_crush/proc/find_valid_grab_target(mob/living/basic/abyss_tentacle/tentacle) - for(var/mob/living/potential_target in oview(2, tentacle)) - //dont attack our owner, dead things, other tentacles, things being grabbed by tentacles, or things recently released - if(potential_target == tentacle.owner) - continue - if(potential_target.stat == DEAD) - continue - if(istype(potential_target, /mob/living/basic/abyss_tentacle)) - continue - if(potential_target in GLOB.global_tentacle_grabs) - continue - if(potential_target in tentacle.recently_released) - continue + tentacle.grab_mob(target) - return potential_target - return null + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/tentacle_grab_target - action_cooldown = 2 SECONDS -/datum/ai_behavior/tentacle_grab_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/mob/living/target = target_key - if(!istype(target)) - return FALSE - return TRUE +/datum/bt_node/ai_behavior/tentacle_crush_victim + time_between_perform = 5 SECONDS -/datum/ai_behavior/tentacle_grab_target/perform(seconds_per_tick, datum/ai_controller/controller, mob/living/target) +/datum/bt_node/ai_behavior/tentacle_crush_victim/perform(seconds_per_tick, datum/ai_controller/controller) . = ..() - var/mob/living/basic/abyss_tentacle/tentacle = controller.pawn - - if(!istype(tentacle)) + var/mob/living/basic/abyss_tentacle/tentacle = astype(controller.pawn) + var/mob/living/grabbed = astype(controller.blackboard[BB_ABYSS_TENTACLE_GRABBED]) + if(!tentacle || !grabbed) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - tentacle.grab_mob(target) + grabbed.apply_damage(40, BRUTE) + to_chat(grabbed, span_danger("The tentacle tightens its grip, crushing you!")) + playsound(tentacle, 'sound/mobs/non-humanoids/venus_trap/venus_trap_hurt.ogg', 50, FALSE) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/tentacle_crush_victim - action_cooldown = 5 SECONDS -/datum/ai_behavior/tentacle_crush_victim/perform(seconds_per_tick, datum/ai_controller/controller) - . = ..() - var/mob/living/basic/abyss_tentacle/tentacle = controller.pawn +/datum/bt_node/ai_behavior/tentacle_release_target - if(!istype(tentacle) || !tentacle.grabbed_mob) +/datum/bt_node/ai_behavior/tentacle_release_target/perform(seconds_per_tick, datum/ai_controller/controller) + . = ..() + var/mob/living/basic/abyss_tentacle/tentacle = astype(controller.pawn) + var/mob/living/grabbed = astype(controller.blackboard[BB_ABYSS_TENTACLE_GRABBED]) + if(!tentacle || !grabbed) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - tentacle.grabbed_mob.apply_damage(40, BRUTE) - to_chat(tentacle.grabbed_mob, span_danger("The tentacle tightens its grip, crushing you!")) - playsound(tentacle, 'sound/mobs/non-humanoids/venus_trap/venus_trap_hurt.ogg', 50, FALSE) - + tentacle.release_grabbed_mob() return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + /mob/living/basic/abyss_tentacle/Initialize(mapload, mob/living/summoner) . = ..() if(summoner) owner = summoner - if(owner) - var/datum/splat/vampire/vampire = get_splat_with_discipline(owner) - var/datum/discipline_power/obtenebration/arms_of_the_abyss/abyss_power = vampire?.get_discipline_power(/datum/discipline_power/obtenebration/arms_of_the_abyss) - if(abyss_power) - aggro_mode = abyss_power.aggro_mode /mob/living/basic/abyss_tentacle/Destroy(force) if(owner) @@ -141,8 +123,7 @@ GLOBAL_LIST_EMPTY(global_tentacle_grabs) var/datum/discipline_power/obtenebration/arms_of_the_abyss/abyss_power = vampire?.get_discipline_power(/datum/discipline_power/obtenebration/arms_of_the_abyss) if(abyss_power) abyss_power.active_tentacles -= src - if(grabbed_mob) - release_grabbed_mob() + release_grabbed_mob() . = ..() @@ -152,7 +133,7 @@ GLOBAL_LIST_EMPTY(global_tentacle_grabs) return if(target in GLOB.global_tentacle_grabs) return - if(grabbed_mob) + if(ai_controller?.blackboard[BB_ABYSS_TENTACLE_GRABBED]) return if(target.client) to_chat(target, span_userdanger("A shadowy tentacle grabs you!")) @@ -163,24 +144,24 @@ GLOBAL_LIST_EMPTY(global_tentacle_grabs) target.forceMove(get_turf(src)) target.set_tentacle_grab(src) - if(aggro_mode == "Control") + if(ai_controller?.blackboard[BB_ABYSS_TENTACLE_MODE] == ABYSS_TENTACLE_MODE_CONTROL) target.mobility_flags &= ~(MOBILITY_STAND | MOBILITY_MOVE) target.set_resting(TRUE, TRUE, TRUE) to_chat(target, span_userdanger("The tentacle forces you to the ground!")) - grabbed_mob = target + ai_controller?.set_blackboard_key(BB_ABYSS_TENTACLE_GRABBED, target) GLOB.global_tentacle_grabs += target RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_grabbed_mob_move)) /mob/living/basic/abyss_tentacle/proc/release_mob(mob/living/target, add_cooldown = TRUE) - if(target == grabbed_mob) - grabbed_mob = null + if(target == ai_controller?.blackboard[BB_ABYSS_TENTACLE_GRABBED]) + ai_controller?.clear_blackboard_key(BB_ABYSS_TENTACLE_GRABBED) GLOB.global_tentacle_grabs -= target target.Stun(0) target.clear_tentacle_grab() - if(aggro_mode == "Control") + if(ai_controller?.blackboard[BB_ABYSS_TENTACLE_MODE] == ABYSS_TENTACLE_MODE_CONTROL) target.mobility_flags |= (MOBILITY_STAND | MOBILITY_MOVE) target.set_resting(FALSE, TRUE, TRUE) @@ -195,6 +176,7 @@ GLOBAL_LIST_EMPTY(global_tentacle_grabs) recently_released -= target /mob/living/basic/abyss_tentacle/proc/release_grabbed_mob() + var/mob/living/grabbed_mob = ai_controller?.blackboard[BB_ABYSS_TENTACLE_GRABBED] if(grabbed_mob) release_mob(grabbed_mob, FALSE) diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/graveyard_zombie.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/graveyard_zombie.dm index a9c0e77b3dad..e913b194bfbe 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/graveyard_zombie.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/graveyard_zombie.dm @@ -79,28 +79,29 @@ // need a custom targeting strategy so they don't kill other zombies /datum/targeting_strategy/basic/zombie_darkpack -/datum/targeting_strategy/basic/zombie_darkpack/can_attack(mob/living/basic/basic_mob, atom/target, vision_range) - if(istype(target, /mob/living/basic/zombie/darkpack)) +/datum/targeting_strategy/basic/zombie_darkpack/is_valid_target(mob/living/living_mob, atom/the_target, vision_range, datum/ai_controller/controller = null) + if(istype(the_target, /mob/living/basic/zombie/darkpack)) return FALSE // don't break down the fence - just try to break the gate - if(istype(target, /obj/structure/vampfence/rich)) + if(istype(the_target, /obj/structure/vampfence/rich)) return FALSE - if(istype(target, /obj/structure/vampgate)) + if(istype(the_target, /obj/structure/vampgate)) return TRUE // use the parent class for everything else return ..() +/* // planning subtree specifically for finding and targeting vampire gates -/datum/ai_planning_subtree/find_and_attack_vampgate +/datum/bt_node/subtree/find_and_attack_vampgate var/scan_range = 9 -/datum/ai_planning_subtree/find_and_attack_vampgate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) +/datum/bt_node/subtree/find_and_attack_vampgate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/zombie = controller.pawn - var/atom/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/atom/current_target = controller.blackboard[BB_CURRENT_TARGET] // if we're attacking a gate, use the targeting strategy to find nearby living mobs. if(istype(current_target, /obj/structure/vampgate)) @@ -109,7 +110,7 @@ for(var/mob/living/potential_target in oview(controller.blackboard[BB_VISION_RANGE], zombie)) if(targeter.can_attack(zombie, potential_target, controller.blackboard[BB_VISION_RANGE])) // if we can attack the potential target clear the gate from the list and attack the nearby living thing. - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + controller.clear_blackboard_key(BB_CURRENT_TARGET) return // if we already have a living current target don't run this @@ -117,16 +118,17 @@ return // if we currently have a mob target from the blackboard then don't run this - if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + if(controller.blackboard_key_exists(BB_CURRENT_TARGET)) return // is there a nearby vampgate? for(var/obj/structure/vampgate/gate in oview(scan_range, zombie)) if(!gate.broken) // theres one, kill it - controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, gate) - controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) + controller.set_blackboard_key(BB_CURRENT_TARGET, gate) + controller.clear_blackboard_key(BB_CURRENT_TARGET_HIDING_LOCATION) return + */ // AI controller for darkpack zombies /datum/ai_controller/basic_controller/zombie/darkpack @@ -136,11 +138,5 @@ BB_VISION_RANGE = 9, ) ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/find_and_attack_vampgate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) + behavior_tree_json = "code/datums/ai/basic_mobs/simple_hostile.bt.json" diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm index 40b7408f828b..8acfbc5f6d38 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm @@ -26,7 +26,7 @@ pixel_w = -8 bloodpool = 10 maxbloodpool = 10 - ai_controller = /datum/ai_controller/basic_controller/crinos_beast + ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile var/clothing_type = 0 // Set to -1 for random clothing faction = list(FACTION_HOSTILE) @@ -41,18 +41,3 @@ . = ..() AddElement(/datum/element/ai_retaliate) ADD_TRAIT(src, TRAIT_FRENETIC_AURA, INNATE_TRAIT) - -///nothing unique, just retaliation. -/datum/ai_controller/basic_controller/crinos_beast - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - ) diff --git a/modular_darkpack/modules/phones/code/_phone.dm b/modular_darkpack/modules/phones/code/_phone.dm index 1374c2c3dde2..324235d8ba13 100644 --- a/modular_darkpack/modules/phones/code/_phone.dm +++ b/modular_darkpack/modules/phones/code/_phone.dm @@ -3,6 +3,7 @@ desc = "A portable device to call anyone you want." icon = 'modular_darkpack/modules/phones/icons/phone.dmi' ONFLOOR_ICON_HELPER('modular_darkpack/modules/phones/icons/phone_onfloor.dmi') + base_icon_state = "phone" icon_state = "phone" inhand_icon_state = "phone" lefthand_file = 'modular_darkpack/modules/phones/icons/lefthand.dmi' @@ -26,10 +27,14 @@ var/obj/machinery/newscaster/irc_channel // Do we have a SIM card? var/obj/item/sim_card/sim_card + /// If we should spawn a SIM card as init, what should its type be? + var/default_sim_card_type = /obj/item/sim_card // There's a wiki in our phone. Literally. var/obj/item/book/manual/wiki/wiki_book - // Phone flags, for things like if its open or if it has no sim card. - var/phone_flags = NONE + // Is the phone opened? + var/opened = FALSE + /// Is this phone always open? + var/always_open = FALSE // The phone's current state. VAR_PRIVATE/current_state = PHONE_AVAILABLE // The number the phone has dialed. @@ -42,8 +47,6 @@ var/ringer = TRUE // If the phone shows balloon alerts when ringing. var/vibration = TRUE - // Passive particle effect generation for when on call - var/obj/effect/abstract/particle_holder/particle_generator // If the phone's microphone is muted. var/muted = FALSE // ID of the timer that the phone uses for ringing. Deleted once the user denies a phone call or misses it. @@ -67,8 +70,8 @@ /obj/item/smartphone/Initialize(mapload) . = ..() GLOB.phones_list += src - if(!sim_card) - sim_card = new() + if(!sim_card && default_sim_card_type) + sim_card = new default_sim_card_type(src) sim_card.phone_weakref = WEAKREF(src) phone_radio = new(src) phone_radio.keyslot = new @@ -113,8 +116,7 @@ if(our_contact.number == sim_card.phone_number) contact_network.contacts -= our_contact - if(particle_generator) - QDEL_NULL(particle_generator) + remove_shared_particles(/particles/phone_ringing, delete_on_empty = FALSE) lose_hearing_sensitivity(ROUNDSTART_TRAIT) UnregisterSignal(src, COMSIG_MOVABLE_HEAR) @@ -136,30 +138,32 @@ . += span_notice("[EXAMINE_HINT("Alt-Click")] or [EXAMINE_HINT("Right-Click")] to toggle the screen.") if(sim_card) . += span_notice("[EXAMINE_HINT("Ctrl-Click")] to remove [sim_card].") + if(sim_card.phone_number && (user.is_holding(src) || isobserver(user))) + . += span_notice("Its phone number is [span_bold("[sim_card.phone_number]")].") else . += span_notice("You can [EXAMINE_HINT("Insert")] a SIM card.") /obj/item/smartphone/attack_self(mob/user, modifiers) . = ..() - if(!(phone_flags & PHONE_OPEN)) + if(!opened) toggle_screen(user) ui_interact(user) /obj/item/smartphone/click_alt(mob/user) - if(!(user.is_holding(src))) + if(!user.is_holding(src)) return CLICK_ACTION_BLOCKING toggle_screen(user) return CLICK_ACTION_SUCCESS /obj/item/smartphone/item_ctrl_click(mob/user) - if(!(user.is_holding(src))) + if(!user.is_holding(src)) return CLICK_ACTION_BLOCKING if(!sim_card) balloon_alert(user, "no sim card!") return CLICK_ACTION_BLOCKING if(do_after(user, 2 SECONDS, src)) balloon_alert(user, "you remove \the [sim_card]!") - log_phone("[key_name(usr)] removed a SIM card with the number [sim_card.phone_number].") + log_phone("[key_name(user)] removed a SIM card with the number [sim_card.phone_number].") switch(current_state) if(PHONE_CALLING) hang_up_phone_call(dialed_number) @@ -170,43 +174,55 @@ user.put_in_hands(sim_card) sim_card.phone_weakref = null sim_card = null - phone_flags |= PHONE_NO_SIM + SStgui.update_uis(src) return CLICK_ACTION_SUCCESS return CLICK_ACTION_BLOCKING /obj/item/smartphone/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(istype(tool, /obj/item/sim_card)) - if(sim_card) - balloon_alert(user, "[sim_card] already installed!") - return ITEM_INTERACT_BLOCKING - balloon_alert(user, "you insert \the [tool]!") - sim_card = tool - user.transferItemToLoc(tool, src) - sim_card.phone_weakref = WEAKREF(src) - phone_flags &= ~PHONE_NO_SIM - log_phone("[key_name(usr)] inserted a SIM card with the number [sim_card.phone_number].") - return TRUE - return ..() + if(!istype(tool, /obj/item/sim_card)) + return NONE + if(sim_card) + balloon_alert(user, "[sim_card] already installed!") + return ITEM_INTERACT_BLOCKING + if(!user.transferItemToLoc(tool, src)) + balloon_alert(user, "failed to install [tool]!") + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "you insert \the [tool]!") + sim_card = tool + sim_card.phone_weakref = WEAKREF(src) + log_phone("[key_name(user)] inserted a SIM card with the number [sim_card.phone_number].") + SStgui.update_uis(src) + return ITEM_INTERACT_SUCCESS + +/obj/item/smartphone/update_icon_state() + . = ..() + icon_state = (opened && !always_open) ? "[base_icon_state]_on" : base_icon_state + inhand_icon_state = (opened && !always_open) ? "[base_icon_state]_on" : base_icon_state /obj/item/smartphone/ui_status(mob/user, datum/ui_state/state) - if(!(phone_flags & PHONE_OPEN)) + if(!opened) return UI_CLOSE return ..() /obj/item/smartphone/ui_interact(mob/user, datum/tgui/ui) - . = ..() - var/datum/asset/background_files = get_asset_datum(/datum/asset/simple/phone_assets) - if(user.client) - background_files.send(user.client) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Telephone") ui.open() +/obj/item/smartphone/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/phone_assets), + ) + /obj/item/smartphone/ui_data(mob/living/user) var/list/data = list() - data["my_number"] = sim_card ? sim_card.phone_number : "No SIM card inserted." - data["no_sim_card"] = (phone_flags & PHONE_NO_SIM) ? TRUE : FALSE + if(sim_card) + data["my_number"] = sim_card.phone_number + data["no_sim_card"] = FALSE + else + data["my_number"] = "No SIM card inserted" + data["no_sim_card"] = TRUE data["phone_in_call"] = (current_state == PHONE_IN_CALL) ? TRUE : FALSE data["phone_ringing"] = (current_state == PHONE_RINGING) ? TRUE : FALSE data["phone_calling"] = (current_state == PHONE_CALLING) ? TRUE : FALSE @@ -302,14 +318,16 @@ return data -/obj/item/smartphone/ui_act(action, params, datum/tgui/ui) +/obj/item/smartphone/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return + + var/mob/user = ui.user switch(action) if("call") - start_phone_call(usr, params["number"]) - log_phone("[key_name(usr)] called [params["number"]].") + start_phone_call(user, params["number"]) + log_phone("[key_name(user)] called [params["number"]].") return TRUE if("hang") @@ -320,93 +338,93 @@ return TRUE if("accept") - accept_phone_call(usr) - log_phone("[key_name(usr)] answered a phone call.") + accept_phone_call(user) + log_phone("[key_name(user)] answered a phone call.") return TRUE if("decline") decline_phone_call() - log_phone("[key_name(usr)] declined a phone call.") + log_phone("[key_name(user)] declined a phone call.") return TRUE if("publish_number") - to_chat(usr, span_notice("This text will represent you in the phonebook. example: Jane Doe | Anarchy Rose Manager")) - var/name = tgui_input_text(usr, "Phonebook Name", "Publish Number") + to_chat(user, span_notice("This text will represent you in the phonebook. example: Jane Doe | Anarchy Rose Manager")) + var/name = tgui_input_text(user, "Phonebook Name", "Publish Number", max_length = MAX_MESSAGE_LEN) if(!name) - to_chat(usr, span_danger("You must input text to publish your number.")) + to_chat(user, span_danger("You must input text to publish your number.")) return if(!sim_card) - to_chat(usr, span_danger("You must insert a SIM card to publish your number.")) + to_chat(user, span_danger("You must insert a SIM card to publish your number.")) return - name = trim(copytext_char(sanitize(name), 1, MAX_MESSAGE_LEN)) for(var/contact in SSphones.published_phone_numbers) if(SSphones.published_phone_numbers[contact] == sim_card.phone_number) - to_chat(usr, span_danger("Error: This number is already published.")) + to_chat(user, span_danger("Error: This number is already published.")) return TRUE SSphones.published_phone_numbers[name] = sim_card.phone_number - to_chat(usr, span_notice("Your number is now published.")) + to_chat(user, span_notice("Your number is now published.")) sim_card.published = TRUE sim_card.published_name = name - log_phone("[key_name(usr)] published their number ([name])/[sim_card.phone_number] to the phonebook.") + log_phone("[key_name(user)] published their number ([name])/[sim_card.phone_number] to the phonebook.") return TRUE if("unpublish_number") for(var/contact in SSphones.published_phone_numbers) - if(SSphones.published_phone_numbers[contact] == sim_card.phone_number) - log_phone("[key_name(usr)] unpublished their number ([contact])/[sim_card.phone_number] from the phonebook.") - SSphones.published_phone_numbers.Remove(contact) - sim_card.published = FALSE - sim_card.published_name = null - to_chat(usr, span_notice("Your number is now unpublished.")) - return TRUE + if(SSphones.published_phone_numbers[contact] != sim_card.phone_number) + continue + log_phone("[key_name(user)] unpublished their number ([contact])/[sim_card.phone_number] from the phonebook.") + SSphones.published_phone_numbers -= contact + sim_card.published = FALSE + sim_card.published_name = null + to_chat(user, span_notice("Your number is now unpublished.")) + return TRUE if("custom_background") - to_chat(usr, span_danger("Do NOT use images that can be considered offensive or obscene, or that contain references to something that happened after the year [CURRENT_STATION_YEAR]. Recommended image dimensions: 400x600 ")) - custom_background = tgui_input_text(usr, "Input background image URL", "Custom Background") + to_chat(user, span_danger("Do NOT use images that can be considered offensive or obscene, or that contain references to something that happened after the year [CURRENT_STATION_YEAR]. Recommended image dimensions: 400x600 ")) + custom_background = tgui_input_text(user, "Input background image URL", "Custom Background") if(!custom_background) - to_chat(usr, span_danger("You must input a URL to set a custom background.")) + to_chat(user, span_danger("You must input a URL to set a custom background.")) return phone_background = custom_background - log_phone("[key_name(usr)] set a custom background image on [src]: [custom_background]") + log_phone("[key_name(user)] set a custom background image on [src]: [custom_background]") return TRUE if("add_contact") - var/number = tgui_input_text(usr, "Input number", "Add Contact") + var/number = tgui_input_text(user, "Input number", "Add Contact") if(length(number) > 15) - to_chat(usr, span_danger("Entered number is too long")) + to_chat(user, span_danger("Entered number is too long")) return FALSE var/stripped_number = replacetext(number, " ", "") // remove spaces - var/new_contact_name = tgui_input_text(usr, "Input name", "Add Contact") + var/new_contact_name = tgui_input_text(user, "Input name", "Add Contact") if(!new_contact_name || !number) - to_chat(usr, span_danger("You must provide both a name and a number.")) + to_chat(user, span_danger("You must provide both a name and a number.")) return FALSE var/datum/phonecontact/new_contact = new() new_contact.number = "[stripped_number]" new_contact.name = "[new_contact_name]" contacts += new_contact - log_phone("[key_name(usr)] added a new contact: [new_contact_name] ([stripped_number])") + log_phone("[key_name(user)] added a new contact: [new_contact_name] ([stripped_number])") return TRUE if("remove_contact") - var/number = tgui_input_text(usr, "Input number", "Remove Contact") + var/number = tgui_input_text(user, "Input number", "Remove Contact") if(length(number) > 15) - to_chat(usr, span_danger("Entered number is too long")) + to_chat(user, span_danger("Entered number is too long")) return FALSE for(var/datum/phonecontact/contact in contacts) if(contact.number == number) contacts -= contact - log_phone("[key_name(usr)] removed a contact with number: [number]") + log_phone("[key_name(user)] removed a contact with number: [number]") return TRUE return FALSE if("block") - var/block_number = tgui_input_text(usr, "Input number to block", "Block Contact") + var/block_number = tgui_input_text(user, "Input number to block", "Block Contact") if(!block_number) - to_chat(usr, span_warning("You must provide a number.")) + to_chat(user, span_warning("You must provide a number.")) return FALSE if(length(block_number) > 15) - to_chat(usr, span_warning("Invalid number.")) + to_chat(user, span_warning("Invalid number.")) return FALSE var/datum/phonecontact/blocked_contact = new() @@ -417,9 +435,9 @@ return TRUE if("unblock") - var/result = tgui_input_text(usr, "Input number to unblock", "Unblock Contact") + var/result = tgui_input_text(user, "Input number to unblock", "Unblock Contact") if(!result) - to_chat(usr, span_warning("You must provide a number.")) + to_chat(user, span_warning("You must provide a number.")) return FALSE for(var/datum/phonecontact/unblocked_contact in blocked_contacts) if(unblocked_contact.name == result) @@ -429,25 +447,22 @@ if("delete_call_history") if(!length(phone_history_list)) - to_chat(usr, span_danger("You have no call history to delete.")) + to_chat(user, span_danger("You have no call history to delete.")) return FALSE - to_chat(usr, "Your total amount of history saved is: [length(phone_history_list)]") - var/number_of_deletions = tgui_input_number(usr, "Input the amount that you want to delete", "Deletion Amount", max_value = length(phone_history_list)) + to_chat(user, span_notice("Your total amount of history saved is: [length(phone_history_list)]")) + var/number_of_deletions = tgui_input_number(user, "Input the amount that you want to delete", "Deletion Amount", max_value = length(phone_history_list)) if(!number_of_deletions) return FALSE //Delete the call history depending on the amount inputed by the User if(number_of_deletions > length(phone_history_list)) //Verify if the requested amount in bigger than the history list. - to_chat(usr, "You cannot delete more items than the history contains.") + to_chat(user, span_warning("You cannot delete more items than the history contains.")) return FALSE else - for(var/i in 1 to number_of_deletions) - //It will always delete the first item of the list, so the last logs are deleted first - var/item_to_remove = phone_history_list[1] - phone_history_list -= item_to_remove - to_chat(usr, "[number_of_deletions] call history entries were deleted. Remaining: [length(phone_history_list)]") + phone_history_list.Cut(1, number_of_deletions + 1) + to_chat(user, span_notice("[number_of_deletions] call history entries were deleted. Remaining: [length(phone_history_list)]")) return TRUE if("terminal_sound") @@ -457,21 +472,23 @@ if("silent") ringer = !ringer - balloon_alert(usr, "ringer [ringer ? "on" : "off"]!") + balloon_alert(user, "ringer [ringer ? "on" : "off"]!") + if(!ringer) + remove_shared_particles(/particles/phone_ringing, delete_on_empty = FALSE) return TRUE if("vibration") vibration = !vibration - balloon_alert(usr, "vibration [vibration ? "on" : "off"]!") + balloon_alert(user, "vibration [vibration ? "on" : "off"]!") return TRUE if("speaker") if(phone_radio.canhear_range == 1) phone_radio.canhear_range = 3 - balloon_alert(usr, "speaker on!") + balloon_alert(user, "speaker on!") else phone_radio.canhear_range = 1 - balloon_alert(usr, "speaker off!") + balloon_alert(user, "speaker off!") return TRUE if("set_background") @@ -481,10 +498,10 @@ if("mute") muted = !muted phone_radio.set_listening(!muted) - balloon_alert(usr, "[muted ? "muted" : "unmuted"]!") + balloon_alert(user, "[muted ? "muted" : "unmuted"]!") if("wiki") - wiki_book.display_content(usr) + wiki_book.display_content(user) if("view_conversation") current_viewed_conversation = params["contact_number"] @@ -494,22 +511,24 @@ return TRUE if("submit_post") - submit_post(params["body"]) + submit_post(user, params["body"]) return TRUE if("endpost_registration") - endpost_registration() + endpost_registration(user) return TRUE if("remove_endpost") + if(!is_admin(user.client)) + CRASH("Non-admin somehow tried to call remove_endpost") var/post_index = text2num(params["post_index"]) if(!post_index) - to_chat(usr, "Invalid post index.") + to_chat(user, span_warning("Invalid post index.")) return FALSE var/list/selected_post = SSphones.endpost_posts[post_index] SSphones.endpost_posts.Cut(post_index, post_index + 1) - to_chat(usr, "Post '[selected_post["body"]]' by [selected_post["author"]] removed.") - log_phone("[key_name(usr)] removed an endpost: [selected_post["body"]] by [selected_post["author"]]", list("author" = selected_post["author"], "body" = selected_post["body"])) + to_chat(user, span_notice("Post '[selected_post["body"]]' by [selected_post["author"]] removed.")) + log_phone("[key_name(user)] removed an endpost: [selected_post["body"]] by [selected_post["author"]]", list("author" = selected_post["author"], "body" = selected_post["body"])) return TRUE if("keyboard_click") @@ -522,7 +541,7 @@ var/message_text = params["message_text"] if(!contact_number || !message_text) return FALSE - send_text_message(contact_number, message_text) + send_text_message(user, contact_number, message_text) if(ringer) playsound(loc, 'modular_darkpack/modules/phones/sounds/text_send.ogg', 50, TRUE) return TRUE @@ -534,7 +553,7 @@ if(convo.contact_number == contact_number) return convo -/obj/item/smartphone/proc/send_text_message(contact_number, message_text) +/obj/item/smartphone/proc/send_text_message(mob/user, contact_number, message_text) if(!contact_number || !message_text) return FALSE @@ -556,7 +575,7 @@ receiving_phone.conversations += recv_conversation recv_conversation.add_message(message_text, FALSE) addtimer(CALLBACK(receiving_phone, PROC_REF(after_text_received), contact_name, message_text), rand(1 SECONDS, 2 SECONDS)) //simulate random delay before sending an audible/visible alert - log_phone("[key_name(usr)] sent a text to [contact_number]: [message_text]", list("sender" = contact_name, "receiver" = recv_contact_name, "message" = message_text)) + log_phone("[key_name(user)] sent a text to [contact_number]: [message_text]", list("sender" = contact_name, "receiver" = recv_contact_name, "message" = message_text)) return TRUE //stuff to do after a text is received @@ -583,15 +602,10 @@ return formatted_messages /obj/item/smartphone/proc/toggle_screen(mob/user) - if(phone_flags & PHONE_OPEN) - phone_flags &= ~PHONE_OPEN - else - phone_flags |= PHONE_OPEN - icon_state = (phone_flags & PHONE_OPEN) ? "phone_on" : "phone" - inhand_icon_state = (phone_flags & PHONE_OPEN) ? "phone_on" : "phone" - update_icon() + opened = always_open || !opened + update_appearance(UPDATE_ICON_STATE) -/obj/item/smartphone/proc/submit_post(body) +/obj/item/smartphone/proc/submit_post(mob/user, body) if(!body) return FALSE @@ -606,13 +620,13 @@ ) UNTYPED_LIST_ADD(SSphones.endpost_posts, new_post) - log_phone("[key_name(usr)] submitted a new endpost: [trim(body)] as [endpost_username]") + log_phone("[key_name(user)] submitted a new endpost: [trim(body)] as [endpost_username]") return TRUE -/obj/item/smartphone/proc/endpost_registration() - var/new_username = tgui_input_text(usr, "Choose your username:", "EndPost Registration", "", 14) //max size of 14 chars or it starts bumping elements around - log_phone("[key_name(usr)] [endpost_username ? "updated" : "registered"] their username as [new_username]") +/obj/item/smartphone/proc/endpost_registration(mob/user) + var/new_username = tgui_input_text(user, "Choose your username:", "EndPost Registration", max_length = 14) //max size of 14 chars or it starts bumping elements around + log_phone("[key_name(user)] [endpost_username ? "updated" : "registered"] their username as [new_username]") endpost_username = new_username return TRUE diff --git a/modular_darkpack/modules/phones/code/phone_effects.dm b/modular_darkpack/modules/phones/code/phone_effects.dm index 0eddabad1ceb..d4193e1d869a 100644 --- a/modular_darkpack/modules/phones/code/phone_effects.dm +++ b/modular_darkpack/modules/phones/code/phone_effects.dm @@ -10,8 +10,3 @@ gravity = list(0, 0.1) position = generator(GEN_SPHERE, 0, 16, NORMAL_RAND) spin = generator(GEN_NUM, -1, 1, NORMAL_RAND) - - -/obj/item/smartphone/proc/setup_particles() - if(!particle_generator) - particle_generator = new(src, /particles/phone_ringing, PARTICLE_ATTACH_MOB) diff --git a/modular_darkpack/modules/phones/code/phone_procs.dm b/modular_darkpack/modules/phones/code/phone_procs.dm index b2744e0af5aa..db3423e400a0 100644 --- a/modular_darkpack/modules/phones/code/phone_procs.dm +++ b/modular_darkpack/modules/phones/code/phone_procs.dm @@ -59,17 +59,17 @@ if(current_state == PHONE_RINGING) START_PROCESSING(SSprocessing, src) if(ringer) - setup_particles() + add_shared_particles(/particles/phone_ringing, particle_flags = PARTICLE_ATTACH_MOB) - if(current_state == PHONE_IN_CALL || current_state == PHONE_AVAILABLE) + else if(current_state == PHONE_IN_CALL || current_state == PHONE_AVAILABLE) if(phone_ringing_timer) deltimer(phone_ringing_timer) - if(particle_generator) - QDEL_NULL(particle_generator) + phone_ringing_timer = null + remove_shared_particles(/particles/phone_ringing, delete_on_empty = FALSE) STOP_PROCESSING(SSprocessing, src) /obj/item/smartphone/proc/check_missing_sim_card(mob/user) - if(phone_flags & PHONE_NO_SIM) + if(QDELETED(sim_card)) balloon_alert(user, "no SIM!") return TRUE return FALSE diff --git a/modular_darkpack/modules/phones/code/phone_subsystem.dm b/modular_darkpack/modules/phones/code/phone_subsystem.dm index dab078bed12d..7fb10bc0d378 100644 --- a/modular_darkpack/modules/phones/code/phone_subsystem.dm +++ b/modular_darkpack/modules/phones/code/phone_subsystem.dm @@ -15,6 +15,12 @@ SUBSYSTEM_DEF(phones) // Posts for the endpost feed var/list/endpost_posts = list() +/datum/controller/subsystem/phones/Recover() + assigned_phone_numbers = SSphones.assigned_phone_numbers + frequencies_in_use = SSphones.frequencies_in_use + published_phone_numbers = SSphones.published_phone_numbers + endpost_posts = SSphones.endpost_posts + // Generates a random phone number from the available ranges, ten digits, starts with a 415 or 628. /datum/controller/subsystem/phones/proc/random_number() return "[pick("415","628")][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)]" @@ -59,5 +65,4 @@ SUBSYSTEM_DEF(phones) for(var/obj/item/sim_card/sim_card as anything in assigned_phone_numbers) if(sim_card.phone_number == phone_number) gotten_sim_card = sim_card - var/gotten_phone = gotten_sim_card?.phone_weakref?.resolve() - return gotten_phone + return gotten_sim_card?.phone_weakref?.resolve() diff --git a/modular_darkpack/modules/phones/code/phone_subtypes.dm b/modular_darkpack/modules/phones/code/phone_subtypes.dm index 813f90e3d258..ee4c4bdb4616 100644 --- a/modular_darkpack/modules/phones/code/phone_subtypes.dm +++ b/modular_darkpack/modules/phones/code/phone_subtypes.dm @@ -114,6 +114,11 @@ , alist(NETWORK_ID = VAMPIRE_LEADER_NETWORK, OUR_ROLE = "Bank Manager", USE_JOB_TITLE = FALSE) ) +/obj/item/smartphone/giovanni_nonni + contact_networks_pre_init = list( + alist(NETWORK_ID = GIOVANNI_NETWORK, OUR_ROLE = "Bank Shareholder", USE_JOB_TITLE = FALSE) + ) + /obj/item/smartphone/giovanni_squadra contact_networks_pre_init = list( alist(NETWORK_ID = GIOVANNI_NETWORK, OUR_ROLE = "Bank Security", USE_JOB_TITLE = FALSE) diff --git a/modular_darkpack/modules/phones/code/stationary_subtypes.dm b/modular_darkpack/modules/phones/code/stationary_subtypes.dm index 723127447a9f..c793f547145a 100644 --- a/modular_darkpack/modules/phones/code/stationary_subtypes.dm +++ b/modular_darkpack/modules/phones/code/stationary_subtypes.dm @@ -3,14 +3,12 @@ desc = "Ring ring. Ring ring. Ring ring." ONFLOOR_ICON_HELPER(null) icon = 'modular_darkpack/modules/phones/icons/phone.dmi' + base_icon_state = "payphone" icon_state = "payphone" anchored = TRUE - -/obj/item/smartphone/payphone/Initialize(mapload) - sim_card = new /obj/item/sim_card/landline() - sim_card.phone_weakref = WEAKREF(src) - phone_flags |= PHONE_OPEN - . = ..() + opened = TRUE + always_open = TRUE + default_sim_card_type = /obj/item/sim_card/landline /obj/item/smartphone/payphone/attack_hand(mob/user, list/modifiers) . = ..() @@ -21,14 +19,12 @@ desc = "The usual phone of a cleaning company used to communicate with employees" ONFLOOR_ICON_HELPER(null) icon = 'modular_darkpack/modules/phones/icons/phone.dmi' + base_icon_state = "phone_black" icon_state = "phone_black" anchored = TRUE - -/obj/item/smartphone/clean/Initialize(mapload) - sim_card = new /obj/item/sim_card/cleaner() - sim_card.phone_weakref = WEAKREF(src) - phone_flags |= PHONE_OPEN - . = ..() + opened = TRUE + always_open = TRUE + default_sim_card_type = /obj/item/sim_card/cleaner /obj/item/smartphone/clean/attack_hand(mob/user, list/modifiers) . = ..() @@ -39,14 +35,12 @@ desc = "A phone used for emergency calls." ONFLOOR_ICON_HELPER(null) icon = 'modular_darkpack/modules/phones/icons/phone.dmi' + base_icon_state = "phone_red" icon_state = "phone_red" anchored = TRUE - -/obj/item/smartphone/emergency/Initialize(mapload) - sim_card = new /obj/item/sim_card/emergency() - sim_card.phone_weakref = WEAKREF(src) - phone_flags |= PHONE_OPEN - . = ..() + opened = TRUE + always_open = TRUE + default_sim_card_type = /obj/item/sim_card/emergency /obj/item/smartphone/emergency/attack_hand(mob/user, list/modifiers) . = ..() diff --git a/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm b/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm index 91ef200f59db..78508bef81a1 100644 --- a/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm +++ b/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm @@ -650,7 +650,7 @@ SEND_SIGNAL(src, COMSIG_POWER_DEACTIVATE, src, target) SEND_SIGNAL(owner, COMSIG_POWER_DEACTIVATE, src, target) - if (target) + if (istype(target)) SEND_SIGNAL(target, COMSIG_POWER_DEACTIVATE_ON, src) if (!multi_activate) diff --git a/modular_darkpack/modules/powers/code/discipline/animalism.dm b/modular_darkpack/modules/powers/code/discipline/animalism.dm index 9cef836b2fed..94de633a1a76 100644 --- a/modular_darkpack/modules/powers/code/discipline/animalism.dm +++ b/modular_darkpack/modules/powers/code/discipline/animalism.dm @@ -1,6 +1,11 @@ /datum/discipline/animalism name = "Animalism" - desc = "Summons spectral animals over your targets. Violates Masquerade." + desc = {"Summons spectral animals over your targets. Violates Masquerade. +● Summon Rat: Passive +●● Summon Cat: Passive +●●● Summon Wolf: Passive +●●●● Summon Bat: Passive +●●●●● Skitter: Passive"} icon_state = "animalism" power_type = /datum/discipline_power/animalism diff --git a/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm b/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm index 4f3e85b35eb3..717542a2312b 100644 --- a/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm +++ b/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm @@ -8,7 +8,12 @@ /datum/discipline/auspex name = "Auspex" - desc = "Allows to see entities, auras and their health through walls." + desc = {"Allows to see entities, auras and their health through walls. +● Heightened Senses: Passive +●● Aura Perception: Passive +●●● The Spirit's Touch: Passive +●●●● Telepathy: Intelligence + Subterfuge vs. target's Willpower +●●●●● Psychic Projection: Perception + Awareness (difficulty 7)"} icon_state = "auspex" power_type = /datum/discipline_power/auspex diff --git a/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm b/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm index 7a997eeba507..f551f5d4152d 100644 --- a/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm +++ b/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm @@ -3,7 +3,8 @@ /datum/discipline/bloodheal name = "Bloodheal" - desc = "Use the power of your Vitae to mend your flesh." + desc = {"Use the power of your Vitae to mend your flesh. +● Bloodheal: Stamina + Survival (difficulty 8) - roll only on interrupted cast"} icon_state = "bloodheal" power_type = /datum/discipline_power/bloodheal selectable = FALSE diff --git a/modular_darkpack/modules/powers/code/discipline/celerity/celerity.dm b/modular_darkpack/modules/powers/code/discipline/celerity/celerity.dm index 16d2d1584d66..476fa030c50d 100644 --- a/modular_darkpack/modules/powers/code/discipline/celerity/celerity.dm +++ b/modular_darkpack/modules/powers/code/discipline/celerity/celerity.dm @@ -1,6 +1,11 @@ /datum/discipline/celerity name = "Celerity" - desc = "Boosts your speed. Violates Masquerade." + desc = {"Boosts your speed. Violates Masquerade. +● Celerity 1: Passive +●● Celerity 2: Passive +●●● Celerity 3: Passive +●●●● Celerity 4: Passive +●●●●● Celerity 5: Passive"} icon_state = "celerity" power_type = /datum/discipline_power/celerity diff --git a/modular_darkpack/modules/powers/code/discipline/daimonion/daimonion.dm b/modular_darkpack/modules/powers/code/discipline/daimonion/daimonion.dm index 978192525bed..8fa7afb7c649 100644 --- a/modular_darkpack/modules/powers/code/discipline/daimonion/daimonion.dm +++ b/modular_darkpack/modules/powers/code/discipline/daimonion/daimonion.dm @@ -1,6 +1,11 @@ /datum/discipline/daimoinon name = "Daimoinon" - desc = "Draw power from the demons and infernal nature of Hell. Use subtle power to manipulate people and when you must, draw upon fire itself and protect yourself." + desc = {"Draw power from the demons and infernal nature of Hell. Use subtle power to manipulate people and when you must, draw upon fire itself and protect yourself. +● Sense the Sin: Perception + Empathy vs. target's Self-Control + 4 +●● Fear of the Void Below: Wits + Intimidation vs. target's Courage + 4 +●●● Conflagration: No roll +●●●● Psychomania: target's lowest Virtue +●●●●● Condemnation: Intelligence + Occult vs. target's Willpower"} icon_state = "daimonion" clan_restricted = TRUE power_type = /datum/discipline_power/daimoinon diff --git a/modular_darkpack/modules/powers/code/discipline/dementation.dm b/modular_darkpack/modules/powers/code/discipline/dementation.dm index 51d6aba9757c..81717267712d 100644 --- a/modular_darkpack/modules/powers/code/discipline/dementation.dm +++ b/modular_darkpack/modules/powers/code/discipline/dementation.dm @@ -1,6 +1,11 @@ /datum/discipline/dementation name = "Dementation" - desc = "Makes all humans in radius mentally ill for a moment, supressing their defending ability." + desc = {"Makes all humans in radius mentally ill for a moment, supressing their defending ability. +● Passion: Charisma + Empathy +●● The Haunting: Manipulation + Subterfuge +●●● Eyes of Chaos: Perception + Occult +●●●● Voice of Madness: Manipulation + Empathy +●●●●● Total Insanity: Manipulation + Intimidation"} icon_state = "dementation" clan_restricted = TRUE power_type = /datum/discipline_power/dementation @@ -374,7 +379,7 @@ frenzy or Rötschreck response is automatic. owner.say(attack_text, spans = list("bold", "singing")) var/list/potential_targets = list() for(var/mob/living/carbon/human/hearer in (get_hearers_in_view(8, owner) - owner)) - if(HAS_TRAIT(hearer, TRAIT_DEAF) || hearer.stat > CONSCIOUS) + if(HAS_TRAIT(hearer, TRAIT_DEAF) || IS_UNCONSCIOUS_OR_CRIT(hearer)) continue potential_targets += hearer var/targets_affected = 0 @@ -437,7 +442,7 @@ determines the duration. return TRUE /datum/discipline_power/dementation/total_insanity/proc/self_attack(iteration) - if(attack_target.stat > CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(attack_target)) return if(iteration <= 0) return diff --git a/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm b/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm index 381f8159bd79..80076d4ffb7a 100644 --- a/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm +++ b/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm @@ -2,7 +2,12 @@ /datum/discipline/dominate name = "Dominate" - desc = "Suppresses will of your targets and forces them to obey you, if their will is not more powerful than yours." + desc = {"Suppresses will of your targets and forces them to obey you, if their will is not more powerful than yours. +● Command: Manipulation + Intimidation +●● Mesmerize: Manipulation + Leadership +●●● The Forgetful Mind: Wits + Subterfuge +●●●● Conditioning: Charisma + Leadership +●●●●● Possession: Charisma + Intimidation"} icon_state = "dominate" power_type = /datum/discipline_power/dominate var/list/botched_targets //a lazylist of weakrefs diff --git a/modular_darkpack/modules/powers/code/discipline/fortitude/fortitude.dm b/modular_darkpack/modules/powers/code/discipline/fortitude/fortitude.dm index b0fc61089dc7..2e1670ac46a2 100644 --- a/modular_darkpack/modules/powers/code/discipline/fortitude/fortitude.dm +++ b/modular_darkpack/modules/powers/code/discipline/fortitude/fortitude.dm @@ -1,6 +1,11 @@ /datum/discipline/fortitude name = "Fortitude" - desc = "Boosts armor." + desc = {"Boosts armor. +● Fortitude 1: Passive +●● Fortitude 2: Passive +●●● Fortitude 3: Passive +●●●● Fortitude 4: Passive +●●●●● Fortitude 5: Passive"} icon_state = "fortitude" power_type = /datum/discipline_power/fortitude diff --git a/modular_darkpack/modules/powers/code/discipline/healer_valeren.dm b/modular_darkpack/modules/powers/code/discipline/healer_valeren.dm index 225a22b69f3c..8556a1f3fccb 100644 --- a/modular_darkpack/modules/powers/code/discipline/healer_valeren.dm +++ b/modular_darkpack/modules/powers/code/discipline/healer_valeren.dm @@ -1,6 +1,11 @@ /datum/discipline/valeren name = "Healer Valeren" - desc = "Use your third eye in healing or protecting needs." + desc = {"Use your third eye in healing or protecting needs. +● Sense Vitality: Passive +●● Anesthetic Touch: Passive +●●● Corpore Sano: Passive +●●●● Shepherd's Watch: Passive +●●●●● Unburden the Bestial Soul: Passive"} icon_state = "valeren" clan_restricted = TRUE power_type = /datum/discipline_power/valeren diff --git a/modular_darkpack/modules/powers/code/discipline/melpominee.dm b/modular_darkpack/modules/powers/code/discipline/melpominee.dm index 47560f46a109..a93acf936f36 100644 --- a/modular_darkpack/modules/powers/code/discipline/melpominee.dm +++ b/modular_darkpack/modules/powers/code/discipline/melpominee.dm @@ -1,6 +1,12 @@ /datum/discipline/melpominee name = "Melpominee" - desc = "Named for the Greek Muse of Tragedy, Melpominee is a unique discipline of the Daughters of Cacophony. It explores the power of the voice, shaking the very soul of those nearby and allowing the vampire to perform sonic feats otherwise impossible." + desc = {"Named for the Greek Muse of Tragedy, Melpominee is a unique discipline of the Daughters of Cacophony. It explores the power of the voice, shaking the very soul of those nearby and allowing the vampire to perform sonic feats otherwise impossible. +● The Missing Voice: Passive +●● Phantom Speaker: Wits + Performance (difficulty 7) +●●● Madrigal: Wits + Performance vs. target's Wits + Awareness +●●●● Siren's Beckoning: Wits + Performance vs. target's Willpower +●●●●● Virtuosa: Passive toggle +●●●●●● Shattering Crescendo: Passive"} icon_state = "melpominee" clan_restricted = TRUE power_type = /datum/discipline_power/melpominee diff --git a/modular_darkpack/modules/powers/code/discipline/mytherceria.dm b/modular_darkpack/modules/powers/code/discipline/mytherceria.dm index f97a78d23b39..1b52be5811b2 100644 --- a/modular_darkpack/modules/powers/code/discipline/mytherceria.dm +++ b/modular_darkpack/modules/powers/code/discipline/mytherceria.dm @@ -1,6 +1,11 @@ /datum/discipline/mytherceria name = "Mytherceria" - desc = "Mytherceria is a Discipline that manifests in faerie-blooded vampires such as the Kiasyd and Maeghar. It grants the vampire mystical senses, the ability to steal knowledge, and other powers attributed to fae." + desc = {"Mytherceria is a Discipline that manifests in faerie-blooded vampires such as the Kiasyd and Maeghar. It grants the vampire mystical senses, the ability to steal knowledge, and other powers attributed to fae. +● Fey Sight: Passive +●● Darkling Trickery: Passive +●●● Goblinism: Passive +●●●● Chanjelin Ward: Passive +●●●●● Riddle Phantastique: Passive"} icon_state = "mytherceria" clan_restricted = TRUE power_type = /datum/discipline_power/mytherceria diff --git a/modular_darkpack/modules/powers/code/discipline/necromancy.dm b/modular_darkpack/modules/powers/code/discipline/necromancy.dm index 89ce91769513..ea90656ca714 100644 --- a/modular_darkpack/modules/powers/code/discipline/necromancy.dm +++ b/modular_darkpack/modules/powers/code/discipline/necromancy.dm @@ -2,7 +2,12 @@ /datum/discipline/necromancy name = "Necromancy" - desc = "Offers control over another, undead reality." + desc = {"Offers control over another, undead reality. +● Shroudsight: Perception + Awareness (difficulty 7) +●● Ethereal Horde: Wits + Occult (difficulty 6) +●●● Ashes to Ashes: Wits + Occult (difficulty 6) +●●●● Cold of the Grave: Wits + Occult (difficulty 6) +●●●●● Shambling Horde: Wits + Occult (difficulty 6)"} icon_state = "necromancy" clan_restricted = TRUE power_type = /datum/discipline_power/necromancy diff --git a/modular_darkpack/modules/powers/code/discipline/obeah/obeah.dm b/modular_darkpack/modules/powers/code/discipline/obeah/obeah.dm index e3ae6845142b..3faefe9bb145 100644 --- a/modular_darkpack/modules/powers/code/discipline/obeah/obeah.dm +++ b/modular_darkpack/modules/powers/code/discipline/obeah/obeah.dm @@ -1,6 +1,11 @@ /datum/discipline/obeah name = "Obeah" - desc = "Use your third eye in healing or protecting needs." + desc = {"Use your third eye in healing or protecting needs. +● Sense Vitality: Perception + Empathy (difficulty 7) +●● Anesthetic Touch: Willpower (difficulty 8 if unwilling) +●●● Corpore Sano: No roll +●●●● Shepherd's Watch: No roll +●●●●● Unburden the Bestial Soul: Intelligence + Empathy (difficulty 8)"} icon_state = "obeah" clan_restricted = TRUE power_type = /datum/discipline_power/obeah diff --git a/modular_darkpack/modules/powers/code/discipline/obfuscate/obfuscate.dm b/modular_darkpack/modules/powers/code/discipline/obfuscate/obfuscate.dm index 6dc7c090700e..e76f6a3188f8 100644 --- a/modular_darkpack/modules/powers/code/discipline/obfuscate/obfuscate.dm +++ b/modular_darkpack/modules/powers/code/discipline/obfuscate/obfuscate.dm @@ -3,7 +3,12 @@ /datum/discipline/obfuscate name = "Obfuscate" - desc = "Makes you less noticable for living and un-living beings." + desc = {"Makes you less noticable for living and un-living beings. +● Cloak of Shadows: Passive +●● Unseen Presence: Passive +●●● Mask of a Thousand Faces: Manipulation + Performance (difficulty 7) +●●●● Vanish from the Mind's Eye: Charisma + Stealth (difficulty 6) +●●●●● Cloak the Gathering: Passive"} icon_state = "obfuscate" power_type = /datum/discipline_power/obfuscate @@ -65,7 +70,7 @@ continue //the corpses are not watching you - if (viewer.is_blind() || (viewer.stat >= UNCONSCIOUS)) + if (viewer.is_blind() || IS_UNCONSCIOUS(viewer)) continue to_chat(owner, span_warning("You cannot use [src] while you're being observed!")) diff --git a/modular_darkpack/modules/powers/code/discipline/obtenebration/obtenebration.dm b/modular_darkpack/modules/powers/code/discipline/obtenebration/obtenebration.dm index 6c39144b65d5..59c3410b92de 100644 --- a/modular_darkpack/modules/powers/code/discipline/obtenebration/obtenebration.dm +++ b/modular_darkpack/modules/powers/code/discipline/obtenebration/obtenebration.dm @@ -1,6 +1,11 @@ /datum/discipline/obtenebration name = "Obtenebration" - desc = "Controls the darkness around you." + desc = {"Controls the darkness around you. +● Shadow Play: Passive +●● Shroud of Night: Manipulation + Occult (difficulty 7) +●●● Arms of the Abyss: Manipulation + Occult (difficulty 7) +●●●● Black Metamorphosis: Manipulation + Courage (difficulty 7) +●●●●● Tenebrous Form: Passive"} icon_state = "obtenebration" clan_restricted = TRUE power_type = /datum/discipline_power/obtenebration @@ -119,7 +124,7 @@ cooldown_length = 1 TURNS var/list/active_tentacles = list() - var/aggro_mode = "Aggressive" + var/aggro_mode = ABYSS_TENTACLE_MODE_AGGRESSIVE /datum/discipline_power/obtenebration/arms_of_the_abyss/activate(atom/target) . = ..() @@ -157,7 +162,7 @@ // if we ended up making a new tentacle add it to our list and inherit set aggro_mode if(new_tentacle) - new_tentacle.aggro_mode = aggro_mode + new_tentacle.ai_controller?.set_blackboard_key(BB_ABYSS_TENTACLE_MODE, aggro_mode) active_tentacles += new_tentacle else to_chat(usr, span_warning("The area is too bright for the shadows to manifest!")) @@ -323,9 +328,9 @@ /datum/action/aggro_mode name = "Tentacle Control" desc = "Switches the aggro mode of your Arms of the Abyss" - button_icon = 'icons/hud/screen_glass.dmi' + button_icon = 'modular_darkpack/master_files/icons/hud/screen_gen.dmi' button_icon_state = "harm" - var/current_mode = "Aggressive" + var/current_mode = ABYSS_TENTACLE_MODE_AGGRESSIVE var/datum/discipline_power/obtenebration/arms_of_the_abyss/abyss_power /datum/action/aggro_mode/New(Target) @@ -347,9 +352,9 @@ return var/list/options = list( - "Aggressive" = "Aggressive (grab and damage targets)", - "Control" = "Control (grab and restrain without damage)", - "Passive" = "Passive (don't attack or grab)" + ABYSS_TENTACLE_MODE_AGGRESSIVE = "Aggressive (grab and damage targets)", + ABYSS_TENTACLE_MODE_CONTROL = "Control (grab and restrain without damage)", + ABYSS_TENTACLE_MODE_PASSIVE = "Passive (don't attack or grab)" ) var/select = tgui_input_list(tentacle_owner, "Select tentacle behaviour", "Tentacle Mode", options) @@ -363,13 +368,13 @@ var/tentacles = 0 for(var/mob/living/basic/abyss_tentacle/T in abyss_power?.active_tentacles) if(T && !QDELETED(T)) - var/was_passive = (T.aggro_mode == "Passive") - T.aggro_mode = select + var/was_passive = (T.ai_controller?.blackboard[BB_ABYSS_TENTACLE_MODE] == ABYSS_TENTACLE_MODE_PASSIVE) + T.ai_controller?.set_blackboard_key(BB_ABYSS_TENTACLE_MODE, abyss_power.aggro_mode) tentacles++ - if(select == "Passive" && T.grabbed_mob) + if(select == ABYSS_TENTACLE_MODE_PASSIVE && T.ai_controller?.blackboard[BB_ABYSS_TENTACLE_GRABBED]) T.release_grabbed_mob() - else if(was_passive && select != "Passive") + else if(was_passive && select != ABYSS_TENTACLE_MODE_PASSIVE) T.recently_released.Cut() if(tentacles) @@ -378,11 +383,11 @@ /datum/action/aggro_mode/proc/update_button_icon() switch(current_mode) - if("Aggressive") + if(ABYSS_TENTACLE_MODE_AGGRESSIVE) button_icon_state = "harm" - if("Control") + if(ABYSS_TENTACLE_MODE_CONTROL) button_icon_state = "grab" - if("Passive") + if(ABYSS_TENTACLE_MODE_PASSIVE) button_icon_state = "disarm" build_all_button_icons() diff --git a/modular_darkpack/modules/powers/code/discipline/potence/potence.dm b/modular_darkpack/modules/powers/code/discipline/potence/potence.dm index 11e318c84ce1..61b9f2e8001b 100644 --- a/modular_darkpack/modules/powers/code/discipline/potence/potence.dm +++ b/modular_darkpack/modules/powers/code/discipline/potence/potence.dm @@ -1,6 +1,11 @@ /datum/discipline/potence name = "Potence" - desc = "Boosts melee and unarmed damage." + desc = {"Boosts melee and unarmed damage. +● Potence 1: Passive +●● Potence 2: Passive +●●● Potence 3: Passive +●●●● Potence 4: Passive +●●●●● Potence 5: Passive"} icon_state = "potence" power_type = /datum/discipline_power/potence diff --git a/modular_darkpack/modules/powers/code/discipline/presence/presence.dm b/modular_darkpack/modules/powers/code/discipline/presence/presence.dm index 457dcdbb3f3f..e9826e510354 100644 --- a/modular_darkpack/modules/powers/code/discipline/presence/presence.dm +++ b/modular_darkpack/modules/powers/code/discipline/presence/presence.dm @@ -2,7 +2,12 @@ /datum/discipline/presence name = "Presence" - desc = "Allows you to attract, sway, and control crowds through supernatural allure and emotional manipulation." + desc = {"Allows you to attract, sway, and control crowds through supernatural allure and emotional manipulation. +● Awe: Charisma + Performance (difficulty 7) +●● Dread Gaze: Charisma + Intimidation vs. Wits + Courage +●●● Entrancement: Appearance + Empathy vs. Willpower +●●●● Summon: Charisma + Subterfuge (difficulty 7) +●●●●● Majesty: Courage vs. Charisma + Intimidation"} icon_state = "presence" power_type = /datum/discipline_power/presence diff --git a/modular_darkpack/modules/powers/code/discipline/protean/protean.dm b/modular_darkpack/modules/powers/code/discipline/protean/protean.dm index 90bda291fa83..3846b760cd60 100644 --- a/modular_darkpack/modules/powers/code/discipline/protean/protean.dm +++ b/modular_darkpack/modules/powers/code/discipline/protean/protean.dm @@ -1,6 +1,11 @@ /datum/discipline/protean name = "Protean" - desc = "Lets your beast out, making you stronger and faster. Violates Masquerade." + desc = {"Lets your beast out, making you stronger and faster. Violates Masquerade. +● Eyes of the Beast: Passive +●● Feral Claws: Passive +●●● Earth Meld: Passive +●●●● Shape of the Beast: Passive +●●●●● Mist Form: Passive"} icon_state = "protean" clan_restricted = TRUE power_type = /datum/discipline_power/protean diff --git a/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm b/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm index 5de620b3ae87..a66d799ce87f 100644 --- a/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm +++ b/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm @@ -1,6 +1,11 @@ /datum/discipline/quietus name = "Quietus" - desc = "The signature discipline of the Banu Haqim, Quietus allows the user to create poison and assassinate their targets quietly, maipulating their blood. Violates Masquerade." + desc = {"The signature discipline of the Banu Haqim, Quietus allows the user to create poison and assassinate their targets quietly, manipulating their blood. Violates Masquerade. +● Silence of Death: Passive +●● Scorpion's Touch: Permanent Willpower (difficulty 6) +●●● Dagon's Call: Stamina vs. target's Willpower +●●●● Baal's Caress: Passive +●●●●● Taste of Death: Stamina + Athletics (difficulty 6)"} icon_state = "quietus" clan_restricted = TRUE power_type = /datum/discipline_power/quietus diff --git a/modular_darkpack/modules/powers/code/discipline/serpentis.dm b/modular_darkpack/modules/powers/code/discipline/serpentis.dm index 1dc6318e24d0..fa40ff012224 100644 --- a/modular_darkpack/modules/powers/code/discipline/serpentis.dm +++ b/modular_darkpack/modules/powers/code/discipline/serpentis.dm @@ -1,6 +1,11 @@ /datum/discipline/serpentis name = "Serpentis" - desc = "Act like a cobra, get the powers to stun targets with your gaze and your tongue, praise the mummy traditions and spread them to your childe. Violates Masquerade." + desc = {"Act like a cobra, get the powers to stun targets with your gaze and your tongue, praise the mummy traditions and spread them to your childe. Violates Masquerade. +● The Eyes of the Serpent: Passive +●● The Tongue of the Asp: Strength (difficulty 6) +●●● The Skin of the Adder: Passive +●●●● The Form of the Cobra: Passive +●●●●● The Heart of Darkness: Passive"} icon_state = "serpentis" clan_restricted = TRUE power_type = /datum/discipline_power/serpentis diff --git a/modular_darkpack/modules/powers/code/discipline/temporis/temporis.dm b/modular_darkpack/modules/powers/code/discipline/temporis/temporis.dm index 015d780c0978..9ce9fff065f5 100644 --- a/modular_darkpack/modules/powers/code/discipline/temporis/temporis.dm +++ b/modular_darkpack/modules/powers/code/discipline/temporis/temporis.dm @@ -1,6 +1,11 @@ /datum/discipline/temporis name = "Temporis" - desc = "Temporis is a Discipline unique to the True Brujah. Supposedly a refinement of Celerity, Temporis grants the Cainite the ability to manipulate the flow of time itself." + desc = {"Temporis is a Discipline unique to the True Brujah. Supposedly a refinement of Celerity, Temporis grants the Cainite the ability to manipulate the flow of time itself. +● Hourglass of the Mind: Passive +●● Recurring Contemplation: Passive +●●● Leaden Moment: Passive +●●●● Patience of the Norns: Passive +●●●●● Clotho's Gift: Passive"} icon_state = "temporis" clan_restricted = TRUE power_type = /datum/discipline_power/temporis diff --git a/modular_darkpack/modules/powers/code/discipline/thanatosis/thanatosis.dm b/modular_darkpack/modules/powers/code/discipline/thanatosis/thanatosis.dm index 7816cc6884e1..fd0a16f90192 100644 --- a/modular_darkpack/modules/powers/code/discipline/thanatosis/thanatosis.dm +++ b/modular_darkpack/modules/powers/code/discipline/thanatosis/thanatosis.dm @@ -1,6 +1,11 @@ /datum/discipline/thanatosis name = "Thanatosis" - desc = "Offers control over your own rotted body" + desc = {"Offers control over your own rotted body. +● Hag's Wrinkles: Stamina + Subterfuge +●● Putrefaction: Dexterity + Medicine vs. target's Stamina +●●● Ashes to Ashes: Passive +●●●● Withering: Manipulation + Medicine vs. target's Stamina +●●●●● Necrosis: Dexterity + Medicine vs. target's Stamina"} icon_state = "thanatosis" clan_restricted = TRUE power_type = /datum/discipline_power/thanatosis diff --git a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm index 4137ead77690..b790df7aae90 100644 --- a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm +++ b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm @@ -1,6 +1,12 @@ /datum/discipline/thaumaturgy name = "Thaumaturgy" - desc = "Opens the secrets of blood magic and how you use it, allows to steal other's blood. Violates Masquerade." + desc = {"Opens the secrets of blood magic and how you use it, allows to steal other's blood. Violates Masquerade. + Difficulty scales depending on the dots used. +● A Taste for Blood: Permanent Willpower +●● Blood Rage: Permanent Willpower +●●● Blood of Potency: Permanent Willpower +●●●● Theft of Vitae: Permanent Willpower +●●●●● Cauldron of Blood: Permanent Willpower"} icon_state = "thaumaturgy" clan_restricted = TRUE power_type = /datum/discipline_power/thaumaturgy diff --git a/modular_darkpack/modules/powers/code/discipline/valeren.dm b/modular_darkpack/modules/powers/code/discipline/valeren.dm index 97b0535e9d2e..049bf87a9aee 100644 --- a/modular_darkpack/modules/powers/code/discipline/valeren.dm +++ b/modular_darkpack/modules/powers/code/discipline/valeren.dm @@ -1,6 +1,11 @@ /datum/discipline/valeren name = "Valeren" - desc = "The warrior's path of Valeren, used by the Salubri antitribu to read and exploit weakness in their enemies." + desc = {"The warrior's path of Valeren, used by the Salubri antitribu to read and exploit weakness in their enemies. +● Sense Vitality: Perception + Empathy (difficulty 7) +●● Anesthetic Touch: Willpower (difficulty 8 if unwilling) +●●● Burning Touch: Passive +●●●● Armor of Caine's Fury: Stamina + Melee (difficulty 7) +●●●●● Vengeance of Samiel: Passive"} icon_state = "valeren" clan_restricted = TRUE power_type = /datum/discipline_power/valeren diff --git a/modular_darkpack/modules/powers/code/discipline/vicissitude/vicissitude.dm b/modular_darkpack/modules/powers/code/discipline/vicissitude/vicissitude.dm index 65607628b6d9..59c01f28be5d 100644 --- a/modular_darkpack/modules/powers/code/discipline/vicissitude/vicissitude.dm +++ b/modular_darkpack/modules/powers/code/discipline/vicissitude/vicissitude.dm @@ -3,7 +3,12 @@ /datum/discipline/vicissitude name = "Vicissitude" - desc = "It is widely known as Tzimisce art of flesh and bone shaping. Violates Masquerade." + desc = {"It is widely known as Tzimisce art of flesh and bone shaping. Violates Masquerade. +● Malleable Visage: Passive +●● Fleshcrafting: Passive +●●● Bonecrafting: Strength + Medicine (difficulty 7) +●●●● Horrid Form: Passive +●●●●● Bloodform: Passive"} icon_state = "vicissitude" clan_restricted = TRUE power_type = /datum/discipline_power/vicissitude diff --git a/modular_darkpack/modules/powers/code/discipline/vicissitude/zulo.dm b/modular_darkpack/modules/powers/code/discipline/vicissitude/zulo.dm index 27f293fde116..4fbc436206f0 100644 --- a/modular_darkpack/modules/powers/code/discipline/vicissitude/zulo.dm +++ b/modular_darkpack/modules/powers/code/discipline/vicissitude/zulo.dm @@ -49,6 +49,7 @@ GLOBAL_LIST_INIT(zulo_forms, list( /mob/living/basic/zulo/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_UNMASQUERADE, INNATE_TRAIT) + AddComponent(/datum/component/seethrough_mob) /mob/living/basic/zulo/mind_initialize() . = ..() diff --git a/modular_darkpack/modules/powers/code/discipline/visceratika.dm b/modular_darkpack/modules/powers/code/discipline/visceratika.dm index 9c09d7cf1645..894eb7c6c1db 100644 --- a/modular_darkpack/modules/powers/code/discipline/visceratika.dm +++ b/modular_darkpack/modules/powers/code/discipline/visceratika.dm @@ -1,6 +1,11 @@ /datum/discipline/visceratika name = "Visceratika" - desc = "The Discipline of Visceratika is the exclusive possession of the Gargoyle bloodline and is an extension of their natural affinity for stone, earth, and things made thereof." + desc = {"The Discipline of Visceratika is the exclusive possession of the Gargoyle bloodline and is an extension of their natural affinity for stone, earth, and things made thereof. +● Skin of the Chameleon: Passive +●● Scry the Hearthstone: Perception + Awareness +●●● Bond with the Mountain: Passive +●●●● Armor of Terra: No roll - always active +●●●●● Flow Within the Mountain: Passive"} icon_state = "visceratika" clan_restricted = TRUE power_type = /datum/discipline_power/visceratika diff --git a/modular_darkpack/modules/retail/code/stores/black_market.dm b/modular_darkpack/modules/retail/code/stores/black_market.dm index 20ca6c5b2181..adcde9064ea0 100644 --- a/modular_darkpack/modules/retail/code/stores/black_market.dm +++ b/modular_darkpack/modules/retail/code/stores/black_market.dm @@ -11,6 +11,7 @@ new /datum/data/vending_product("LSD pill bottle", /obj/item/storage/pill_bottle/lsd, 50), new /datum/data/vending_product("knife", /obj/item/knife/vamp, 85), new /datum/data/vending_product("switchblade", /obj/item/switchblade/vamp, 85), + new /datum/data/vending_product("knuckledusters", /obj/item/clothing/gloves/vampire/brassknuckles, 100), new /datum/data/vending_product("stake", /obj/item/vampire_stake, 100), new /datum/data/vending_product("Surgery dufflebag", /obj/item/storage/backpack/duffelbag/sec/surgery, 100), new /datum/data/vending_product("Handcuffs", /obj/item/restraints/handcuffs, 50), diff --git a/modular_darkpack/modules/retail/code/stores/clothing_store.dm b/modular_darkpack/modules/retail/code/stores/clothing_store.dm index 1ec31edbcaea..fbe8dc3a7d6d 100644 --- a/modular_darkpack/modules/retail/code/stores/clothing_store.dm +++ b/modular_darkpack/modules/retail/code/stores/clothing_store.dm @@ -96,7 +96,8 @@ new /datum/data/vending_product("blue scarf", /obj/item/clothing/neck/vampire/scarf/blue, 10), new /datum/data/vending_product("green scarf", /obj/item/clothing/neck/vampire/scarf/green, 10), new /datum/data/vending_product("white scarf", /obj/item/clothing/neck/vampire/scarf/white, 10), - new /datum/data/vending_product("cross", /obj/item/card/hunter, 25), + new /datum/data/vending_product("gold cross", /obj/item/card/hunter, 30), + new /datum/data/vending_product("silver cross", /obj/item/card/hunter/silver, 25), new /datum/data/vending_product("duffelbag", /obj/item/storage/backpack/duffelbag, 100), new /datum/data/vending_product("backpack", /obj/item/storage/backpack, 100), new /datum/data/vending_product("satchel", /obj/item/storage/backpack/satchel, 100), diff --git a/modular_darkpack/modules/retail/code/stores/costume_store.dm b/modular_darkpack/modules/retail/code/stores/costume_store.dm index 5bfd7082d2fa..5b8238067708 100644 --- a/modular_darkpack/modules/retail/code/stores/costume_store.dm +++ b/modular_darkpack/modules/retail/code/stores/costume_store.dm @@ -23,4 +23,5 @@ new /datum/data/vending_product("comedy mask", /obj/item/clothing/mask/vampire/comedy, 25), new /datum/data/vending_product("tragedy mask", /obj/item/clothing/mask/vampire/tragedy, 25), new /datum/data/vending_product("shemagh", /obj/item/clothing/mask/vampire/shemagh, 20), + new /datum/data/vending_product("gothic cross", /obj/item/card/hunter/gothic, 20), ) diff --git a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/calling_the_hungry_shade.dm b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/calling_the_hungry_shade.dm index 207c5311bd66..39c55e5a4bd1 100644 --- a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/calling_the_hungry_shade.dm +++ b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/calling_the_hungry_shade.dm @@ -21,7 +21,7 @@ . = ..() var/mob/living/basic/shadow_guard/hungry_shade/shade = new(get_turf(src)) shade.ai_controller = new /datum/ai_controller/basic_controller/simple/simple_hostile(shade) - shade.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, last_activator) + shade.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, last_activator) shade.remove_faction(VAMPIRE_CLAN_LASOMBRA) to_chat(last_activator, span_warning("The ritual slips from your grasp - something answers the call regardless!")) qdel(src) @@ -30,7 +30,7 @@ . = ..() var/mob/living/basic/shadow_guard/hungry_shade/shade = new(get_turf(src)) shade.ai_controller = new /datum/ai_controller/basic_controller/simple/simple_hostile(shade) - shade.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, last_activator) + shade.ai_controller.set_blackboard_key(BB_CURRENT_TARGET, last_activator) shade.remove_faction(VAMPIRE_CLAN_LASOMBRA) to_chat(last_activator, span_warning("You lose control over the ritual!")) last_activator.apply_damage(30, AGGRAVATED) diff --git a/modular_darkpack/modules/ritual_necromancy/code/rituals/locate.dm b/modular_darkpack/modules/ritual_necromancy/code/rituals/locate.dm index fda2ee1584ad..1d39d33ed15c 100644 --- a/modular_darkpack/modules/ritual_necromancy/code/rituals/locate.dm +++ b/modular_darkpack/modules/ritual_necromancy/code/rituals/locate.dm @@ -35,7 +35,7 @@ to_chat(usr, span_ghostalert("This soul yet persists in the Skinlands at [targetarea.name].")) playsound(loc, 'modular_darkpack/modules/ritual_necromancy/sounds/necromancy1on.ogg', 50, FALSE) - if(livetarget.stat > SOFT_CRIT) + if(IS_UNCONSCIOUS(livetarget)) to_chat(usr, span_ghostalert("Their connection to this is realm weak, and fading. Death waits for them.")) if(livetarget.get_discipline(/datum/discipline/necromancy)) //other necromancers catch onto it if targeted var/area/userarea = get_area(usr) diff --git a/modular_darkpack/modules/ritual_thaumaturgy/rituals/gargoyle_transformation.dm b/modular_darkpack/modules/ritual_thaumaturgy/rituals/gargoyle_transformation.dm index a138c3269e6b..c7eaa3c19467 100644 --- a/modular_darkpack/modules/ritual_thaumaturgy/rituals/gargoyle_transformation.dm +++ b/modular_darkpack/modules/ritual_thaumaturgy/rituals/gargoyle_transformation.dm @@ -18,7 +18,7 @@ else if(H.is_clan(/datum/subsplat/vampire_clan/gargoyle)) to_chat(usr, span_warning("You may not use this ritual on a Gargoyle!")) return - else if(H.stat > SOFT_CRIT) + else if(IS_UNCONSCIOUS(H)) valid_bodies += H else H.adjust_agg_loss(50) diff --git a/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm b/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm index b83087902e52..6b655a62c6e0 100644 --- a/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm +++ b/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm @@ -32,6 +32,7 @@ // Applies on_join_round effects when a client logs into this mob if(joining_round) RegisterSignal(gaining_mob, COMSIG_MOB_LOGIN, PROC_REF(on_join_round)) + RegisterSignal(gaining_mob, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED, PROC_REF(apply_after_setup)) /** * Undoes the effects of on_gain more or less @@ -42,7 +43,7 @@ */ /datum/subsplat/proc/on_lose(mob/living/carbon/human/losing_mob) SHOULD_CALL_PARENT(TRUE) - UnregisterSignal(losing_mob, COMSIG_MOB_LOGIN) + UnregisterSignal(losing_mob, list(COMSIG_MOB_LOGIN, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED)) return @@ -52,8 +53,7 @@ * at roundstart. Anything that's not innate * to the subsplat and more part of its social * structure or whatnot should go in here. - * Will teleport Masquerade-breaching Clans to - * safe areas and give them their subsplat keys by + * Will give them their subsplat keys by * default. * * Arguments: @@ -61,7 +61,6 @@ */ /datum/subsplat/proc/on_join_round(mob/living/carbon/human/joining) SIGNAL_HANDLER - SHOULD_CALL_PARENT(TRUE) if(subsplat_keys && check_doors_for_keys && key_has_matching_door(subsplat_keys)) @@ -69,6 +68,19 @@ UnregisterSignal(joining, COMSIG_MOB_LOGIN) +/** + * Applies anything that should only be applied when joining the round through late-start or round-start + * This exludes spawning as a ghost and other such sources. This is for stuff like outfits or teleporting players to locations. + * + * Arguments: + * * source - Human with this subsplat joining the round. + */ +/datum/subsplat/proc/apply_after_setup(mob/living/carbon/human/source) + SIGNAL_HANDLER + SHOULD_CALL_PARENT(TRUE) + + UnregisterSignal(source, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED) + /// Displays description and roleplay level of the subsplat. /datum/subsplat/proc/show_lore(mob/user) if(desc) diff --git a/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm b/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm index e2a0cda9ae7c..fe42637d391b 100644 --- a/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm +++ b/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm @@ -52,6 +52,8 @@ . = ..() if(HAS_TRAIT(roller, TRAIT_RAZOR_CLAWS)) // Your still using claws. A bit homebrew tho. . += 1 + if(HAS_TRAIT(roller, TRAIT_BRASSKNUCKLES)) // Method for giving brass knuckles bonus punch damage. It's blunt, and punch damage is naturally low, so equals out. + . += 2 /datum/storyteller_roll/damage/bite bumper_text = "damage (bite)" diff --git a/modular_darkpack/modules/storyteller_stats/code/_st_stats.dm b/modular_darkpack/modules/storyteller_stats/code/_st_stats.dm index b7cd90e076fa..02f42bf7e6e9 100644 --- a/modular_darkpack/modules/storyteller_stats/code/_st_stats.dm +++ b/modular_darkpack/modules/storyteller_stats/code/_st_stats.dm @@ -26,10 +26,10 @@ /// If the user can spend points on that stat. var/editable = TRUE - /// A dictionary of modifiers to this attribute. - var/list/modifiers = list() - /// A dictionary of auto success scores to this attribute. - var/list/auto_successes = list() + /// LAZYLIST. A dictionary of modifiers to this attribute. + var/list/modifiers + /// LAZYLIST. A dictionary of auto success scores to this attribute. + var/list/auto_successes /// What score does this stat start out with at character creation. var/starting_score = 0 /// How many points are in this stat category that the player can use. Used in abstract classes only. @@ -195,3 +195,6 @@ points -= amount freebie_cost_spent += amount return TRUE + +/datum/st_stat/proc/update_mob(mob/living/our_mob, initial) + return diff --git a/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm b/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm index 4f7ff1e5b93e..d67ab257a4cc 100644 --- a/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm +++ b/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm @@ -58,11 +58,7 @@ /mob/living/proc/update_modifiers_from_stats(initial = FALSE) for(var/stat_typepath in storyteller_stats) var/datum/st_stat/stat_datum = storyteller_stats[stat_typepath] - if(stat_datum.stat_flags & AFFECTS_HEALTH) - recalculate_max_health(initial) - if(stat_datum.stat_flags & AFFECTS_SPEED) - add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/dexterity, multiplicative_slowdown = -(st_get_stat(STAT_DEXTERITY) / 20)) - + stat_datum.update_mob(src, initial) /datum/preferences/proc/apply_stats_from_prefs(mob/living/carbon/human/character) character.storyteller_stats = preference_storyteller_stats.Copy() diff --git a/modular_darkpack/modules/storyteller_stats/code/st_stats/default_attributes.dm b/modular_darkpack/modules/storyteller_stats/code/st_stats/default_attributes.dm index 194cb87643c3..7069f15513aa 100644 --- a/modular_darkpack/modules/storyteller_stats/code/st_stats/default_attributes.dm +++ b/modular_darkpack/modules/storyteller_stats/code/st_stats/default_attributes.dm @@ -3,17 +3,24 @@ description = "Affects your unarmed attack damage multiplier. Increases your chances to knock down an opponent in unarmed combat." subcategory = "Physical" + /datum/st_stat/attribute/dexterity name = "Dexterity" description = "Affects your speed and melee weapon accuracy. Increases your defense against being knocked down in unarmed combat. Increases the speed of certain actions." subcategory = "Physical" - stat_flags = AFFECTS_SPEED + +/datum/st_stat/attribute/dexterity/update_mob(mob/living/our_mob, initial) + our_mob.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/dexterity, multiplicative_slowdown = -(get_score() / 20)) + /datum/st_stat/attribute/stamina name = "Stamina" description = "Affects your maximum health. Used in Quietus." subcategory = "Physical" - stat_flags = AFFECTS_HEALTH + +/datum/st_stat/attribute/stamina/update_mob(mob/living/our_mob, initial) + our_mob.recalculate_max_health(initial) + /datum/st_stat/attribute/charisma name = "Charisma" @@ -30,6 +37,12 @@ description = "A measure of how well a character makes a first impression. Used in social disciplines and makes your character more attractive." subcategory = "Social" +/* For the bimbo in the audience +/datum/st_stat/attribute/appearance/update_mob(mob/living/our_mob, initial) + update_bloodquality_from_appearance() +*/ + + /datum/st_stat/attribute/perception name = "Perception" description = "A character's ability to observe their environment. Increases your examine speed. Used in Auspex." diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/give_vitae.dm b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/give_vitae.dm index 545fb6d43042..99ac636da7b7 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/give_vitae.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/give_vitae.dm @@ -40,7 +40,7 @@ owner.visible_message( span_danger("[owner] starts pouring blood into \the [target][ismob(target) ? "'s mouth" : null]!"), span_info("You start pouring blood into \the [target][ismob(target) ? "'s mouth" : null].")) - if(!do_after(owner, delay = charge_duration, target = target, icon = 'modular_darkpack/modules/vampire_the_masquerade/icons/vitae.dmi', iconstate = "vitae")) + if(!do_after(owner, delay = charge_duration, target = target, cog_icon = 'modular_darkpack/modules/vampire_the_masquerade/icons/vitae.dmi', cog_iconstate = "vitae")) return FALSE var/mob/living/carbon/vamp = owner @@ -81,7 +81,7 @@ if(!length(discipline_entries)) return FALSE - if(!student || student.stat != CONSCIOUS) + if(!student || IS_UNCONSCIOUS_OR_CRIT(student)) owner.balloon_alert(owner,"they must be conscious!") return FALSE @@ -98,10 +98,10 @@ owner.visible_message( span_danger("[owner] starts pouring blood into [student]'s mouth!"), span_info("You start pouring blood into [student]'s mouth.")) - if(!do_after(owner, delay = 1 MINUTES, target = student, icon = 'modular_darkpack/modules/vampire_the_masquerade/icons/vitae.dmi', iconstate = "vitae")) + if(!do_after(owner, delay = 1 MINUTES, target = student, cog_icon = 'modular_darkpack/modules/vampire_the_masquerade/icons/vitae.dmi', cog_iconstate = "vitae")) return FALSE - if(student.stat != CONSCIOUS) + if(IS_UNCONSCIOUS_OR_CRIT(student)) return FALSE var/mob/living/carbon/vamp = owner diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm b/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm index 8413d855710e..ecfe699d3b1a 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm @@ -71,7 +71,7 @@ RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(handle_lose_organ)) //vampires don't die while in crit, they just slip into torpor after 2 minutes of being critted - RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(handle_enter_critical_condition)) + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(handle_enter_critical_condition)) //vampires resist vampire bites better than mortals RegisterSignal(owner, COMSIG_MOB_VAMPIRE_SUCKED, PROC_REF(on_vampire_bitten)) @@ -109,7 +109,7 @@ UnregisterSignal(owner, list( COMSIG_CARBON_LOSE_ORGAN, - SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), + COMSIG_MOB_STATCHANGE, COMSIG_MOB_VAMPIRE_SUCKED, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, COMSIG_HUMAN_ON_HANDLE_BLOOD, @@ -188,8 +188,10 @@ source.death() -/datum/splat/vampire/kindred/proc/handle_enter_critical_condition(mob/living/carbon/human/source) +/datum/splat/vampire/kindred/proc/handle_enter_critical_condition(mob/living/carbon/human/source, new_stat, old_stat) SIGNAL_HANDLER + if(new_stat < SOFT_CRIT) + return to_chat(source, span_warning("You can feel yourself slipping into Torpor. You can use succumb to immediately sleep...")) addtimer(CALLBACK(src, PROC_REF(slip_into_torpor), source), 2 MINUTES) @@ -197,7 +199,7 @@ /datum/splat/vampire/kindred/proc/slip_into_torpor(mob/living/carbon/human/kindred) if (!kindred || (kindred.stat == DEAD)) return - if (kindred.stat < SOFT_CRIT) + if (!IS_UNCONSCIOUS_OR_CRIT(kindred)) return kindred.torpor(DAMAGE_TRAIT) diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/__vampire_clan.dm b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/__vampire_clan.dm index 92373f6cc395..1b3d5652bd9a 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/__vampire_clan.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/__vampire_clan.dm @@ -91,20 +91,29 @@ losing_mob.remove_faction(id) -/datum/subsplat/vampire_clan/on_join_round(mob/living/carbon/human/joining) +/datum/subsplat/vampire_clan/apply_after_setup(mob/living/carbon/human/source) . = ..() - if (HAS_TRAIT(joining, TRAIT_MASQUERADE_VIOLATING_FACE)) + // if they spawn with the masquerade violating face trait, give them the things to cover up so they aren't stuck in the sewer. + if (HAS_TRAIT(source, TRAIT_MASQUERADE_VIOLATING_FACE) || HAS_TRAIT(source, TRAIT_MASQUERADE_VIOLATING_EYES)) if (length(GLOB.masquerade_latejoin)) - var/obj/effect/landmark/latejoin_masquerade/LM = pick(GLOB.masquerade_latejoin) - if (LM) - joining.forceMove(get_turf(LM)) - // if they spawn with the masquerade violating face trait, give them the things to cover up so they aren't stuck in the sewer. - var/obj/item/clothing/suit/hooded/robes/darkred/new_robe = new(joining.loc) - joining.equip_to_appropriate_slot(new_robe, FALSE) - - var/obj/item/clothing/mask/vampire/venetian_mask/fancy/new_mask = new(joining.loc) - joining.equip_to_appropriate_slot(new_mask, FALSE) + var/turf/join_spot = pick(GLOB.masquerade_latejoin) + if(join_spot) + source.forceMove(join_spot) + + if(HAS_TRAIT(source, TRAIT_MASQUERADE_VIOLATING_FACE) && !(source.obscured_slots & HIDEFACE)) + var/obj/item/clothing/mask/vampire/venetian_mask/fancy/new_mask = new(source.loc) + source.equip_to_appropriate_slot(new_mask, FALSE) + if(HAS_TRAIT(source, TRAIT_MASQUERADE_VIOLATING_EYES) && !source.is_eyes_covered()) + var/obj/item/clothing/glasses/vampire/sun/new_glasses = new(source.loc) + source.equip_to_appropriate_slot(new_glasses, FALSE) + + // We dont have masquerade breaching body. YET. + /* + var/obj/item/clothing/suit/hooded/robes/darkred/new_robe = new(source.loc) + source.equip_to_appropriate_slot(new_robe, FALSE) + */ + /// effect from daimonion psychomania /datum/subsplat/vampire_clan/proc/psychomania_effect(mob/living/target, mob/living/owner) diff --git a/modular_darkpack/modules/weapons/icons/worn_melee.dmi b/modular_darkpack/modules/weapons/icons/worn_melee.dmi index 4e4969ed9988..97b298b94583 100644 Binary files a/modular_darkpack/modules/weapons/icons/worn_melee.dmi and b/modular_darkpack/modules/weapons/icons/worn_melee.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/banes/suffocating.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/banes/suffocating.dm index e935f9d81399..33b6b3a38b48 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/banes/suffocating.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/banes/suffocating.dm @@ -4,28 +4,10 @@ maxHealth = 50 health = 50 - ai_controller = /datum/ai_controller/basic_controller/suffocating_bane - mob_size = MOB_SIZE_SMALL -/datum/ai_controller/basic_controller/suffocating_bane - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/random_speech/suffocating_bane - ) - - -/datum/ai_planning_subtree/random_speech/suffocating_bane +/* Cant be asked to setup a ai controller for 1 random speech. +/datum/bt_node/ai_behavior/random_speech/suffocating_bane speech_chance = 15 speak = list("gasps") sound = list( @@ -35,3 +17,4 @@ 'sound/mobs/humanoids/human/gasp/gasp_male1.ogg', 'sound/mobs/humanoids/human/gasp/gasp_male2.ogg', ) +*/ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/_bird.dm similarity index 64% rename from modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird.dm rename to modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/_bird.dm index ae252e62ff10..e650f726d3a4 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/_bird.dm @@ -39,13 +39,17 @@ /mob/living/basic/corvid/Initialize(mapload) . = ..() add_verb(src, /mob/living/proc/toggle_resting) - var/datum/action/innate/togglecorvidflight/toggleflight = new() - toggleflight.Grant(src) - AddElement(/datum/element/ai_retaliate) + GRANT_ACTION(/datum/action/innate/togglecorvidflight) + AddElement(/datum/element/ai_retaliate) // Birds dont forget AddElement(/datum/element/pet_bonus, "caw") AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) + AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) update_appearance(UPDATE_ICON) + // For bird crime (theft). + ai_controller.set_blackboard_key(BB_HOARD_LOCATION, get_turf(src)) + AddElement(/datum/element/dextrous, can_throw = TRUE, hands_count = 1) + /mob/living/basic/corvid/update_icon_state() . = ..() if(stat == DEAD) @@ -90,13 +94,67 @@ // sadly, "is flying animal" does not give us flying traits when life() is called, only during VV or upon Init. We're doing this the hard way. // the corax sprites already animate up-and-down bobbing, no need to float corvid.add_traits(list(TRAIT_MOVE_FLYING, TRAIT_NO_FLOATING_ANIM), ACTION_TRAIT) + corvid.remove_traits(list(TRAIT_WADDLING), INNATE_TRAIT) // we set this while we wait for the icons to update, otherwise there is latency else to_chat(corvid, span_notice("You settle gently back onto the ground...")) corvid.remove_traits(list(TRAIT_MOVE_FLYING, TRAIT_NO_FLOATING_ANIM), ACTION_TRAIT) + corvid.AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) corvid.update_icon(UPDATE_ICON) + +/// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly. +/// This is pretty much meant for players, AI will use the task-specific procs instead. +/mob/living/basic/corvid/early_melee_attack(atom/target, list/modifiers, ignore_cooldown) + . = ..() + if(.) + return + + if(iscarbon(target) && steal_from_mob(target)) + return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN + +/// Picks up an item from the ground and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. +/mob/living/basic/corvid/put_in_hand_check(obj/item/item_to_pick_up) + if(item_to_pick_up.w_class > WEIGHT_CLASS_SMALL) + balloon_alert(src, "too big to pick up!") + return FALSE + + return ..() + +/mob/living/basic/corvid/put_in_hand(obj/item/target, hand_index, forced = FALSE, ignore_anim = TRUE, visuals_only = FALSE) + if(..()) + visible_message( + span_notice("[src] grabs [target]!"), + span_notice("You grab [target]!"), + span_hear("You hear the sounds of wings flapping furiously."), + ) + return TRUE + +/// Looks for an item that we can snatch and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. +/mob/living/basic/corvid/proc/steal_from_mob(mob/living/carbon/victim) + if(!isnull(get_active_held_item())) + balloon_alert(src, "already holding something!") + return FALSE + + for(var/obj/item/stealable in victim.held_items) + if(stealable.w_class > WEIGHT_CLASS_SMALL) + continue + + if(!victim.temporarilyRemoveItemFromInventory(stealable)) + continue + + dropItemToGround(stealable) + visible_message( + span_notice("[src] wrestles [stealable] out of [victim]'s hand!"), + span_notice("You wrestles [stealable] out of [victim]'s hand!"), + span_hear("You hear the sounds of wings flapping furiously."), + ) + return TRUE + + return FALSE + + /datum/emote/corvid mob_type_allowed_typecache = /mob/living/basic/corvid mob_type_blacklist_typecache = list() @@ -117,6 +175,7 @@ name = "raven" desc = "Unlike a crow, it has a wedge shaped tail." verb_say = "gronks" + verb_exclaim = "gronks" speak_emote = list("gronks") /mob/living/basic/corvid/raven/Initialize(mapload) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird_ai.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/bird_ai.dm similarity index 63% rename from modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird_ai.dm rename to modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/bird_ai.dm index c627732fbddf..11dfc4df0dbe 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/bird_ai.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/bird_ai.dm @@ -1,26 +1,26 @@ /datum/ai_controller/basic_controller/corvid + behavior_tree_json = "modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.json" blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, + BB_HOARD_LOCATION_RANGE = 12, ) - ai_traits = PASSIVE_AI_FLAGS ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/random_speech/corvid, - /datum/ai_planning_subtree/find_and_hunt_target/find_shiney, - ) -/datum/ai_planning_subtree/random_speech/corvid +/datum/bt_node/ai_behavior/random_speech/corvid speech_chance = 5 speak = list("Caw!") sound = list('modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/caw.ogg') emote_hear = list("Caws.") -/datum/ai_planning_subtree/find_and_hunt_target/find_shiney + +/// Use spawnpoint as nest, carry loot home, then go steal more. +/datum/bt_node/subtree/corvid_hoard + behavior_tree_json = "modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.json" + +/* +/datum/bt_node/subtree/find_and_hunt_target/find_shiney target_key = BB_LOW_PRIORITY_HUNTING_TARGET hunting_behavior = /datum/ai_behavior/hunt_target/find_shiney finding_behavior = /datum/ai_behavior/find_hunt_target/find_shiney @@ -36,3 +36,4 @@ /datum/ai_behavior/hunt_target/find_shiney/target_caught(mob/living/hunter, obj/item/shiney) hunter.start_pulling(shiney) +*/ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.json b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.json new file mode 100644 index 000000000000..c829e0023909 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid.bt.json @@ -0,0 +1,171 @@ +{ + "dm_type": "/datum/ai_controller/basic_controller/corvid", + "type": "selector", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_contained_in_obj", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/container_attackable", + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/break_out_of_object/from_bb", + "vars": { + "target_key": "BB_BASIC_MOB_ESCAPE_TARGET" + } + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_grabbed_by_enemy", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_is_restrained", + "vars": { + "observer_abort": "BT_ABORT_LOWER_PRIORITY" + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + ] + }, + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree", + "override_id": "SUBPLAN_ID_PET_COMMAND" + }, + { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_CHILD_ONE", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "repeat_secondary_delay": "BASIC_MOB_FIND_TARGET_RATE", + "finish_on_primary": true, + "children": [ + { + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_BOTH", + "key": "BB_CURRENT_TARGET" + }, + "child": { + "type": "parallel", + "failure_policy": "BT_PARALLEL_FAILURE_ANY", + "success_policy": "BT_PARALLEL_SUCCESS_CHILD_ONE", + "repeat_secondary": true, + "finish_on_primary": true, + "children": [ + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_LOOP_ON_FAILURE", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "required_dist": 1, + "finish_on_arrival": false + } + } + ] + } + }, + { + "type": "subplan", + "success_policy": "BT_SUBPLAN_LOOP_ON_SUCCESS", + "failure_policy": "BT_SUBPLAN_FAIL_ON_FAILURE", + "loop_delay": "IDLE_BEHAVIOR_RATE", + "children": [ + { + "type": "selector", + "children": [ + { + "type": "subtree", + "subtype": "/datum/bt_node/subtree/corvid_hoard" + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/idle_random_walk" + } + ] + } + ] + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/target_from_retaliate_list", + "vars": { + "target_key": "BB_CURRENT_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/random_speech/corvid" + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/pawn_buckled_to_obj", + "child": { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/resist" + } + } + } + ] + } + ] +} diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.json b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.json new file mode 100644 index 000000000000..0e11f57d2f09 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/corvid/corvid_hoard.bt.json @@ -0,0 +1,88 @@ +{ + "dm_type": "/datum/bt_node/subtree/parrot_hoard", + "type": "selector", + "children": [ + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/target_is_holding_item", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_MY_PAWN" + }, + "child": { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HOARD_LOCATION", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/drop_all_held_items" + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/bb_key_set", + "vars": { + "observer_abort": "BT_ABORT_SELF", + "key": "BB_HOARD_ITEM_TARGET" + }, + "child": { + "type": "selector", + "children": [ + { + "type": "sequence", + "children": [ + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/move_to_target", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET", + "required_dist": 1 + } + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/basic_melee_attack/interact_once/parrot", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET", + "targeting_strategy": "BB_TARGETING_STRATEGY", + "hiding_location_key": "BB_CURRENT_TARGET_HIDING_LOCATION" + } + } + ] + }, + { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/clear_key", + "vars": { + "key": "BB_HOARD_ITEM_TARGET" + } + } + ] + } + }, + { + "type": "decorator", + "decorator": "/datum/bt_node/decorator/random_chance", + "vars": { + "chance": 0.05 + }, + "child": { + "type": "leaf", + "behavior": "/datum/bt_node/ai_behavior/acquire_target/parrot_hoard_item", + "vars": { + "target_key": "BB_HOARD_ITEM_TARGET", + "vision_range": 12 + } + } + } + ] +} diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/wolf.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/wolf.dm index a51a8c82a759..c887c472e272 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/wolf.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/basic_mobs/wolf.dm @@ -143,7 +143,7 @@ . += damage_overlay /mob/living/basic/pet/dog/wolf/proc/wolfresting() - return stat > CONSCIOUS || IsSleeping() || IsParalyzed() || body_position == LYING_DOWN + return IS_UNCONSCIOUS_OR_CRIT(src) || IsSleeping() || IsParalyzed() || body_position == LYING_DOWN // WOLF TYPES /mob/living/basic/pet/dog/wolf/kinfolk diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm index f03b9f164040..0f4f8e558446 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm @@ -153,7 +153,7 @@ var/mob/living/carbon/human/clicker = usr if(!istype(clicker)) return - if(clicker.stat >= UNCONSCIOUS) + if(IS_UNCONSCIOUS(clicker)) return var/datum/splat/werewolf/shifter/shifting = get_shifter_splat(clicker) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm index 0d5bd0396e56..a826b6606d4a 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm @@ -46,7 +46,7 @@ /datum/action/change_apparel/Activate(atom/target) . = ..() var/mob/living/carbon/werewolf/crinos/C = owner - if(C.stat == CONSCIOUS) + if(!IS_UNCONSCIOUS_OR_CRIT(C)) if(C.sprite_apparel == 4) C.sprite_apparel = 0 else diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/species/fera_species.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/fera_species.dm index aadc5c6793a5..90ed73d3c5c8 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/species/fera_species.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/fera_species.dm @@ -322,12 +322,15 @@ if(HAS_TRAIT(human_who_gained_species, TRAIT_FERA_FLIGHT)) var/datum/action/innate/toggle_fera_flight/ability = new(human_who_gained_species) ability.Grant(human_who_gained_species) + human_who_gained_species.AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) /datum/species/human/shifter/feral/on_species_loss(mob/living/carbon/human/human, datum/species/new_species, pref_load) . = ..() for(var/datum/action/innate/toggle_fera_flight/action in human.actions) action.Remove(human) + if(HAS_TRAIT(human, TRAIT_FERA_FLIGHT)) + REMOVE_TRAIT(human, TRAIT_WADDLING, INNATE_TRAIT) /datum/movespeed_modifier/shifter abstract_type = /datum/movespeed_modifier/shifter diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/species/flight_action.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/flight_action.dm index 527a86e934aa..89fe7f43a8e3 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/species/flight_action.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/flight_action.dm @@ -16,9 +16,14 @@ if (!(HAS_TRAIT(fera_mob, TRAIT_MOVE_FLYING))) to_chat(fera_mob, span_notice("You beat your wings and begin to hover gently above the ground...")) fera_mob.add_traits(list(TRAIT_MOVE_FLYING, TRAIT_NO_FLOATING_ANIM), ACTION_TRAIT) + fera_mob.remove_traits(list(TRAIT_WADDLING), INNATE_TRAIT) + playsound(fera_mob, 'modular_darkpack/modules/external_organs/sounds/wing_flap_flying.ogg', 50, TRUE) else to_chat(fera_mob, span_notice("You settle gently back onto the ground...")) fera_mob.remove_traits(list(TRAIT_MOVE_FLYING, TRAIT_NO_FLOATING_ANIM), ACTION_TRAIT) + if(!HAS_TRAIT(fera_mob, TRAIT_WADDLING)) + fera_mob.AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) + playsound(fera_mob, 'modular_darkpack/modules/external_organs/sounds/wing_flap_flying.ogg', 50, TRUE) fera_mob.update_body_parts() // fera_mob.update_icon(UPDATE_ICON) @@ -29,6 +34,9 @@ return to_chat(fera_mob, span_notice("You settle gently back onto the ground...")) fera_mob.remove_traits(list(TRAIT_MOVE_FLYING, TRAIT_NO_FLOATING_ANIM), ACTION_TRAIT) + if(!HAS_TRAIT(fera_mob, TRAIT_WADDLING)) + fera_mob.AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) + playsound(fera_mob, 'modular_darkpack/modules/external_organs/sounds/wing_flap_flying.ogg', 50, TRUE) fera_mob.update_body_parts() diff --git a/modular_darkpack/modules/z_travel/code/helpers.dm b/modular_darkpack/modules/z_travel/code/helpers.dm index 33163676331d..9af6609b6006 100644 --- a/modular_darkpack/modules/z_travel/code/helpers.dm +++ b/modular_darkpack/modules/z_travel/code/helpers.dm @@ -1,16 +1,13 @@ // Copypasta of get_z_move_affected /// Returns a list of movables that should also be affected when src moves through teleporters, and src. -/atom/movable/proc/get_teleport_move_affected() - . = list(src) +/atom/movable/proc/get_teleport_move_affected(list/returning_list = list()) + . = returning_list + . |= src if(buckled_mobs) . |= buckled_mobs for(var/mob/living/buckled as anything in buckled_mobs) if(buckled.pulling) . |= buckled.pulling - if(pulling) - . |= pulling - if (pulling.buckled_mobs) - . |= pulling.buckled_mobs - - if (pulling.pulling) - . |= pulling.pulling.get_teleport_move_affected() + //we pass in the list from this proc to ensure we dont reach an infinite loop due to mobs grabbed in a loop or two mobs grabing eachother. + if(pulling && !(pulling in .)) + . |= pulling.get_teleport_move_affected(.) diff --git a/modular_zapoc/modules/gray_masses/code/tall_gray_man.dm b/modular_zapoc/modules/gray_masses/code/tall_gray_man.dm index cc5d6667777a..5f40dbabe623 100644 --- a/modular_zapoc/modules/gray_masses/code/tall_gray_man.dm +++ b/modular_zapoc/modules/gray_masses/code/tall_gray_man.dm @@ -4,7 +4,6 @@ desc = "A towering skeleton, embodying the terrifying power of the Gray Masses." icon = 'modular_zapoc/modules/gray_masses/icons/gray_masses_tall.dmi' loot_drop = list(/obj/effect/spawner/random/occult/artifact) - stat_attack = CONSCIOUS attack_action_types = list( /datum/action/innate/elite_attack/legionnaire_charge, /datum/action/innate/elite_attack/head_detach, diff --git a/sound/effects/footstep/concrete1.ogg b/sound/effects/footstep/concrete1.ogg new file mode 100644 index 000000000000..1df1955bb338 Binary files /dev/null and b/sound/effects/footstep/concrete1.ogg differ diff --git a/sound/effects/footstep/concrete2.ogg b/sound/effects/footstep/concrete2.ogg new file mode 100644 index 000000000000..941e140941b8 Binary files /dev/null and b/sound/effects/footstep/concrete2.ogg differ diff --git a/sound/effects/footstep/concrete3.ogg b/sound/effects/footstep/concrete3.ogg new file mode 100644 index 000000000000..cf12cc2be2c2 Binary files /dev/null and b/sound/effects/footstep/concrete3.ogg differ diff --git a/sound/effects/footstep/concrete4.ogg b/sound/effects/footstep/concrete4.ogg new file mode 100644 index 000000000000..0319e910e2e6 Binary files /dev/null and b/sound/effects/footstep/concrete4.ogg differ diff --git a/sound/effects/footstep/concrete5.ogg b/sound/effects/footstep/concrete5.ogg new file mode 100644 index 000000000000..77b9d385055f Binary files /dev/null and b/sound/effects/footstep/concrete5.ogg differ diff --git a/sound/machines/gravgen/grav_gen_mid2.ogg b/sound/machines/gravgen/grav_gen_mid2.ogg deleted file mode 100644 index 92d026f74af7..000000000000 Binary files a/sound/machines/gravgen/grav_gen_mid2.ogg and /dev/null differ diff --git a/sound/machines/sm/loops/calm.ogg b/sound/machines/sm/loops/calm.ogg index 49d56170d2eb..1f5d4d047e63 100644 Binary files a/sound/machines/sm/loops/calm.ogg and b/sound/machines/sm/loops/calm.ogg differ diff --git a/sound/machines/sm/loops/calm_end.ogg b/sound/machines/sm/loops/calm_end.ogg new file mode 100644 index 000000000000..9b9f2041a45b Binary files /dev/null and b/sound/machines/sm/loops/calm_end.ogg differ diff --git a/sound/machines/sm/loops/calm_start.ogg b/sound/machines/sm/loops/calm_start.ogg new file mode 100644 index 000000000000..d9e2b3c2eb0e Binary files /dev/null and b/sound/machines/sm/loops/calm_start.ogg differ diff --git a/strings/antagonist_flavor/heretic_influences.json b/strings/antagonist_flavor/heretic_influences.json index 0b15c074a635..73ebf1ccaf8c 100644 --- a/strings/antagonist_flavor/heretic_influences.json +++ b/strings/antagonist_flavor/heretic_influences.json @@ -80,5 +80,13 @@ "We don't deserve this... do we?", "Are we prepared for this?", "We thought we were predators, but we are prey." + ], + "hypnosis": [ + "I AM BEING WATCHED. FROM EVERYWHERE. THEY MUST BE BLINDED.", + "I CAN SEE EVERYTHING. TOO MUCH. MY EYES ARE TOO OPEN, TOO WEAK.", + "I CAN SEE THE FUTURE. DREAD COMES. IT MUST BE STOPPED.", + "WE ARE SO INSIGNIFICANT. I MUST BE GREATER THAN THIS.", + "ARMS, WEAPONS, TOOLS, I NEED THEM ALL. I MUST BE PREPARED.", + "THEY ARE COMING. THEY ARE COMING FOR ME. I MUST BE READY." ] } diff --git a/strings/fishing_tips.txt b/strings/fishing_tips.txt index 12c6ae3f9a6c..cacfbc493979 100644 --- a/strings/fishing_tips.txt +++ b/strings/fishing_tips.txt @@ -5,7 +5,7 @@ You could buy fish dirt cheap from the black market, but you probably won't get Rescue hooks can be used to snag in other people. Good for getting husks out of plasma rivers. Pufferfish are known for the slow yet lethal poison they store inside their liver. Magnet hooks are great to fish anything that's NOT a fish. -You can perform scanning experiments with an experi-scanner and some fish. They'll give sci some extra techweb points and get you more modules for the fishing portal generator. +You can perform scanning experiments with an experi-scanner and some fish. They'll give sci some extra techweb points and get you more modules for the fishing portal generator. Fish scanning experiments can be automatically performed with an advanced fishing rod. However to print one you need to have at least completed the first one. Advanced fishing rods come pre-equipped with a unexhaustible bait that ignores bait preferences of each fish, letting you catch'em all. Don't expect to be able to catch a lot of fish without a bait. @@ -53,7 +53,6 @@ Got a bioelectricity generator aquarium but no electrogenic fish? Worry not, you Fish can grow in size and weight if you fed them somewhat frequently. Giving them growth serum (from fly amanita) will also boost the rate at which they grow. Feeding a fish mutagen can triple the probability of generating evolved offsprings, provided it has an evolution. You can print fishing rods of different materials from an autolathe, which can inrease or decrease fishing difficulty, casting range, experience gained and can have other, special effects. -Albeit scarcely, it's possible to catch fish made of the same materials of a custom material fishing rod. Equipping a shiny fishing hook and the quality of the bait can improve your odds. You can use a fishing rod to snatch random organs during the "manipulate organs" step of the "organ manipulation" surgery. You can try to save cash on vending machines by using a fishing rod with a coin, holochip or bill attached to it. The value of the currency used as bait will reduce difficulty and risks and raise the odds of getting more expensive items. By opening the aquarium panel and turning "Safe Mode" on, you can easily set up a purely decorative aquarium without having to worry about food, temperature and type of water. diff --git a/tgstation.dme b/tgstation.dme index c0bdda7d0859..4c46ae943d84 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -288,6 +288,7 @@ #include "code\__DEFINES\zoom.dm" #include "code\__DEFINES\ai\ai.dm" #include "code\__DEFINES\ai\ai_blackboard.dm" +#include "code\__DEFINES\ai\behavior_trees.dm" #include "code\__DEFINES\ai\bot_keys.dm" #include "code\__DEFINES\ai\carp.dm" #include "code\__DEFINES\ai\haunted.dm" @@ -438,6 +439,7 @@ #include "code\__DEFINES\~darkpack\banning.dm" #include "code\__DEFINES\~darkpack\beastmaster.dm" #include "code\__DEFINES\~darkpack\blooper.dm" +#include "code\__DEFINES\~darkpack\book_sources.dm" #include "code\__DEFINES\~darkpack\branding.dm" #include "code\__DEFINES\~darkpack\colors.dm" #include "code\__DEFINES\~darkpack\combat.dm" @@ -479,6 +481,7 @@ #include "code\__DEFINES\~darkpack\vampire_clan.dm" #include "code\__DEFINES\~darkpack\walls.dm" #include "code\__DEFINES\~darkpack\xp_and_freebie.dm" +#include "code\__DEFINES\~darkpack\ai\ai_blackboard.dm" #include "code\__DEFINES\~darkpack\dcs\beastmaster_signals.dm" #include "code\__DEFINES\~darkpack\dcs\bouncer_signals.dm" #include "code\__DEFINES\~darkpack\dcs\fire_signals.dm" @@ -649,7 +652,7 @@ #include "code\_globalvars\darkpack\lists\splats.dm" #include "code\_globalvars\lists\achievements.dm" #include "code\_globalvars\lists\ambience.dm" -#include "code\_globalvars\lists\basic_ai.dm" +#include "code\_globalvars\lists\bitrunning.dm" #include "code\_globalvars\lists\canisters.dm" #include "code\_globalvars\lists\cargo.dm" #include "code\_globalvars\lists\client.dm" @@ -772,7 +775,7 @@ #include "code\controllers\subsystem\achievements.dm" #include "code\controllers\subsystem\admin_verbs.dm" #include "code\controllers\subsystem\ai_controllers.dm" -#include "code\controllers\subsystem\ai_idle_controllers.dm" +#include "code\controllers\subsystem\ai_controllers_low_priority.dm" #include "code\controllers\subsystem\air.dm" #include "code\controllers\subsystem\ambience.dm" #include "code\controllers\subsystem\area_contents.dm" @@ -856,8 +859,6 @@ #include "code\controllers\subsystem\transport.dm" #include "code\controllers\subsystem\tts.dm" #include "code\controllers\subsystem\tutorials.dm" -#include "code\controllers\subsystem\unplanned_ai_idle_controllers.dm" -#include "code\controllers\subsystem\unplanned_controllers.dm" #include "code\controllers\subsystem\verb_manager.dm" #include "code\controllers\subsystem\verbs.dm" #include "code\controllers\subsystem\vis_overlays.dm" @@ -901,8 +902,6 @@ #include "code\controllers\subsystem\persistence\trophy_fishes.dm" #include "code\controllers\subsystem\processing\acid.dm" #include "code\controllers\subsystem\processing\ai_basic_avoidance.dm" -#include "code\controllers\subsystem\processing\ai_behaviors.dm" -#include "code\controllers\subsystem\processing\ai_idle_behaviors.dm" #include "code\controllers\subsystem\processing\antag_hud.dm" #include "code\controllers\subsystem\processing\aura.dm" #include "code\controllers\subsystem\processing\clock_component.dm" @@ -926,6 +925,7 @@ #include "code\controllers\subsystem\processing\singulo.dm" #include "code\controllers\subsystem\processing\station.dm" #include "code\controllers\subsystem\processing\supermatter_cascade.dm" +#include "code\controllers\subsystem\processing\timed_actions.dm" #include "code\controllers\subsystem\processing\turrets.dm" #include "code\controllers\subsystem\processing\wet_floors.dm" #include "code\datums\alarm.dm" @@ -978,6 +978,7 @@ #include "code\datums\station_integrity.dm" #include "code\datums\stock_market_events.dm" #include "code\datums\tgs_event_handler.dm" +#include "code\datums\timed_actions.dm" #include "code\datums\verb_callbacks.dm" #include "code\datums\verb_metadata.dm" #include "code\datums\view.dm" @@ -1044,113 +1045,235 @@ #include "code\datums\actions\mobs\sequences\dash_attack.dm" #include "code\datums\actions\mobs\sequences\projectile.dm" #include "code\datums\ai\_ai_behavior.dm" +#include "code\datums\ai\_ai_bt_composites.dm" +#include "code\datums\ai\_ai_bt_decorators.dm" +#include "code\datums\ai\_ai_bt_node.dm" +#include "code\datums\ai\_ai_bt_subtree.dm" #include "code\datums\ai\_ai_controller.dm" -#include "code\datums\ai\_ai_planning_subtree.dm" #include "code\datums\ai\_item_behaviors.dm" +#include "code\datums\ai\bt_viewer.dm" #include "code\datums\ai\telegraph_effects.dm" #include "code\datums\ai\babies\babies_behaviors.dm" -#include "code\datums\ai\babies\babies_subtrees.dm" -#include "code\datums\ai\bane\bane_behaviors.dm" #include "code\datums\ai\bane\bane_controller.dm" -#include "code\datums\ai\bane\bane_subtrees.dm" #include "code\datums\ai\basic_mobs\admin_ai_templates.dm" #include "code\datums\ai\basic_mobs\base_basic_controller.dm" #include "code\datums\ai\basic_mobs\generic_controllers.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\befriend_target.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\clear_key.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\climb_tree.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\emote_with_target.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\call_reinforcements.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_flee_location.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_parent.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\interact_with_target.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targeting.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\pick_up_item.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\pull_target.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\run_away_from_target.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\set_travel_destination.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\step_towards_turf.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\play_dead.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\stop_and_stare.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeted_mob_ability.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\unbuckle_mob.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\use_mob_ability.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\wounded_targeting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\write_on_paper.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\attack_adjacent_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\call_reinforcements.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\capricious_retaliate.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\climb_tree.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\drag_items.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\enrage.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\escape_captivity.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\express_happiness.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_paper_and_write.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\find_parent.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\find_targets_prioritize_traits.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\fishing.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\generic_hunger.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\generic_play_instrument.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\go_for_swim.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\maintain_distance.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\mine_walls.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\move_to_cardinal.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\opportunistic_ventcrawler.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\play_with_owners.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\prepare_travel_to_destination.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\ranged_skirmish.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\run_emote.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\shapechange_ambush.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_wounded_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\sleep_with_no_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\speech_subtree.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\stare_at_thing.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\targeted_mob_ability.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\teleport_away_from_target.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\tipped_subtree.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\travel_to_point.dm" -#include "code\datums\ai\basic_mobs\basic_subtrees\use_mob_ability.dm" -#include "code\datums\ai\basic_mobs\pet_commands\fetch.dm" -#include "code\datums\ai\basic_mobs\pet_commands\pet_command_planning.dm" -#include "code\datums\ai\basic_mobs\pet_commands\pet_follow_friend.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\random_speech_loop.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\run_away_from_target.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\skittish_brawler_combat.dm" +#include "code\datums\ai\basic_mobs\pet_commands\pet_command_bt.dm" #include "code\datums\ai\basic_mobs\pet_commands\pet_use_targeted_ability.dm" -#include "code\datums\ai\basic_mobs\pet_commands\play_dead.dm" #include "code\datums\ai\basic_mobs\target_priority_strategies\_target_priority_strategy.dm" #include "code\datums\ai\basic_mobs\target_priority_strategies\mining_strategies.dm" +#include "code\datums\ai\basic_mobs\target_sources\_target_source.dm" +#include "code\datums\ai\basic_mobs\target_sources\held_items_then_oview.dm" +#include "code\datums\ai\basic_mobs\target_sources\held_items_typed.dm" +#include "code\datums\ai\basic_mobs\target_sources\mobs_in_oview.dm" +#include "code\datums\ai\basic_mobs\target_sources\near_village_humans.dm" +#include "code\datums\ai\basic_mobs\target_sources\oview_single_type.dm" +#include "code\datums\ai\basic_mobs\target_sources\oview_typed_from_bb_key.dm" +#include "code\datums\ai\basic_mobs\target_sources\range_turfs_typecache_visible.dm" +#include "code\datums\ai\basic_mobs\target_sources\slime_source.dm" +#include "code\datums\ai\basic_mobs\target_sources\turfs_in_oview.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\_targeting_strategy.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\accessible_cable.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\ally_mob.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\baby_raptor.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\basic_targeting_strategy.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\beamable_hydro.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\befriendable_cultist.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\cat_food.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\chargeable_apc.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\conscious_human.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\conscious_mob.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\conscious_snail.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\damaged_eyes.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\damaged_machine.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\dead_mob.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\decorated_donut.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\dont_target_friends.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\drillable_ice.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\empty_paper.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\finished_stove.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\food_or_drink.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\goliath_diggable_turf.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\goose_edible.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\huntable.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\huntable_mouse.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\injured_mob.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\injured_raptor.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\legged_conscious_human.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\living_not_dead.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\non_stump_tree.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\pickup_item.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\playable_deer.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\playable_synthesizer.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\pollinatable_hydro.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\raptor_trough.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\slime_food.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\sniffable_hydro.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\stealable_item.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\stocked_beehive.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\treatable_hydro.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\trough_with_ore.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\unbroken_light.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\uncarried_egg.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\unlit_bonfire.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\valid_cat_home.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\valid_kitten.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\walkable_turf.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\water_dispenser.dm" #include "code\datums\ai\basic_mobs\targeting_strategies\with_object.dm" +#include "code\datums\ai\basic_mobs\targeting_strategies\working_machine.dm" +#include "code\datums\ai\bots\bot_decorators.dm" +#include "code\datums\ai\bots\bot_subtrees.dm" #include "code\datums\ai\cursed\cursed_behaviors.dm" #include "code\datums\ai\cursed\cursed_controller.dm" -#include "code\datums\ai\cursed\cursed_subtrees.dm" -#include "code\datums\ai\dog\dog_behaviors.dm" +#include "code\datums\ai\dog\dog_bt.dm" #include "code\datums\ai\dog\dog_controller.dm" -#include "code\datums\ai\dog\dog_subtrees.dm" -#include "code\datums\ai\generic\find_and_set.dm" -#include "code\datums\ai\generic\generic_behaviors.dm" -#include "code\datums\ai\generic\generic_subtrees.dm" +#include "code\datums\ai\generic_behaviors\acquire_injured_target.dm" +#include "code\datums\ai\generic_behaviors\acquire_target.dm" +#include "code\datums\ai\generic_behaviors\agile_melee_movement.dm" +#include "code\datums\ai\generic_behaviors\ai_interact.dm" +#include "code\datums\ai\generic_behaviors\attack_obstacles.dm" +#include "code\datums\ai\generic_behaviors\battle_screech.dm" +#include "code\datums\ai\generic_behaviors\break_out_of_object.dm" +#include "code\datums\ai\generic_behaviors\break_spine.dm" +#include "code\datums\ai\generic_behaviors\cancel_current_plan.dm" +#include "code\datums\ai\generic_behaviors\clear_key.dm" +#include "code\datums\ai\generic_behaviors\consume.dm" +#include "code\datums\ai\generic_behaviors\copy_bb_key.dm" +#include "code\datums\ai\generic_behaviors\drag_target.dm" +#include "code\datums\ai\generic_behaviors\drop_all_held_items.dm" +#include "code\datums\ai\generic_behaviors\express_happiness.dm" +#include "code\datums\ai\generic_behaviors\face_target_or_face_initial.dm" +#include "code\datums\ai\generic_behaviors\fail.dm" +#include "code\datums\ai\generic_behaviors\find_furthest_turf_from_target.dm" +#include "code\datums\ai\generic_behaviors\find_nearby.dm" +#include "code\datums\ai\generic_behaviors\find_target_facing_turf.dm" +#include "code\datums\ai\generic_behaviors\find_unwebbed_turf.dm" +#include "code\datums\ai\generic_behaviors\find_valid_teleport_location.dm" +#include "code\datums\ai\generic_behaviors\give.dm" +#include "code\datums\ai\generic_behaviors\grab_target.dm" +#include "code\datums\ai\generic_behaviors\heal_eye_damage.dm" +#include "code\datums\ai\generic_behaviors\hunt_target.dm" +#include "code\datums\ai\generic_behaviors\issue_pet_command.dm" +#include "code\datums\ai\generic_behaviors\keep_playing_instrument.dm" +#include "code\datums\ai\generic_behaviors\maintain_distance.dm" +#include "code\datums\ai\generic_behaviors\mine_walls.dm" +#include "code\datums\ai\generic_behaviors\move_to_target.dm" +#include "code\datums\ai\generic_behaviors\perform_emote.dm" +#include "code\datums\ai\generic_behaviors\pick_random_ability.dm" +#include "code\datums\ai\generic_behaviors\pick_up.dm" +#include "code\datums\ai\generic_behaviors\play_instrument.dm" +#include "code\datums\ai\generic_behaviors\random_walk.dm" +#include "code\datums\ai\generic_behaviors\resist.dm" +#include "code\datums\ai\generic_behaviors\run_emote.dm" +#include "code\datums\ai\generic_behaviors\set_bb_cooldown.dm" +#include "code\datums\ai\generic_behaviors\set_bb_key.dm" +#include "code\datums\ai\generic_behaviors\setup_instrument.dm" +#include "code\datums\ai\generic_behaviors\shove_target.dm" +#include "code\datums\ai\generic_behaviors\speech.dm" +#include "code\datums\ai\generic_behaviors\spin_web.dm" +#include "code\datums\ai\generic_behaviors\stop_dragging.dm" +#include "code\datums\ai\generic_behaviors\stuff_in_disposal.dm" +#include "code\datums\ai\generic_behaviors\succeed.dm" +#include "code\datums\ai\generic_behaviors\target_retaliate.dm" +#include "code\datums\ai\generic_behaviors\unlock_do_once.dm" +#include "code\datums\ai\generic_behaviors\use_in_hand.dm" +#include "code\datums\ai\generic_behaviors\use_on_object.dm" +#include "code\datums\ai\generic_behaviors\virtual_pick_up_item.dm" +#include "code\datums\ai\generic_behaviors\wait.dm" +#include "code\datums\ai\generic_decorators\ability_available.dm" +#include "code\datums\ai\generic_decorators\bb_key_at_least.dm" +#include "code\datums\ai\generic_decorators\bb_key_equals.dm" +#include "code\datums\ai\generic_decorators\bb_key_list_min_count.dm" +#include "code\datums\ai\generic_decorators\bb_key_set.dm" +#include "code\datums\ai\generic_decorators\bb_key_true.dm" +#include "code\datums\ai\generic_decorators\buckle_target_dangerous.dm" +#include "code\datums\ai\generic_decorators\can_see_target.dm" +#include "code\datums\ai\generic_decorators\check_cooldown.dm" +#include "code\datums\ai\generic_decorators\check_rider_stat.dm" +#include "code\datums\ai\generic_decorators\container_attackable.dm" +#include "code\datums\ai\generic_decorators\do_once.dm" +#include "code\datums\ai\generic_decorators\is_at_distance.dm" +#include "code\datums\ai\generic_decorators\is_dragging.dm" +#include "code\datums\ai\generic_decorators\is_grabbing_target.dm" +#include "code\datums\ai\generic_decorators\is_holding_target.dm" +#include "code\datums\ai\generic_decorators\is_in_vent.dm" +#include "code\datums\ai\generic_decorators\is_target_stunned.dm" +#include "code\datums\ai\generic_decorators\item_inside_pawn.dm" +#include "code\datums\ai\generic_decorators\key_in_typelist.dm" +#include "code\datums\ai\generic_decorators\keys_different_gender.dm" +#include "code\datums\ai\generic_decorators\mob_stat_at_least.dm" +#include "code\datums\ai\generic_decorators\no_humans_watching.dm" +#include "code\datums\ai\generic_decorators\optional.dm" +#include "code\datums\ai\generic_decorators\pawn_buckled_to_obj.dm" +#include "code\datums\ai\generic_decorators\pawn_contained_in_obj.dm" +#include "code\datums\ai\generic_decorators\pawn_farther_than_from_key.dm" +#include "code\datums\ai\generic_decorators\pawn_grabbed_by_enemy.dm" +#include "code\datums\ai\generic_decorators\pawn_has_gravity.dm" +#include "code\datums\ai\generic_decorators\pawn_has_trait_from.dm" +#include "code\datums\ai\generic_decorators\pawn_health_below.dm" +#include "code\datums\ai\generic_decorators\pawn_inside_mob.dm" +#include "code\datums\ai\generic_decorators\pawn_is_restrained.dm" +#include "code\datums\ai\generic_decorators\pawn_loc_is_type.dm" +#include "code\datums\ai\generic_decorators\pawn_nutrition_below.dm" +#include "code\datums\ai\generic_decorators\pawn_same_z_as_key.dm" +#include "code\datums\ai\generic_decorators\pawn_turf_has_trait.dm" +#include "code\datums\ai\generic_decorators\random_chance.dm" +#include "code\datums\ai\generic_decorators\random_chance_from_key.dm" +#include "code\datums\ai\generic_decorators\target_can_be_shove_stunned.dm" +#include "code\datums\ai\generic_decorators\target_has_reagent.dm" +#include "code\datums\ai\generic_decorators\target_has_trait.dm" +#include "code\datums\ai\generic_decorators\target_health_below_fraction.dm" +#include "code\datums\ai\generic_decorators\target_holding_lit_item.dm" +#include "code\datums\ai\generic_decorators\target_is_holding_item.dm" +#include "code\datums\ai\generic_decorators\target_is_type.dm" +#include "code\datums\ai\generic_decorators\target_legcuffed.dm" +#include "code\datums\ai\generic_decorators\target_on_ground.dm" +#include "code\datums\ai\generic_decorators\true_for_time.dm" +#include "code\datums\ai\generic_subtrees\basic_find_target.dm" +#include "code\datums\ai\generic_subtrees\capricious_pick_target.dm" +#include "code\datums\ai\generic_subtrees\climb_tree.dm" +#include "code\datums\ai\generic_subtrees\find_food.dm" +#include "code\datums\ai\generic_subtrees\find_partner.dm" +#include "code\datums\ai\generic_subtrees\find_stealable_object.dm" +#include "code\datums\ai\generic_subtrees\forage_and_retaliate.dm" +#include "code\datums\ai\generic_subtrees\make_babies.dm" +#include "code\datums\ai\generic_subtrees\move_to_and_eat.dm" +#include "code\datums\ai\generic_subtrees\move_to_and_hunt.dm" +#include "code\datums\ai\generic_subtrees\move_to_reinforce.dm" +#include "code\datums\ai\generic_subtrees\pick_retaliate_target.dm" +#include "code\datums\ai\generic_subtrees\random_walk.dm" +#include "code\datums\ai\generic_subtrees\skittish_and_speak.dm" +#include "code\datums\ai\generic_subtrees\steal_and_flee.dm" +#include "code\datums\ai\hauntium\haunted_bt_nodes.dm" #include "code\datums\ai\hauntium\haunted_controller.dm" -#include "code\datums\ai\hauntium\hauntium_subtrees.dm" -#include "code\datums\ai\hunting_behavior\hunting_behaviors.dm" -#include "code\datums\ai\hunting_behavior\hunting_cockroach.dm" -#include "code\datums\ai\hunting_behavior\hunting_corpses.dm" -#include "code\datums\ai\hunting_behavior\hunting_lights.dm" -#include "code\datums\ai\hunting_behavior\hunting_mouse.dm" -#include "code\datums\ai\idle_behaviors\_idle_behavior.dm" -#include "code\datums\ai\idle_behaviors\idle_dog.dm" -#include "code\datums\ai\idle_behaviors\idle_haunted.dm" -#include "code\datums\ai\idle_behaviors\idle_monkey.dm" -#include "code\datums\ai\idle_behaviors\idle_random_walk.dm" -#include "code\datums\ai\monkey\monkey_behaviors.dm" +#include "code\datums\ai\monkey\monkey_bt_nodes.dm" #include "code\datums\ai\monkey\monkey_controller.dm" -#include "code\datums\ai\monkey\monkey_subtrees.dm" #include "code\datums\ai\movement\_ai_movement.dm" #include "code\datums\ai\movement\ai_movement_basic_avoidance.dm" #include "code\datums\ai\movement\ai_movement_complete_stop.dm" @@ -1160,11 +1283,11 @@ #include "code\datums\ai\objects\vending_machines\vending_machine_controller.dm" #include "code\datums\ai\robot_customer\robot_customer_behaviors.dm" #include "code\datums\ai\robot_customer\robot_customer_controller.dm" -#include "code\datums\ai\robot_customer\robot_customer_subtrees.dm" #include "code\datums\ai_laws\ai_laws.dm" #include "code\datums\ai_laws\laws_antagonistic.dm" #include "code\datums\ai_laws\laws_neutral.dm" #include "code\datums\ai_laws\laws_station_sided.dm" +#include "code\datums\ai_laws\state_laws_ui.dm" #include "code\datums\announcers\_announcer.dm" #include "code\datums\announcers\default_announcer.dm" #include "code\datums\announcers\intern_announcer.dm" @@ -1639,6 +1762,7 @@ #include "code\datums\elements\beauty.dm" #include "code\datums\elements\bed_tucking.dm" #include "code\datums\elements\befriend_petting.dm" +#include "code\datums\elements\blessed_turf.dm" #include "code\datums\elements\block_area_power_fail.dm" #include "code\datums\elements\block_turf_fingerprints.dm" #include "code\datums\elements\blocks_explosives.dm" @@ -1964,6 +2088,7 @@ #include "code\datums\mutations\active.dm" #include "code\datums\mutations\adaptation.dm" #include "code\datums\mutations\antenna.dm" +#include "code\datums\mutations\aoe_moodlet.dm" #include "code\datums\mutations\autotomy.dm" #include "code\datums\mutations\body.dm" #include "code\datums\mutations\chameleon.dm" @@ -2427,6 +2552,7 @@ #include "code\game\machinery\wishgranter.dm" #include "code\game\machinery\ai_core\_core.dm" #include "code\game\machinery\ai_core\core_construction.dm" +#include "code\game\machinery\ai_core\law.dm" #include "code\game\machinery\big_manipulator\_defines.dm" #include "code\game\machinery\big_manipulator\big_manipulator.dm" #include "code\game\machinery\big_manipulator\big_manipulator_interactions.dm" @@ -2455,7 +2581,6 @@ #include "code\game\machinery\computer\crew.dm" #include "code\game\machinery\computer\dna_console.dm" #include "code\game\machinery\computer\launchpad_control.dm" -#include "code\game\machinery\computer\law.dm" #include "code\game\machinery\computer\mechlaunchpad.dm" #include "code\game\machinery\computer\modular_shield.dm" #include "code\game\machinery\computer\operating_computer.dm" @@ -2564,7 +2689,6 @@ #include "code\game\objects\obj_defense.dm" #include "code\game\objects\objs.dm" #include "code\game\objects\structures.dm" -#include "code\game\objects\effects\blessing.dm" #include "code\game\objects\effects\bump_teleporter.dm" #include "code\game\objects\effects\countdown.dm" #include "code\game\objects\effects\cursor_catcher.dm" @@ -2771,11 +2895,11 @@ #include "code\game\objects\items\wiki_manuals.dm" #include "code\game\objects\items\wind_turbine.dm" #include "code\game\objects\items\AI_modules\_AI_modules.dm" +#include "code\game\objects\items\AI_modules\ai_upgrades.dm" #include "code\game\objects\items\AI_modules\freeform.dm" #include "code\game\objects\items\AI_modules\full_lawsets.dm" #include "code\game\objects\items\AI_modules\hacked.dm" #include "code\game\objects\items\AI_modules\ion.dm" -#include "code\game\objects\items\AI_modules\repair.dm" #include "code\game\objects\items\AI_modules\supplied.dm" #include "code\game\objects\items\AI_modules\zeroth.dm" #include "code\game\objects\items\circuitboards\circuitboard.dm" @@ -2914,7 +3038,6 @@ #include "code\game\objects\items\rcd\RSF.dm" #include "code\game\objects\items\rcd\RTD.dm" #include "code\game\objects\items\rcd\RWD.dm" -#include "code\game\objects\items\robot\ai_upgrades.dm" #include "code\game\objects\items\robot\robot_parts.dm" #include "code\game\objects\items\robot\robot_upgrades.dm" #include "code\game\objects\items\robot\items\food.dm" @@ -2935,6 +3058,7 @@ #include "code\game\objects\items\stacks\tape.dm" #include "code\game\objects\items\stacks\telecrystal.dm" #include "code\game\objects\items\stacks\tickets.dm" +#include "code\game\objects\items\stacks\wall_filling.dm" #include "code\game\objects\items\stacks\wrap.dm" #include "code\game\objects\items\stacks\golem_food\golem_food_buff.dm" #include "code\game\objects\items\stacks\golem_food\golem_hand_actions.dm" @@ -3112,6 +3236,7 @@ #include "code\game\objects\structures\traps.dm" #include "code\game\objects\structures\tripwire.dm" #include "code\game\objects\structures\votingbox.dm" +#include "code\game\objects\structures\wall_support.dm" #include "code\game\objects\structures\windoor_assembly.dm" #include "code\game\objects\structures\window.dm" #include "code\game\objects\structures\beds_chairs\alien_nest.dm" @@ -3198,6 +3323,7 @@ #include "code\game\turfs\closed\indestructible.dm" #include "code\game\turfs\closed\minerals.dm" #include "code\game\turfs\closed\walls.dm" +#include "code\game\turfs\closed\wall\concrete_walls.dm" #include "code\game\turfs\closed\wall\material_walls.dm" #include "code\game\turfs\closed\wall\mineral_walls.dm" #include "code\game\turfs\closed\wall\misc_walls.dm" @@ -3222,6 +3348,7 @@ #include "code\game\turfs\open\snow.dm" #include "code\game\turfs\open\water.dm" #include "code\game\turfs\open\floor\catwalk_plating.dm" +#include "code\game\turfs\open\floor\concrete_floor.dm" #include "code\game\turfs\open\floor\fancy_floor.dm" #include "code\game\turfs\open\floor\glass.dm" #include "code\game\turfs\open\floor\hull.dm" @@ -3552,7 +3679,6 @@ #include "code\modules\antagonists\heretic\cosmic_effect.dm" #include "code\modules\antagonists\heretic\heretic_antag.dm" #include "code\modules\antagonists\heretic\heretic_curses.dm" -#include "code\modules\antagonists\heretic\heretic_focus.dm" #include "code\modules\antagonists\heretic\heretic_knowledge.dm" #include "code\modules\antagonists\heretic\heretic_living_heart.dm" #include "code\modules\antagonists\heretic\heretic_monsters.dm" @@ -3569,6 +3695,7 @@ #include "code\modules\antagonists\heretic\items\heretic_armor.dm" #include "code\modules\antagonists\heretic\items\heretic_blades.dm" #include "code\modules\antagonists\heretic\items\heretic_grenade.dm" +#include "code\modules\antagonists\heretic\items\heretic_lantern.dm" #include "code\modules\antagonists\heretic\items\heretic_necks.dm" #include "code\modules\antagonists\heretic\items\heretic_shoes.dm" #include "code\modules\antagonists\heretic\items\hunter_rifle.dm" @@ -3576,6 +3703,7 @@ #include "code\modules\antagonists\heretic\items\labyrinth_handbook.dm" #include "code\modules\antagonists\heretic\items\madness_mask.dm" #include "code\modules\antagonists\heretic\items\unfathomable_curio.dm" +#include "code\modules\antagonists\heretic\items\voidballd.dm" #include "code\modules\antagonists\heretic\knowledge\_heretic_paths.dm" #include "code\modules\antagonists\heretic\knowledge\ash_lore.dm" #include "code\modules\antagonists\heretic\knowledge\blade_lore.dm" @@ -3636,9 +3764,7 @@ #include "code\modules\antagonists\heretic\magic\star_blast.dm" #include "code\modules\antagonists\heretic\magic\star_touch.dm" #include "code\modules\antagonists\heretic\magic\void_cold_cone.dm" -#include "code\modules\antagonists\heretic\magic\void_conduit.dm" #include "code\modules\antagonists\heretic\magic\void_phase.dm" -#include "code\modules\antagonists\heretic\magic\void_prison.dm" #include "code\modules\antagonists\heretic\magic\void_pull.dm" #include "code\modules\antagonists\heretic\magic\wave_of_desperation.dm" #include "code\modules\antagonists\heretic\magic\wolves_among_sheep.dm" @@ -3652,6 +3778,7 @@ #include "code\modules\antagonists\heretic\structures\carving_knife.dm" #include "code\modules\antagonists\heretic\structures\lock_final.dm" #include "code\modules\antagonists\heretic\structures\mawed_crucible.dm" +#include "code\modules\antagonists\heretic\structures\void_conduit.dm" #include "code\modules\antagonists\highlander\highlander.dm" #include "code\modules\antagonists\hypnotized\hypnotized.dm" #include "code\modules\antagonists\magic_servant\servant.dm" @@ -4219,6 +4346,7 @@ #include "code\modules\client\preferences\silicon_gender.dm" #include "code\modules\client\preferences\skin_tone.dm" #include "code\modules\client\preferences\sounds.dm" +#include "code\modules\client\preferences\spacer_born.dm" #include "code\modules\client\preferences\species.dm" #include "code\modules\client\preferences\statpanel.dm" #include "code\modules\client\preferences\status_bar.dm" @@ -4450,19 +4578,9 @@ #include "code\modules\emote_panel\emote_panel.dm" #include "code\modules\error_handler\error_handler.dm" #include "code\modules\error_handler\error_viewer.dm" -#include "code\modules\escape_menu\admin_page.dm" -#include "code\modules\escape_menu\details.dm" #include "code\modules\escape_menu\dimmer.dm" #include "code\modules\escape_menu\escape_menu.dm" -#include "code\modules\escape_menu\home_page.dm" -#include "code\modules\escape_menu\leave_body.dm" -#include "code\modules\escape_menu\quit_game.dm" #include "code\modules\escape_menu\subsystem.dm" -#include "code\modules\escape_menu\title.dm" -#include "code\modules\escape_menu\screen_objects\admin_text.dm" -#include "code\modules\escape_menu\screen_objects\leave_body_text.dm" -#include "code\modules\escape_menu\screen_objects\lobby_buttons.dm" -#include "code\modules\escape_menu\screen_objects\text.dm" #include "code\modules\events\_event.dm" #include "code\modules\events\_event_admin_setup.dm" #include "code\modules\events\aurora_caelus.dm" @@ -5143,6 +5261,7 @@ #include "code\modules\mapfluff\ruins\spaceruin_code\spacehotel.dm" #include "code\modules\mapfluff\ruins\spaceruin_code\the_Outlet.dm" #include "code\modules\mapfluff\ruins\spaceruin_code\TheDerelict.dm" +#include "code\modules\mapfluff\ruins\spaceruin_code\tramsyndicateoutpost.dm" #include "code\modules\mapfluff\ruins\spaceruin_code\waystation.dm" #include "code\modules\mapfluff\ruins\spaceruin_code\whiteship.dm" #include "code\modules\mapfluff\ruins\spaceruin_code\whiteshipruin_box.dm" @@ -5399,6 +5518,7 @@ #include "code\modules\mob\living\basic\farm_animals\bee\_bee.dm" #include "code\modules\mob\living\basic\farm_animals\bee\bee_ai_behavior.dm" #include "code\modules\mob\living\basic\farm_animals\bee\bee_ai_subtree.dm" +#include "code\modules\mob\living\basic\farm_animals\bee\bee_bt.dm" #include "code\modules\mob\living\basic\farm_animals\chicken\chick.dm" #include "code\modules\mob\living\basic\farm_animals\chicken\chicken.dm" #include "code\modules\mob\living\basic\farm_animals\cow\_cow.dm" @@ -5503,10 +5623,10 @@ #include "code\modules\mob\living\basic\lavaland\mook\mook.dm" #include "code\modules\mob\living\basic\lavaland\mook\mook_abilities.dm" #include "code\modules\mob\living\basic\lavaland\mook\mook_ai.dm" +#include "code\modules\mob\living\basic\lavaland\mook\mook_bt.dm" #include "code\modules\mob\living\basic\lavaland\mook\mook_village.dm" #include "code\modules\mob\living\basic\lavaland\node_drone\node_drone.dm" #include "code\modules\mob\living\basic\lavaland\raptor\_raptor.dm" -#include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_behavior.dm" #include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_controller.dm" #include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_subtrees.dm" #include "code\modules\mob\living\basic\lavaland\raptor\raptor_color.dm" @@ -5531,12 +5651,11 @@ #include "code\modules\mob\living\basic\pets\pet.dm" #include "code\modules\mob\living\basic\pets\pet_designer.dm" #include "code\modules\mob\living\basic\pets\sloth.dm" -#include "code\modules\mob\living\basic\pets\cat\bread_cat_ai.dm" #include "code\modules\mob\living\basic\pets\cat\cat.dm" #include "code\modules\mob\living\basic\pets\cat\cat_ai.dm" +#include "code\modules\mob\living\basic\pets\cat\cat_bt.dm" #include "code\modules\mob\living\basic\pets\cat\feral.dm" #include "code\modules\mob\living\basic\pets\cat\keeki.dm" -#include "code\modules\mob\living\basic\pets\cat\kitten_ai.dm" #include "code\modules\mob\living\basic\pets\cat\runtime.dm" #include "code\modules\mob\living\basic\pets\dog\_dog.dm" #include "code\modules\mob\living\basic\pets\dog\corgi.dm" @@ -5560,6 +5679,7 @@ #include "code\modules\mob\living\basic\pets\penguin\penguin_ai.dm" #include "code\modules\mob\living\basic\pets\pet_cult\pet_cult_abilities.dm" #include "code\modules\mob\living\basic\pets\pet_cult\pet_cult_ai.dm" +#include "code\modules\mob\living\basic\pets\pet_cult\pet_cult_bt.dm" #include "code\modules\mob\living\basic\ruin_defender\blob_of_flesh.dm" #include "code\modules\mob\living\basic\ruin_defender\cybersun_aicore.dm" #include "code\modules\mob\living\basic\ruin_defender\dark_wizard.dm" @@ -5585,7 +5705,7 @@ #include "code\modules\mob\living\basic\slime\ai\behaviours.dm" #include "code\modules\mob\living\basic\slime\ai\controller.dm" #include "code\modules\mob\living\basic\slime\ai\pet_command.dm" -#include "code\modules\mob\living\basic\slime\ai\subtrees.dm" +#include "code\modules\mob\living\basic\slime\ai\slime_bt.dm" #include "code\modules\mob\living\basic\snails\snail.dm" #include "code\modules\mob\living\basic\snails\snail_ability.dm" #include "code\modules\mob\living\basic\snails\snail_ai.dm" @@ -5620,7 +5740,6 @@ #include "code\modules\mob\living\basic\space_fauna\demon\demon_subtypes.dm" #include "code\modules\mob\living\basic\space_fauna\eyeball\_eyeball.dm" #include "code\modules\mob\living\basic\space_fauna\eyeball\eyeball_ability.dm" -#include "code\modules\mob\living\basic\space_fauna\eyeball\eyeball_ai_behavior.dm" #include "code\modules\mob\living\basic\space_fauna\eyeball\eyeball_ai_subtree.dm" #include "code\modules\mob\living\basic\space_fauna\hivebot\_hivebot.dm" #include "code\modules\mob\living\basic\space_fauna\hivebot\hivebot_behavior.dm" @@ -5646,13 +5765,11 @@ #include "code\modules\mob\living\basic\space_fauna\revenant\revenant_objectives.dm" #include "code\modules\mob\living\basic\space_fauna\snake\banded_snake.dm" #include "code\modules\mob\living\basic\space_fauna\snake\snake.dm" -#include "code\modules\mob\living\basic\space_fauna\snake\snake_ai.dm" #include "code\modules\mob\living\basic\space_fauna\space_dragon\dragon_breath.dm" #include "code\modules\mob\living\basic\space_fauna\space_dragon\dragon_gust.dm" #include "code\modules\mob\living\basic\space_fauna\space_dragon\space_dragon.dm" #include "code\modules\mob\living\basic\space_fauna\spider\spider.dm" #include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider_ai.dm" -#include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider_subtrees.dm" #include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spiders.dm" #include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\hivemind.dm" #include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\lay_eggs.dm" @@ -7130,8 +7247,10 @@ #include "modular_darkpack\master_files\code\controllers\configuration\entries\game_options.dm" #include "modular_darkpack\master_files\code\controllers\configuration\entries\general.dm" #include "modular_darkpack\master_files\code\controllers\subsystem\ambience.dm" +#include "modular_darkpack\master_files\code\datums\datum.dm" #include "modular_darkpack\master_files\code\datums\actions\action.dm" #include "modular_darkpack\master_files\code\datums\components\dejavu.dm" +#include "modular_darkpack\master_files\code\datums\components\crafting\_recipes.dm" #include "modular_darkpack\master_files\code\datums\mapgen\woods_generator.dm" #include "modular_darkpack\master_files\code\datums\mind\_mind.dm" #include "modular_darkpack\master_files\code\datums\quirks\_quirk.dm" @@ -7164,12 +7283,14 @@ #include "modular_darkpack\master_files\code\game\turfs\open\water.dm" #include "modular_darkpack\master_files\code\game\turfs\open\floor\fancy_floor.dm" #include "modular_darkpack\master_files\code\game\turfs\open\space\space.dm" +#include "modular_darkpack\master_files\code\modules\asset_cache\assets\tgui.dm" #include "modular_darkpack\master_files\code\modules\cargo\expressconsole.dm" #include "modular_darkpack\master_files\code\modules\client\preferences_savefile.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\_admin.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\_preference.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\auto_dementor.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\country_of_origin.dm" +#include "modular_darkpack\master_files\code\modules\client\preferences\middleware\quirks.dm" #include "modular_darkpack\master_files\code\modules\economy\account.dm" #include "modular_darkpack\master_files\code\modules\fishing\sources\subtypes\structures.dm" #include "modular_darkpack\master_files\code\modules\fishing\sources\subtypes\turfs.dm" @@ -7274,6 +7395,7 @@ #include "modular_darkpack\modules\blooper\code\changeling.dm" #include "modular_darkpack\modules\blooper\code\preferences\preferences.dm" #include "modular_darkpack\modules\blooper\code\preferences\middleware\blooper.dm" +#include "modular_darkpack\modules\book_sources\code\source_book_datum.dm" #include "modular_darkpack\modules\books\code\bookcases.dm" #include "modular_darkpack\modules\books\code\books.dm" #include "modular_darkpack\modules\brain_damage\code\phobia.dm" @@ -7478,6 +7600,7 @@ #include "modular_darkpack\modules\jobs\code\garou\warder.dm" #include "modular_darkpack\modules\jobs\code\garou\wyrmfoe.dm" #include "modular_darkpack\modules\jobs\code\giovanni\capo.dm" +#include "modular_darkpack\modules\jobs\code\giovanni\i_nonni.dm" #include "modular_darkpack\modules\jobs\code\giovanni\la_famiglia.dm" #include "modular_darkpack\modules\jobs\code\giovanni\la_squadra.dm" #include "modular_darkpack\modules\jobs\code\hunters\abbe.dm" @@ -7609,6 +7732,7 @@ #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\permanent_wound.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\pierced_veil.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\prey_exclusion.dm" +#include "modular_darkpack\modules\merits_flaws\code\negative_quirks\seizures.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\short.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\smell_of_the_grave.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\speech_impediment.dm" @@ -7616,6 +7740,7 @@ #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\thaumaturgically_inept.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\the_largest_maw.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\third_eye.dm" +#include "modular_darkpack\modules\merits_flaws\code\negative_quirks\uncontrollable.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\vengeful.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\victim_of_the_masquerade.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\weak_willed.dm" @@ -8081,12 +8206,12 @@ #include "modular_darkpack\modules\werewolf_the_apocalypse\code\silver_damage.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\totems.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\werewolf_globals.dm" -#include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\bird.dm" -#include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\bird_ai.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\wolf.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\banes\_banes.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\banes\religion.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\banes\suffocating.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\corvid\_bird.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\basic_mobs\corvid\bird_ai.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\changing_breeds\corax.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\_gift.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\ahroun.dm" diff --git a/tgui/bun.lock b/tgui/bun.lock index 62c3de9babef..95a9a2a421f5 100644 --- a/tgui/bun.lock +++ b/tgui/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "tgui-workspace", @@ -67,6 +66,17 @@ "stacktrace-parser": "^0.1.11", }, }, + "packages/tgui-escape-menu": { + "name": "tgui-escape-menu", + "version": "1.0.0", + "dependencies": { + "common": "workspace:*", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "tgui": "workspace:*", + "tgui-core": "^6.1.1", + }, + }, "packages/tgui-panel": { "name": "tgui-panel", "version": "6.0.0", @@ -864,7 +874,7 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], @@ -1082,6 +1092,8 @@ "tgui-dev-server": ["tgui-dev-server@workspace:packages/tgui-dev-server"], + "tgui-escape-menu": ["tgui-escape-menu@workspace:packages/tgui-escape-menu"], + "tgui-panel": ["tgui-panel@workspace:packages/tgui-panel"], "tgui-say": ["tgui-say@workspace:packages/tgui-say"], @@ -1208,12 +1220,12 @@ "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "@parcel/watcher/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], - "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], @@ -1248,6 +1260,8 @@ "make-fetch-happen/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -1264,6 +1278,8 @@ "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "sass/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -1284,8 +1300,6 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], - "webpack-bundle-analyzer/ws": ["ws@7.5.11", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA=="], "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], diff --git a/tgui/happydom.ts b/tgui/happydom.ts index e3799b447408..a5ca6988a329 100644 --- a/tgui/happydom.ts +++ b/tgui/happydom.ts @@ -1,5 +1,10 @@ +import { afterEach } from 'bun:test'; import { GlobalRegistrator } from '@happy-dom/global-registrator'; GlobalRegistrator.register(); -import 'tgui/__mocks__/setup.ts'; +const { cleanup } = await import('@testing-library/react'); + +afterEach(cleanup); + +import './packages/tgui/__mocks__/setup'; diff --git a/tgui/packages/tgui-dev-server/link/client.ts b/tgui/packages/tgui-dev-server/link/client.ts index eb8d542a6875..be283a676018 100644 --- a/tgui/packages/tgui-dev-server/link/client.ts +++ b/tgui/packages/tgui-dev-server/link/client.ts @@ -106,7 +106,7 @@ function serializeObject(obj: Record): string { // Error object const isError = value instanceof Error || - (value.code && value.message && value.message.includes('Error')); + (value.code && value.message?.includes('Error')); if (isError) { return { __error__: true, diff --git a/tgui/packages/tgui-escape-menu/EscapeMenu.tsx b/tgui/packages/tgui-escape-menu/EscapeMenu.tsx new file mode 100644 index 000000000000..b1d563e9ea0b --- /dev/null +++ b/tgui/packages/tgui-escape-menu/EscapeMenu.tsx @@ -0,0 +1,224 @@ +import './styles/main.scss'; + +import { useEffect, useReducer, useRef } from 'react'; + +import { playCloseSounds, playOpenSounds } from './audio'; +import { AdminPage } from './pages/AdminPage'; +import { HomePage } from './pages/HomePage'; +import { LeaveBodyPage } from './pages/LeaveBodyPage'; +import { PlayersPage } from './pages/PlayersPage'; +import { QuitPage } from './pages/QuitPage'; +import { updateScaling } from './scaling'; + +type Page = 'home' | 'admin' | 'players' | 'leave_body' | 'quit'; + +export type PlayerInfo = { + ckey: string; + displayName: string; + rank?: string; + feedbackLink?: string; + ping?: number; + ignored: boolean; + isSelf?: boolean; +}; + +export type ServerState = { + stationName: string; + roundId: string; + mapName: string; + mapFeedbackLink: string | null; + serverTime: string; + shiftTime: string; + timeDilation: string; + canLeaveBody: boolean; + canAdminHelp: boolean; + canSeeNotes: boolean; + hasTicketNotification: boolean; + resources: ResourceLink[]; + admins: PlayerInfo[]; + players: PlayerInfo[]; + ignoredOffline: string[]; + suicideIcon: string | null; +}; + +export type ResourceLink = { + id: string; + label: string; + tooltip: string; +}; + +type State = { + page: Page; + isOpen: boolean; + showResources: boolean; + serverState: ServerState | null; +}; + +type Action = + | { type: 'open' } + | { type: 'close' } + | { type: 'navigate'; page: Page } + | { type: 'toggleResources' } + | { type: 'serverUpdate'; state: Partial }; + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'open': + return { ...state, isOpen: true }; + case 'close': + return { ...state, isOpen: false, page: 'home', showResources: false }; + case 'navigate': + return { ...state, page: action.page, showResources: false }; + case 'toggleResources': + return { ...state, showResources: !state.showResources }; + case 'serverUpdate': + return { + ...state, + serverState: { ...state.serverState!, ...action.state }, + }; + } +} + +const initialState: State = { + page: 'home', + isOpen: false, + showResources: false, + serverState: null, +}; + +function sendAction(action: string, payload?: Record) { + Byond.sendMessage('action', { action, ...payload }); +} + +let resizeFrozen = false; +export function isResizeFrozen() { + return resizeFrozen; +} + +function openMenu(dispatch: React.Dispatch) { + setTimeout(() => { + resizeFrozen = false; + updateScaling(); + }, 100); + playOpenSounds(); + sendAction('opened'); + dispatch({ type: 'open' }); +} + +function closeMenu(dispatch: React.Dispatch) { + resizeFrozen = true; + Byond.winset('mapwindow.escape_menu', { 'is-visible': false }); + Byond.winset('map', { focus: true }); + playCloseSounds(); + sendAction('closed'); + dispatch({ type: 'close' }); +} + +export function EscapeMenu() { + const [state, dispatch] = useReducer(reducer, initialState); + const isOpenRef = useRef(false); + isOpenRef.current = state.isOpen; + + useEffect(() => { + Byond.subscribeTo('init', (data: ServerState) => { + dispatch({ type: 'serverUpdate', state: data }); + }); + + Byond.subscribeTo('state', (data: Partial) => { + dispatch({ type: 'serverUpdate', state: data }); + }); + + Byond.subscribeTo('toggle', () => { + if (isOpenRef.current) { + closeMenu(dispatch); + } else { + openMenu(dispatch); + } + }); + }, []); + + if (!state.serverState) { + return null; + } + + const navigate = (page: Page) => dispatch({ type: 'navigate', page }); + + const handleAction = (action: string, payload?: Record) => { + sendAction(action, payload); + }; + + const handleClose = () => closeMenu(dispatch); + + const refocusMap = () => { + Byond.winset('map', { focus: true }); + }; + + return ( +
    +
    +
    +
    + {state.page === 'home' && ( + dispatch({ type: 'toggleResources' })} + /> + )} + {state.page === 'admin' && ( + + )} + {state.page === 'players' && ( + + )} + {state.page === 'leave_body' && ( + + )} + {state.page === 'quit' && ( + + )} +
    +
    + ); +} + +function Details({ serverState }: { serverState: ServerState }) { + return ( +
    +
    Round ID: {serverState.roundId || 'Unset'}
    +
    Server Time: {serverState.serverTime}
    +
    Round Time: {serverState.shiftTime}
    {/* DARKPACK EDIT CHANGE */} +
    + Map:{' '} + {serverState.mapFeedbackLink ? ( + Byond.command(`.url ${serverState.mapFeedbackLink}`)} + > + {serverState.mapName || 'Loading...'} + + ) : ( + serverState.mapName || 'Loading...' + )} +
    +
    Time Dilation: {serverState.timeDilation}%
    +
    + ); +} diff --git a/tgui/packages/tgui-escape-menu/assets.ts b/tgui/packages/tgui-escape-menu/assets.ts new file mode 100644 index 000000000000..4adb8d68a9d8 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/assets.ts @@ -0,0 +1 @@ +export const assetMap: Record = {}; diff --git a/tgui/packages/tgui-escape-menu/audio.ts b/tgui/packages/tgui-escape-menu/audio.ts new file mode 100644 index 000000000000..0e3b409746ff --- /dev/null +++ b/tgui/packages/tgui-escape-menu/audio.ts @@ -0,0 +1,42 @@ +import { assetMap } from './assets'; + +let ambientAudio: HTMLAudioElement | null = null; + +function getAssetUrl(name: string): string | null { + return assetMap[name] ?? null; +} + +function playOneShot(name: string) { + const url = getAssetUrl(name); + if (!url) return; + const audio = new Audio(url); + audio.volume = 0.8; + audio.play().catch(() => {}); +} + +function startAmbient(name: string) { + stopAmbient(); + const url = getAssetUrl(name); + if (!url) return; + ambientAudio = new Audio(url); + ambientAudio.volume = 0.8; + ambientAudio.play().catch(() => {}); +} + +function stopAmbient() { + if (ambientAudio) { + const audio = ambientAudio; + ambientAudio = null; + audio.pause(); + } +} + +export function playOpenSounds() { + playOneShot('esc_open.ogg'); + startAmbient('esc_middle.ogg'); +} + +export function playCloseSounds() { + stopAmbient(); + playOneShot('esc_close.ogg'); +} diff --git a/tgui/packages/tgui-escape-menu/index.tsx b/tgui/packages/tgui-escape-menu/index.tsx new file mode 100644 index 000000000000..e15acf5e86da --- /dev/null +++ b/tgui/packages/tgui-escape-menu/index.tsx @@ -0,0 +1,32 @@ +import './styles/main.scss'; + +import { loadMappings, loadStyleSheet } from 'common/assets'; +import { createRoot, type Root } from 'react-dom/client'; +import { assetMap } from './assets'; +import { EscapeMenu, isResizeFrozen } from './EscapeMenu'; +import { updateScaling } from './scaling'; + +let reactRoot: Root | null = null; + +document.onreadystatechange = () => { + if (document.readyState !== 'complete') return; + + updateScaling(); + + window.addEventListener('resize', () => { + if (isResizeFrozen()) return; + updateScaling(); + }); + + if (!reactRoot) { + const root = document.getElementById('react-root'); + reactRoot = createRoot(root!); + } + + reactRoot.render(); + + Byond.subscribeTo('asset/stylesheet', loadStyleSheet); + Byond.subscribeTo('asset/mappings', (payload: Record) => { + loadMappings(payload, assetMap); + }); +}; diff --git a/tgui/packages/tgui-escape-menu/package.json b/tgui/packages/tgui-escape-menu/package.json new file mode 100644 index 000000000000..bc7c07fca359 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/package.json @@ -0,0 +1,12 @@ +{ + "name": "tgui-escape-menu", + "version": "1.0.0", + "dependencies": { + "common": "workspace:*", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "tgui": "workspace:*", + "tgui-core": "^6.1.1" + }, + "private": true +} diff --git a/tgui/packages/tgui-escape-menu/pages/AdminPage.tsx b/tgui/packages/tgui-escape-menu/pages/AdminPage.tsx new file mode 100644 index 000000000000..e1d28ef6f3ab --- /dev/null +++ b/tgui/packages/tgui-escape-menu/pages/AdminPage.tsx @@ -0,0 +1,120 @@ +import { Tooltip } from 'tgui-core/components'; + +type Props = { + serverState: { + canAdminHelp: boolean; + canSeeNotes: boolean; + hasTicketNotification: boolean; + }; + onNavigate: (page: 'home') => void; + onAction: (action: string) => void; + onClose: () => void; +}; + +export function AdminPage({ + serverState, + onNavigate, + onAction, + onClose, +}: Props) { + return ( + <> + onNavigate('home')} /> +
    + { + onAction('create_ticket'); + onClose(); + }} + disabled={!serverState.canAdminHelp} + > + Create Admin Ticket + + onAction('view_ticket')} + blinking={serverState.hasTicketNotification} + tooltip={ + serverState.hasTicketNotification + ? 'An admin is trying to talk to you!' + : undefined + } + > + View Latest Ticket + + { + onAction('admin_notice'); + onClose(); + }} + > + See Admin Notices + + { + onAction('pray'); + onClose(); + }} + > + Pray + + {!!serverState.canSeeNotes && ( + { + onAction('see_notes'); + onClose(); + }} + > + See Notes + + )} +
    + + ); +} + +function BackButton({ onClick }: { onClick: () => void }) { + return ( + + ); +} + +type MenuButtonProps = { + children: React.ReactNode; + onClick: () => void; + disabled?: boolean; + blinking?: boolean; + tooltip?: string; +}; + +function MenuButton({ + children, + onClick, + disabled, + blinking, + tooltip, +}: MenuButtonProps) { + const button = ( + + ); + + if (tooltip) { + return {button}; + } + + return button; +} diff --git a/tgui/packages/tgui-escape-menu/pages/HomePage.tsx b/tgui/packages/tgui-escape-menu/pages/HomePage.tsx new file mode 100644 index 000000000000..563cb7a63368 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/pages/HomePage.tsx @@ -0,0 +1,181 @@ +import { useEffect, useState } from 'react'; +import { Tooltip } from 'tgui-core/components'; + +import type { ResourceLink } from '../EscapeMenu'; + +type ServerState = { + stationName: string; + canLeaveBody: boolean; + canAdminHelp: boolean; + hasTicketNotification: boolean; + resources: ResourceLink[]; +}; + +type Props = { + serverState: ServerState; + onNavigate: (page: 'admin' | 'players' | 'leave_body' | 'quit') => void; + onAction: (action: string) => void; + onClose: () => void; + showResources: boolean; + onToggleResources: () => void; +}; + +const COLLAPSE_DURATION = 400; + +export function HomePage({ + serverState, + onNavigate, + onAction, + onClose, + showResources, + onToggleResources, +}: Props) { + const [mounted, setMounted] = useState(false); + const [collapsing, setCollapsing] = useState(false); + + useEffect(() => { + if (showResources) { + setMounted(true); + setCollapsing(false); + } else if (mounted) { + setCollapsing(true); + const timer = setTimeout(() => { + setMounted(false); + setCollapsing(false); + }, COLLAPSE_DURATION); + return () => clearTimeout(timer); + } + }, [showResources]); + + return ( + <> +
    +
    +
    Another day in...
    {/* DARKPACK EDIT CHANGE - `on : in` because this is a city*/} +
    + {serverState.stationName} +
    +
    +
    + Resume + { + onAction('character'); + onClose(); + }} + > + Character + + { + onAction('settings'); + onClose(); + }} + > + Settings + + onNavigate('players')}>Players + onNavigate('admin')} + blinking={serverState.hasTicketNotification} + tooltip={ + serverState.hasTicketNotification + ? 'An admin is trying to talk to you!' + : undefined + } + > + Admin Help + + onNavigate('leave_body')} + disabled={!serverState.canLeaveBody} + > + Leave Body + + onNavigate('quit')}>Quit +
    +
    +
    + {mounted && ( +
    + {serverState.resources.map((resource) => ( + + + + ))} +
    + )} + +
    + + ); +} + +function IconButton({ iconClass }: { iconClass: string }) { + return ( +
    + + +
    + ); +} + +type MenuButtonProps = { + children: React.ReactNode; + onClick: () => void; + disabled?: boolean; + blinking?: boolean; + tooltip?: string; +}; + +function MenuButton({ + children, + onClick, + disabled, + blinking, + tooltip, +}: MenuButtonProps) { + const button = ( + + ); + + if (tooltip) { + return {button}; + } + + return button; +} diff --git a/tgui/packages/tgui-escape-menu/pages/LeaveBodyPage.tsx b/tgui/packages/tgui-escape-menu/pages/LeaveBodyPage.tsx new file mode 100644 index 000000000000..07eea97d1405 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/pages/LeaveBodyPage.tsx @@ -0,0 +1,67 @@ +import type { ServerState } from '../EscapeMenu'; + +type Props = { + serverState: ServerState; + onNavigate: (page: 'home') => void; + onAction: (action: string) => void; + onClose: () => void; +}; + +export function LeaveBodyPage({ + serverState, + onNavigate, + onAction, + onClose, +}: Props) { + return ( + <> + onNavigate('home')} /> +
    + + +
    + + ); +} + +function BackButton({ onClick }: { onClick: () => void }) { + return ( + + ); +} diff --git a/tgui/packages/tgui-escape-menu/pages/PlayersPage.tsx b/tgui/packages/tgui-escape-menu/pages/PlayersPage.tsx new file mode 100644 index 000000000000..3b092b700f5d --- /dev/null +++ b/tgui/packages/tgui-escape-menu/pages/PlayersPage.tsx @@ -0,0 +1,131 @@ +import type { PlayerInfo, ServerState } from '../EscapeMenu'; + +type Props = { + serverState: ServerState; + onNavigate: (page: 'home') => void; + onAction: (action: string, payload?: Record) => void; +}; + +export function PlayersPage({ serverState, onNavigate, onAction }: Props) { + return ( + <> + onNavigate('home')} /> +
    + {serverState.admins.length > 0 ? ( + + {serverState.admins.map((admin) => ( + + onAction('toggle_ignore', { ckey: admin.ckey }) + } + /> + ))} + + ) : ( +
    + No Admins Online! +
    + )} + + {serverState.players.map((player) => ( + + onAction('toggle_ignore', { ckey: player.ckey }) + } + /> + ))} + + {serverState.ignoredOffline.length > 0 && ( + + {serverState.ignoredOffline.map((ckey) => ( +
    + onAction('toggle_ignore', { ckey })} + > + {ckey} + +
    + ))} +
    + )} +
    + + ); +} + +function BackButton({ onClick }: { onClick: () => void }) { + return ( + + ); +} + +function PlayerSection({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) { + return ( +
    +
    {title}
    +
    {children}
    +
    + ); +} + +function PlayerEntry({ + player, + onToggleIgnore, +}: { + player: PlayerInfo; + onToggleIgnore: () => void; +}) { + return ( +
    + + {player.displayName} + {player.ping !== undefined && ( + ({player.ping}ms) + )} + + {player.rank && ( + Byond.command(`.url ${player.feedbackLink}`) + : undefined + } + > + {player.rank} + + )} +
    + ); +} diff --git a/tgui/packages/tgui-escape-menu/pages/QuitPage.tsx b/tgui/packages/tgui-escape-menu/pages/QuitPage.tsx new file mode 100644 index 000000000000..c5ae9c0ca3e8 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/pages/QuitPage.tsx @@ -0,0 +1,28 @@ +type Props = { + onNavigate: (page: 'home') => void; + onAction: (action: string) => void; +}; + +export function QuitPage({ onNavigate, onAction }: Props) { + return ( +
    +
    + Are you sure you wish to quit? +
    +
    + + +
    +
    + ); +} diff --git a/tgui/packages/tgui-escape-menu/scaling.ts b/tgui/packages/tgui-escape-menu/scaling.ts new file mode 100644 index 000000000000..fa6131dcd701 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/scaling.ts @@ -0,0 +1,11 @@ +const ICON_SCALE_BASE_WIDTH = 800; +const REM_BASE_DIVISOR = 100; + +export function updateScaling() { + const width = window.innerWidth; + document.documentElement.style.fontSize = `${width / REM_BASE_DIVISOR}px`; + document.documentElement.style.setProperty( + '--icon-scale', + `${width / ICON_SCALE_BASE_WIDTH}`, + ); +} diff --git a/tgui/packages/tgui-escape-menu/styles/fonts.css b/tgui/packages/tgui-escape-menu/styles/fonts.css new file mode 100644 index 000000000000..bb4eddfd2ade --- /dev/null +++ b/tgui/packages/tgui-escape-menu/styles/fonts.css @@ -0,0 +1,9 @@ +@font-face { + font-family: "Pixellari"; + src: url("Pixellari.ttf") format("truetype"); +} + +@font-face { + font-family: "Grand9K Pixel"; + src: url("Grand9K_Pixel.ttf") format("truetype"); +} diff --git a/tgui/packages/tgui-escape-menu/styles/main.scss b/tgui/packages/tgui-escape-menu/styles/main.scss new file mode 100644 index 000000000000..88c92b2e7846 --- /dev/null +++ b/tgui/packages/tgui-escape-menu/styles/main.scss @@ -0,0 +1,409 @@ +@use 'sass:meta'; + +@include meta.load-css('~tgui/styles/reset'); +@include meta.load-css('~tgui-core/styles/base.scss'); +@include meta.load-css('~tgui-core/styles/colors.scss'); +@include meta.load-css('~tgui-core/styles/vars-components.scss'); +@include meta.load-css('~tgui-core/styles/components/Tooltip.scss'); + +html, +body, +#react-root { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + background: transparent !important; + background-color: transparent !important; + cursor: default !important; +} + +* { + cursor: default !important; + user-select: none; +} + +.Tooltip { + font-family: 'Pixellari', monospace; + transform: translateY(-1.5rem); +} + +.escape-menu { + --details-width: 25rem; + --content-margin: 4rem; + --details-gap: 3rem; + + position: relative; + width: 100%; + height: 100%; + font-family: 'Pixellari', 'Grand9K Pixel', monospace; + color: white; + -webkit-text-stroke: 0.2rem black; + paint-order: stroke fill; + -webkit-font-smoothing: none; + -moz-osx-font-smoothing: unset; + text-rendering: optimizeSpeed; + + &__overlay { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.65); + z-index: 0; + } + + &__content { + position: relative; + z-index: 1; + width: 100%; + height: 100%; + } + + &__home-column { + position: absolute; + top: var(--content-margin); + left: var(--content-margin); + max-width: calc( + 100% - var(--content-margin) - var(--details-width) - var(--details-gap) + ); + display: flex; + flex-direction: column; + } + + &__title { + margin-bottom: 2rem; + } + + &__subtitle { + font-size: 1.8rem; + color: #ccc; + margin-bottom: 0.4rem; + } + + &__station-name { + font-size: 5rem; + font-weight: bold; + -webkit-text-stroke: 0.3rem black; + } + + &__details { + position: absolute; + top: 2rem; + right: 2rem; + width: var(--details-width); + text-align: right; + font-size: 1.8rem; + line-height: 1.5; + color: #ddd; + z-index: 2; + } + + &__details-link { + color: #5599ff; + + &:hover { + text-decoration: underline; + -webkit-text-stroke: 0; + } + } + + &__buttons { + display: flex; + flex-direction: column; + gap: 0.3rem; + + &--page { + position: absolute; + top: 12rem; + left: var(--content-margin); + } + } + + &__menu-button { + background: none; + border: none; + color: white; + font-family: 'Pixellari', monospace; + font-size: 4.5rem; + font-weight: bold; + text-align: left; + padding: 0.2rem 0.5rem; + cursor: pointer; + -webkit-text-stroke: 0.3rem black; + + &:hover { + text-decoration: underline; + -webkit-text-stroke: 0; + } + + &--disabled { + color: #666; + cursor: default; + + &:hover { + text-decoration: none; + } + } + + &--blinking { + animation: blink-red 0.8s infinite; + } + } + + &__back-button { + position: absolute; + top: 3rem; + left: 2rem; + background: none; + border: none; + color: white; + font-family: 'Grand9K Pixel', monospace; + font-size: 1.4rem; + cursor: pointer; + padding: 0.8rem 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.3rem; + + .escape-menu__icon-button { + transform: scale(var(--icon-scale, 1.5)); + } + + &:hover { + filter: brightness(1.1); + } + } + + &__leave-body { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + gap: 6rem; + } + + &__large-button { + background: none; + border: none; + color: white; + font-family: 'Pixellari', monospace; + padding: 0; + cursor: pointer; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + + &:hover { + filter: brightness(1.1); + } + } + + &__large-button-label { + font-size: 3rem; + font-weight: bold; + } + + &__quit { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + } + + &__quit-prompt { + font-size: 4.5rem; + margin-bottom: 3rem; + } + + &__quit-buttons { + display: flex; + gap: 4rem; + justify-content: center; + } + + &__resources { + position: absolute; + bottom: 2rem; + right: 2rem; + } + + &__resource-toggle, + &__resource-button { + background: none; + border: none; + color: white; + padding: 0; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + width: calc(40px * var(--icon-scale, 1.5) + 2rem); + + &:hover { + filter: brightness(1.1); + } + } + + &__resource-button { + animation: resource-slide-in 0.4s cubic-bezier(0.33, 1, 0.68, 1) both; + + @for $i from 1 through 8 { + &:nth-child(#{$i}) { + animation-delay: #{$i * 0.04}s; + } + } + } + + &__resource-label, + &__resource-button-label { + font-family: 'Grand9K Pixel', monospace; + font-size: 1.2rem; + text-align: center; + } + + &__resource-list { + position: absolute; + top: 0; + right: calc(40px * var(--icon-scale, 1.5) + 3rem); + display: flex; + flex-direction: row-reverse; + align-items: flex-start; + + &--collapsing .escape-menu__resource-button { + animation: resource-slide-out 0.4s cubic-bezier(0.33, 1, 0.68, 1) both; + } + } + + &__icon-button { + position: relative; + display: inline-block; + transform: scale(var(--icon-scale, 1.5)); + transform-origin: center bottom; + image-rendering: pixelated; + } + + &__icon-overlay { + position: absolute; + top: 0; + left: 0; + } + + &__suicide-icon-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + image-rendering: pixelated; + } + + &__player-list { + position: absolute; + top: 12rem; + left: var(--content-margin); + right: calc(var(--details-width) + var(--details-gap)); + bottom: var(--content-margin); + overflow-y: auto; + scrollbar-color: rgba(255, 255, 255, 0.3) transparent; + } + + &__player-section { + margin-bottom: 2rem; + } + + &__player-section-title { + font-size: 3rem; + font-weight: bold; + margin-bottom: 1rem; + padding-bottom: 0.4rem; + border-bottom: 2px solid rgba(255, 255, 255, 0.15); + } + + &__player-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); + gap: 0.4rem 0.8rem; + } + + &__player-entry { + color: white; + font-family: 'Pixellari', monospace; + padding: 0.4rem 0.8rem; + display: flex; + flex-direction: column; + + &--ignored { + color: #666; + } + } + + &__player-name { + font-size: 2rem; + overflow-wrap: break-word; + + &:hover { + text-decoration: underline; + -webkit-text-stroke: 0; + } + } + + &__player-ping { + font-size: 1.4rem; + color: #777; + font-family: 'Grand9K Pixel', monospace; + } + + &__player-rank { + font-size: 1.4rem; + color: #aaa; + font-family: 'Grand9K Pixel', monospace; + + &--link { + color: #5599ff; + text-decoration: none; + + &:hover { + text-decoration: underline; + -webkit-text-stroke: 0; + } + } + } +} + +@keyframes blink-red { + 0%, + 100% { + color: white; + } + 50% { + color: red; + } +} + +@keyframes resource-slide-in { + from { + opacity: 0; + transform: translateX(6rem); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes resource-slide-out { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(6rem); + } +} diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index 4acc617c4fd6..fe7e799077bb 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -976,6 +976,11 @@ em { display: inline-block; } +.mansus { + color: #0d9b00; + text-shadow: 0 0 6px #0d9b00; +} + .cyan { color: #bde0dc; text-shadow: 0 0 6px #bde0dc; diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index 4098d9a0ffc1..89dcc717f5ee 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -990,6 +990,11 @@ h2.alert { display: inline-block; } +.mansus { + color: #0b8000; + text-shadow: 0 0 6px #0b8000; +} + .cyan { color: #95afac; text-shadow: 0 0 6px #95afac; diff --git a/tgui/packages/tgui/events/handlers/update.ts b/tgui/packages/tgui/events/handlers/update.ts index 8bde870fe6eb..54a45bb27217 100644 --- a/tgui/packages/tgui/events/handlers/update.ts +++ b/tgui/packages/tgui/events/handlers/update.ts @@ -48,7 +48,6 @@ function resume(payload: UpdatePayload): void { Byond.winset(Byond.windowId, { 'is-visible': true, }); - Byond.sendMessage('visible'); perf.mark('resume/finish'); if (process.env.NODE_ENV !== 'production') { diff --git a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx index 6a9cf50972fb..8b895f1b0875 100644 --- a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx +++ b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx @@ -2,6 +2,7 @@ import '../styles/interfaces/AntagInfoHeretic.scss'; import { useState } from 'react'; import { + BlockQuote, Box, Button, DmIcon, @@ -64,8 +65,9 @@ type Knowledge = { depth: number; done: BooleanLike; ascension: BooleanLike; - disabled: BooleanLike; - tooltip?: string; + disabled?: BooleanLike; + notice?: string; + info?: string; }; enum ShopCategory { @@ -81,6 +83,7 @@ type KnowledgeTier = { type HereticPassive = { name: string; + recharge: string; description: string[]; }; @@ -117,7 +120,7 @@ const IntroductionSection = (props) => { return ( -
    +
    @@ -223,30 +226,12 @@ const GuideSection = () => { Living Heart. - - Make yourself a focus to be able - to cast various advanced spells to assist you in acquiring harder and - harder sacrifices. - - - - Accomplish all of your objectives to be able to learn the{' '} - final ritual. Complete the ritual - to become all powerful! - - - WARNING! -
    Accumulating a total of {points_to_aura}  + - Accumulating a total of {points_to_aura}  knowledge points -  to manifest a visible aura of  - Mansus energy around you. Simply - gaining the points is sufficent, spending them will not trigger it. -
    - This aura will be visible to all those around you and will mark you as - a heretic. Consider the risks before accumulating too much knowledge! -
    - Keep in mind that using a  - Codex Cicatrix will also make you - very obvious as a heretic when draining  - influences +  will manifest a visible aura of  + Mansus energy around you. This aura + will be visible to all those around you and will mark you as a + Heretic. Consider the risks before accumulating too much knowledge!
    @@ -328,6 +313,38 @@ const KnowledgeTree = () => { ); }; +// take • in from byond and make sure it's rendered properly +function bulletpointHelper(text: string) { + return text.replace(/•/g, '•'); +} + +// description or info text may have
    s, +// we need translate it into separate children for the tooltip to render it properly +function formatTooltipText(text: string) { + return ( + + {text.split('
    ').map((line, index) => { + const isBulletPoint = line.includes('•'); + if (isBulletPoint) { + line = line.replace(/•/g, '•'); + } + return ( + + {line} + + ); + })} +
    + ); +} + type KnowledgeNodeProps = { node: Knowledge; purchaseCategory?: ShopCategory; @@ -359,9 +376,30 @@ const KnowledgeNode = (props: KnowledgeNodeProps) => { + + + + + + {selectedDec && ( + + Observer: {selectedDec.label} — highlighted nodes would + be cancelled. Click node again to deselect. + {(selectedDec.observed_keys?.length ?? 0) > 0 && ( + + Watching: {selectedDec.observed_keys!.join(', ')} + + )} + + )} +
    +
    +
    + +
    + + + {roots && roots.length > 0 && ( +
    + {roots.map((rootIdx) => ( + + ))} +
    + )} +
    +
    + {(!roots || roots.length === 0) && ( + + {mob_name + ? 'No behavior nodes in controller.' + : 'Pick a mob to view its behavior tree.'} + + )} +
    +
    + + + ); +} diff --git a/tgui/packages/tgui/interfaces/BehaviorTreeViewer/types.ts b/tgui/packages/tgui/interfaces/BehaviorTreeViewer/types.ts new file mode 100644 index 000000000000..8fcbee70fdae --- /dev/null +++ b/tgui/packages/tgui/interfaces/BehaviorTreeViewer/types.ts @@ -0,0 +1,41 @@ +export type BlackboardEntry = { + key: string; // BB_* constant name + value: string; // stringified value +}; + +export type BehaviorTreeViewerData = { + mob_name: string | null; + controller_type: string | null; + active_execution_index: number; + fired_indices: number[]; // all leaf execution indices that fired since last poll + awaiting_pick: boolean; + roots: number[]; // execution indices of root nodes + nodes: BtNodeData[]; // flat list of all nodes in the tree + blackboard: BlackboardEntry[]; +}; + +// All dem nodes +export const BT_NODE_SELECTOR = 0; +export const BT_NODE_SEQUENCE = 1; +export const BT_NODE_PARALLEL = 2; +export const BT_NODE_DECORATOR = 3; +export const BT_NODE_LEAF = 4; +export const BT_NODE_SUBTREE = 5; +export const BT_NODE_SUBPLAN = 6; + +export type BtNodeData = { + exec_index: number; // unique key for this node instance + label: string; // display label + node_type: number; // BT_NODE_* constant + priority: number; // sibling priority (1-based) + last_exec_index?: number; // last execution index in subtree — omitted when same as exec_index + children?: number[]; // child exec_indices — omitted when no children + observer_abort?: 0 | 1 | 2 | 3; // abort scope — only on observing decorators + observed_keys?: string[]; // watched blackboard keys — only when non-empty + invert?: boolean; // condition is inverted — only when true +}; + +export const BT_ABORT_NONE = 0; +export const BT_ABORT_SELF = 1; +export const BT_ABORT_LOWER_PRIORITY = 2; +export const BT_ABORT_BOTH = 3; diff --git a/tgui/packages/tgui/interfaces/BigManipulator/index.tsx b/tgui/packages/tgui/interfaces/BigManipulator/index.tsx index e56d1dd431f8..6595f2afc2f1 100644 --- a/tgui/packages/tgui/interfaces/BigManipulator/index.tsx +++ b/tgui/packages/tgui/interfaces/BigManipulator/index.tsx @@ -23,14 +23,20 @@ const TASK_TYPE_LABELS: Record = { throw: 'Throw...', use: 'Use held...', interact: 'Interact...', + move: 'Move...', wait: 'Wait...', + stop: 'Stop...', }; const TASK_TYPE_ICONS: Record = { pickup: 'hand', drop: 'box-open', + throw: 'paper-plane', + use: 'mouse-pointer', interact: 'bolt', + move: 'arrows-alt', wait: 'hourglass-half', + stop: 'stop-circle', }; const TASKING_STRATEGY_ICONS: Record = { @@ -150,15 +156,6 @@ const getPointButtonNumber = (offset: string): number | null => { return yIndex * 3 + xIndex + 1; }; -const getFilteringModeText = (mode: number) => { - switch (mode) { - case 1: return 'Items'; - case 2: return 'Closets'; - case 3: return 'Humans'; - default: return 'Unknown'; - } -}; - type TaskEditModalProps = { task: ManipulatorTask; onClose: () => void; @@ -175,6 +172,7 @@ function TaskEditModal(props: TaskEditModalProps) { const isPickup = task.task_type.includes('pickup'); const isDropoff = task.task_type === 'drop' || task.task_type === 'throw' || task.task_type === 'use'; const isInteract = task.task_type.includes('interact'); + const isMove = task.task_type === 'move'; const currentButton = task.turf ? getPointButtonNumber(task.turf) @@ -208,7 +206,7 @@ function TaskEditModal(props: TaskEditModalProps) { )} - {isCargo && ( + {isCargo && ( - {(task.task_type === 'pickup' || - task.task_type === 'drop' || - task.task_type === 'throw') && ( + {!isMove && ( adjust('cycle_filtering_mode')} - tooltip="Cycle object category" + label="Use Filters" + content={task.filters_status ? 'TRUE' : 'FALSE'} + onClick={() => adjust('toggle_filter_skip')} + tooltip="Toggle filter usage" /> )} - adjust('toggle_filter_skip')} - tooltip="Toggle filter usage" - /> {isPickup && ( - adjust('cycle_worker_interaction')} - tooltip="Normal / Single use / Empty hand" - /> adjust('toggle_skip_anchored')} tooltip="Skip anchored objects when looking for interaction targets" /> - adjust('cycle_post_interaction')} - tooltip="What to do when nothing left to interact with" - /> )}
    @@ -320,7 +298,7 @@ function TaskEditModal(props: TaskEditModalProps) { )} - {isCargo && ( + {isCargo && !isMove && ( <>
    { '...'} )} - {task.turf && ...at [{task.turf}]...} + {task.turf && ...{task.task_type === 'move' ? 'to' : 'at'} [{task.turf}]...} {task.time && ...for {task.time} second{task.time > 1 && "s"}...} + {task.sub_name && ...{task.sub_name}} diff --git a/tgui/packages/tgui/interfaces/BigManipulator/types.ts b/tgui/packages/tgui/interfaces/BigManipulator/types.ts index 91283d7a8891..3e7b4ab49ab0 100644 --- a/tgui/packages/tgui/interfaces/BigManipulator/types.ts +++ b/tgui/packages/tgui/interfaces/BigManipulator/types.ts @@ -11,6 +11,7 @@ export type TaskType = | 'Throw...' | 'Use...' | 'Interact with...' + | 'Move to...' | 'Wait for...'; export interface ManipulatorTask { @@ -21,7 +22,6 @@ export interface ManipulatorTask { turf?: string; item_filters?: string[]; filters_status?: BooleanLike; - filtering_mode?: number; settings_list?: PrioritySettings[]; // pickup only pickup_eagerness?: string; @@ -29,14 +29,14 @@ export interface ManipulatorTask { interaction_mode?: string; overflow_status?: string; throw_range?: number; - worker_interaction?: string; - use_post_interaction?: string; worker_use_rmb?: BooleanLike; worker_combat_mode?: BooleanLike; skip_anchored?: BooleanLike; // interact only - // (worker_interaction, use_post_interaction, worker_use_rmb, worker_combat_mode, skip_anchored shared with dropoff) + // (worker_use_rmb, worker_combat_mode, skip_anchored shared with dropoff) time?: number; + // stop only + sub_name?: string; } export interface ManipulatorData { diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx b/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx index d1c4b5fdce69..a5399b2c990d 100644 --- a/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx +++ b/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx @@ -14,12 +14,13 @@ type Data = { connected: BooleanLike; notice: string; unlocked: BooleanLike; + visualizing: BooleanLike; target: string; }; export const BluespaceArtillery = (props) => { const { act, data } = useBackend(); - const { notice, connected, unlocked, target } = data; + const { connected, notice, unlocked, visualizing, target } = data; return ( @@ -72,11 +73,20 @@ export const BluespaceArtillery = (props) => {
    - + + + ) : ( + + )}
    diff --git a/tgui/packages/tgui/interfaces/Cargo/CargoCart.tsx b/tgui/packages/tgui/interfaces/Cargo/CargoCart.tsx index 13777f2e8e63..5cd4c7f9c781 100644 --- a/tgui/packages/tgui/interfaces/Cargo/CargoCart.tsx +++ b/tgui/packages/tgui/interfaces/Cargo/CargoCart.tsx @@ -85,7 +85,7 @@ function CheckoutItems(props) { {entry.object} - {!!can_send && !!entry.can_be_cancelled ? ( + {can_send && entry.can_be_cancelled ? ( <> act('reset', { reset: 'freq', }) } - /> + > + Reset + } > { buttons={ } > { - const { act, data } = useBackend(); - const { mechs = [] } = data; + const { act, data } = useBackend(); + const { mechs } = data; return ( @@ -26,17 +42,17 @@ export const ExosuitControlConsole = (props) => { <> } > @@ -61,40 +79,44 @@ export const ExosuitControlConsole = (props) => { - - {(typeof mech.charge === 'number' && `${mech.charge}%`) || - 'Not Found'} - + {mech.charge !== null ? ( + + {mech.charge}% + + ) : ( + Not Found + )} - {(typeof mech.airtank === 'number' && ( + {mech.airtank !== null ? ( `${toFixed(value, 2)} kPa`} /> - )) || - 'Not Equipped'} + ) : ( + 'Not Equipped' + )} - {(mech.pilot.length > 0 && - mech.pilot.map((pilot) => ( - - {pilot} - {mech.pilot.length > 1 ? '|' : ''} - - ))) || - 'None'} + {mech.pilot.length > 0 + ? mech.pilot.map((pilot) => ( + + {pilot} + {mech.pilot.length > 1 ? '|' : ''} + + )) + : 'None'} {mech.location || 'Unknown'} - {mech.cargo_space >= 0 && ( + {mech.cargo_space !== undefined && ( { icon="cog" fontSize={1.2} textAlign="center" - disabled={!design || design.disableReason !== ''} + disabled={design?.disableReason !== ''} tooltip={design.disableReason} tooltipPosition="bottom" onClick={() => onPrint()} diff --git a/tgui/packages/tgui/interfaces/GhostMenu.tsx b/tgui/packages/tgui/interfaces/GhostMenu.tsx index 365fc6a12211..9e56ac28a988 100644 --- a/tgui/packages/tgui/interfaces/GhostMenu.tsx +++ b/tgui/packages/tgui/interfaces/GhostMenu.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { Button, Dropdown, @@ -19,6 +18,7 @@ type Data = { lag_switch_on: BooleanLike; notification_data: NotificationData[]; max_extra_view: number; + current_extra_view: number; body_name: string; current_darkness: string; darkness_levels: string[]; @@ -166,10 +166,14 @@ const HudSection = (props) => { }; const GhostSettingsSection = (props) => { - const [viewNumber, setviewNumber] = useState(0); const { act, data } = useBackend(); - const { current_darkness, darkness_levels, max_extra_view, lag_switch_on } = - data; + const { + current_darkness, + darkness_levels, + max_extra_view, + current_extra_view, + lag_switch_on, + } = data; return ( @@ -198,15 +202,14 @@ const GhostSettingsSection = (props) => { { - setviewNumber(new_range); + onChange={(new_range) => act('view_range', { new_view_range: new_range, - }); - }} + }) + } /> )} diff --git a/tgui/packages/tgui/interfaces/IDCBook.tsx b/tgui/packages/tgui/interfaces/IDCBook.tsx index 44a17acb4f61..38a6fce21ed1 100644 --- a/tgui/packages/tgui/interfaces/IDCBook.tsx +++ b/tgui/packages/tgui/interfaces/IDCBook.tsx @@ -11,6 +11,7 @@ type Disease = { spread_by: string; // used by diseases - how the disease is spread, like "Airborne" or "Contact" cured_by: string | null; // used by symptoms and diseases - how the disease or symptom is cured, like a medicine name or "Unknown" illness: string; // used by symptoms - the common illness associated with the symptom, like "Flu" for "Fever" + level: string; // used by symptoms - determines how to obtain the symptom id: string; }; @@ -99,13 +100,15 @@ function estimateHeight(entry: BookEntry) { const desc_height = Math.ceil(entry.desc.length / 50) * 14; const illness_height = entry.illness !== 'Unidentified' ? 10 : 0; const cured_by_height = 10; + const level_height = 10; return ( title_height + desc_height + illness_height + cured_by_height + - extra_spacing + extra_spacing + + level_height ); } @@ -149,6 +152,12 @@ function renderDSMEntry(entry: BookEntry) { {' '} {entry.desc} + + + Level: + {' '} + {entry.level} + {entry.illness !== 'Unidentified' && ( @@ -238,6 +247,7 @@ export const IDCBook = () => { spread_by: disease.spread_by, cured_by: disease.cured_by, illness: disease.illness, + level: disease.level, })) .sort((a, b) => a.form > b.form ? 1 : a.form < b.form ? -1 : a.name > b.name ? 1 : -1, diff --git a/tgui/packages/tgui/interfaces/LawRack.tsx b/tgui/packages/tgui/interfaces/LawRack.tsx new file mode 100644 index 000000000000..b2d792b562a9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/LawRack.tsx @@ -0,0 +1,628 @@ +import { capitalize } from 'es-toolkit'; +import { useState } from 'react'; +import { + Box, + Button, + DmIcon, + Flex, + Floating, + Section, + Stack, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { useBackend } from '../backend'; +import { Window } from '../layouts'; + +enum SlotSecurity { + NONE = 0, + SCREWED = 1, + WELDED = 2, +} + +type SlotData = { + name: string; + security: SlotSecurity; + ioned: BooleanLike; + empty: BooleanLike; +}; + +type Linkable = { + name: string; + ref: string; +}; + +type Data = { + // All slots, including empty ones. Index 0 is the core slot if it exists. + slots: SlotData[]; + // If the ui viewer is holding a module + holding_module: BooleanLike; + // If the ui viewer is holding a screwdriver + holding_screwdriver: BooleanLike; + // If the ui viewer is holding a welder + holding_welder: BooleanLike; + // If the ui viewer is holding a multitool + holding_multitool: BooleanLike; + // If this rack has a core slot + has_core_slot: BooleanLike; + // If the rack has no power + depowered: BooleanLike; + // If we are a broadcaster: This represents our parent rack + parent_rack?: Linkable; + // If we are a base: This is all silicons we can link to + linkable_silicons?: Linkable[]; + // If we are a broadcaster: This is all racks we can link to + linkable_racks?: Linkable[]; + // If we are a base: This is all silicons currently linked to us + linked_mobs?: string[]; + // Number of modules in all linked racks + linked_rack_module_count?: number; + // If refresh is on cooldown + refresh_cooldown: number; +}; + +function getSlotColor( + slot: SlotData, + index: number, + has_core_slot: BooleanLike, +): string { + const is_core_slot = index === 0 && has_core_slot; + if (slot.empty) return is_core_slot ? '#5AA6FF' : '#83BFB1'; + if (slot.ioned) return '#DD7973'; + return is_core_slot ? '#4183CD' : '#14BB69'; +} + +const LawSlotScrew = (props: { + noscrew: BooleanLike; + disabled: BooleanLike; + tooltip?: string; + handleScrew: () => void; +}) => { + return ( + + )} + {slot.security !== SlotSecurity.WELDED && !!holding_multitool && ( + + )} + {slot.security !== SlotSecurity.WELDED && + !holding_multitool && + !holding_welder && ( + + )} + + + act('screw_module', { slot: index + 1 })} + /> + + + + ); +}; + +const LinkableFloating = (props: { + show_floating: boolean; + linkable: Linkable[]; + link_act: (ref: string, name: string) => void; + hide_floating: () => void; + button: React.ReactNode; +}) => { + const { show_floating, linkable, link_act, button, hide_floating } = props; + + if (!show_floating) { + return button; + } + + return ( + + {linkable.map((item) => ( + + + + ))} + + } + > + {button} + + ); +}; + +const UnlinkableFloating = (props: { + show_floating: boolean; + unlinkable: string[]; + unlink_act: (index: number, name: string) => void; + hide_floating: () => void; + button: React.ReactNode; + can_unlink: BooleanLike; + unlink_tooltip?: string; +}) => { + const { + button, + can_unlink, + hide_floating, + show_floating, + unlink_act, + unlink_tooltip, + unlinkable, + } = props; + + if (!show_floating) { + return button; + } + + return ( + + {unlinkable.map((name, list_index) => ( + + + + ))} + + } + > + {button} + + ); +}; + +const AiFlavorDisplay = (props: { show_face: string }) => { + return ( + + + {!!props.show_face && ( + + )} + + ); +}; + +const BroadcasterDisplay = () => { + const { data, act } = useBackend(); + const { depowered, parent_rack, linkable_racks } = data; + + const [rackLink, setRackLink] = useState(true); + return ( + + + + + > Linked Rack: + + + {parent_rack ? capitalize(parent_rack.name) : 'N/A'} + + + + + + + + + + + act('link_rack', { + rack_ref: ref, + rack_name: name, + }) + } + hide_floating={() => setRackLink(false)} + button={ + + } + /> + + + + + ); +}; + +const BaseDisplay = () => { + const { data, act } = useBackend(); + const { depowered, linkable_silicons, linked_mobs, parent_rack } = data; + + const [siliconLink, setSiliconLink] = useState(true); + const [siliconUnlink, setSiliconUnlink] = useState(true); + + return ( + + + + + > Linked Silicons: + + + {linked_mobs?.length ? linked_mobs.join(', ') : 'N/A'} + + + + + + + act('unlink_silicon', { + silicon_index: index, + silicon_name: name, + }) + } + hide_floating={() => setSiliconUnlink(false)} + button={ + + } + /> + + + act('link_silicon', { + silicon_ref: ref, + silicon_name: name, + }) + } + hide_floating={() => setSiliconLink(false)} + button={ + + } + /> + + + + + ); +}; +export const LawRack = () => { + const { data, act } = useBackend(); + const { + depowered, + linkable_racks, + linked_mobs, + parent_rack, + slots, + refresh_cooldown, + has_core_slot, + linked_rack_module_count, + } = data; + + const screen_display = () => { + if (depowered) { + return 'off'; + } + if (parent_rack || linked_mobs?.length) { + return Math.round(Math.random() * 100) === 1 ? 'ai_hal' : 'ai_sal'; + } + return 'ai_bsod'; + }; + + return ( + + +
    + + {slots.map((slot, index) => ( + + + + ))} + {linked_rack_module_count !== undefined && ( + + {linked_rack_module_count} other module(s) are being + loaded remotely. + + )} + +
    +
    0} + tooltipPosition="left" + tooltip={ + depowered + ? 'No power!' + : refresh_cooldown > 0 + ? `Cooldown: ${Math.round(refresh_cooldown / 10)}s` + : 'Refreshes module and link data.' + } + onClick={() => act('refresh')} + /> + } + > + + + + {!!has_core_slot && ( + + + + )} + {linkable_racks && ( + + + + )} + {linked_mobs && ( + + + + )} + + + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/Lawpanel.tsx b/tgui/packages/tgui/interfaces/Lawpanel.tsx index b0670ffcaaaf..15266b91e7e2 100644 --- a/tgui/packages/tgui/interfaces/Lawpanel.tsx +++ b/tgui/packages/tgui/interfaces/Lawpanel.tsx @@ -19,31 +19,10 @@ import { Window } from '../layouts'; const lawtype_to_color = { inherent: 'white', supplied: 'purple', - ion: 'green', hacked: 'orange', zeroth: 'red', } as const; -const lawtype_to_tooltip = { - inherent: `Core laws. - Inherent laws are the "core" laws of the AI. - Resetting the AI will not remove these, - these are intrinsic to whatever lawset they are running.`, - supplied: `Supplied laws. - Supplied laws are, well, supplied in addition to the inherent laws. - These laws will go away when an AI is reset.`, - ion: `Ion laws. - Special, (usually) randomized laws which are above all over laws. - These laws will go away when an AI is reset.`, - hacked: `Hacked laws. - Syndicate uploaded laws which are above all other laws. - These laws will go away when an AI is reset.`, - zeroth: `Zeroth law. - A lawset can only have 1 zeroth law, it's the top dog. - Given out by malfunctioning AIs to allow them to do whatever. - Nothing will remove this, allegedly, unless it's admin forced.`, -} as const; - type Law = { lawtype: string; // The actual law text @@ -106,18 +85,6 @@ export const LawPrintout = (props: { cyborg_ref: string; lawset: Law[] }) => { color={lawtype_to_color[law.lawtype] || 'pink'} buttons={ - - {law.lawtype === 'inherent' && ( <> @@ -183,21 +150,6 @@ export const LawPrintout = (props: { cyborg_ref: string; lawset: Law[] }) => {
    )} - {law.lawtype === 'supplied' && ( - -